Quieter chat chrome: no repeated names, no English working line, no tool names (#186)
* Name a run's chat once when its group carries the bot's name A project's main conversation is named after its lead bot, so Today showed "Chief · Chief". Web and mobile now share one title helper that drops the repeated name. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ * Drop the "1:1 with" line under a conversation's title The stage header repeated what the chat already shows, such as "1:1 with Chief" under a project's name. The title and the phase pill remain. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ * Let the stage's translated thinking lines speak while a bot works A running bot raised a "working" awareness event whose English line, such as "Chief is working on your request.", replaced the translated thinking lines and added an "In progress" strip next to the phase pill. The strip now only shows collaboration. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ * Ask bots to describe their steps without naming tools Long runs showed lines such as "I'm using browser_navigate" in the chat. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ * Announce the stage phase as status and keep following after a tap The working strip used to be the only status region while a bot worked; the stage's phase pill now carries that role. A tap at the latest message no longer stops the transcript from following new messages. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
f720683496
commit
83d2106633
@ -6,6 +6,7 @@ import {
|
|||||||
type SpaceBot,
|
type SpaceBot,
|
||||||
type SpaceGroup,
|
type SpaceGroup,
|
||||||
} from "@rakazo/contracts";
|
} from "@rakazo/contracts";
|
||||||
|
import { runChatTitle } from "@rakazo/core";
|
||||||
import { botColors } from "@rakazo/ui-tokens";
|
import { botColors } from "@rakazo/ui-tokens";
|
||||||
import { Redirect, useFocusEffect, useRouter } from "expo-router";
|
import { Redirect, useFocusEffect, useRouter } from "expo-router";
|
||||||
import { type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
@ -828,7 +829,7 @@ function ActivityRow({
|
|||||||
bot?: MobileBot;
|
bot?: MobileBot;
|
||||||
onPress: () => void;
|
onPress: () => void;
|
||||||
}) {
|
}) {
|
||||||
const title = run.groupName ? `${run.botName} · ${run.groupName}` : run.botName;
|
const title = runChatTitle(run);
|
||||||
const status = activityStatusLabel(run.status);
|
const status = activityStatusLabel(run.status);
|
||||||
const preview = run.promptSnippet ? `${run.promptSnippet} · ${status}` : status;
|
const preview = run.promptSnippet ? `${run.promptSnippet} · ${status}` : status;
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -67,7 +67,7 @@ test("main conversation does not show a group subtitle for one Oo", async ({ pag
|
|||||||
await signup(page, `main-1-1-${stamp}@rakazo.test`, "password12", "Solo");
|
await signup(page, `main-1-1-${stamp}@rakazo.test`, "password12", "Solo");
|
||||||
await completeOnboarding(page, undefined, { mainConversation: true });
|
await completeOnboarding(page, undefined, { mainConversation: true });
|
||||||
await expect(page.getByTestId("bot-settings-trigger")).not.toContainText("Gruppenunterhaltung");
|
await expect(page.getByTestId("bot-settings-trigger")).not.toContainText("Gruppenunterhaltung");
|
||||||
await expect(page.getByTestId("oo-group-stage")).toContainText(/1:1/i);
|
await expect(page.getByTestId("oo-group-stage")).not.toContainText("Gruppenunterhaltung");
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe("mobile shell", () => {
|
test.describe("mobile shell", () => {
|
||||||
|
|||||||
@ -171,6 +171,7 @@ export function OoStage({
|
|||||||
{(hasMessages || phase !== "idle") && (
|
{(hasMessages || phase !== "idle") && (
|
||||||
<div className={`flex w-full justify-center ${hasMessages ? "mb-1" : "mb-3"}`}>
|
<div className={`flex w-full justify-center ${hasMessages ? "mb-1" : "mb-3"}`}>
|
||||||
<div
|
<div
|
||||||
|
role="status"
|
||||||
className="inline-flex items-center gap-2 rounded-full border border-border bg-foreground/5 px-2.5 py-0.5 text-ui-label font-semibold uppercase tracking-wider text-muted-foreground backdrop-blur-sm"
|
className="inline-flex items-center gap-2 rounded-full border border-border bg-foreground/5 px-2.5 py-0.5 text-ui-label font-semibold uppercase tracking-wider text-muted-foreground backdrop-blur-sm"
|
||||||
style={
|
style={
|
||||||
reducedMotion
|
reducedMotion
|
||||||
@ -434,12 +435,6 @@ export function OoGroupStage({
|
|||||||
multiOo,
|
multiOo,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const headerSubtitle = multiOo
|
|
||||||
? t`Group conversation`
|
|
||||||
: speaker
|
|
||||||
? t`1:1 with ${speaker.name}`
|
|
||||||
: t`1:1 conversation`;
|
|
||||||
|
|
||||||
const energy = groupStageEnergy(phase, groupSpark, workingBots.length);
|
const energy = groupStageEnergy(phase, groupSpark, workingBots.length);
|
||||||
|
|
||||||
const phaseLabel = showEnsembleOnly
|
const phaseLabel = showEnsembleOnly
|
||||||
@ -477,7 +472,10 @@ export function OoGroupStage({
|
|||||||
className={`relative z-10 mx-auto flex w-full max-w-3xl flex-col items-center px-4 md:px-8 ${hasMessages ? "py-2 md:py-3" : "py-5 md:py-6"}`}
|
className={`relative z-10 mx-auto flex w-full max-w-3xl flex-col items-center px-4 md:px-8 ${hasMessages ? "py-2 md:py-3" : "py-5 md:py-6"}`}
|
||||||
>
|
>
|
||||||
<div className="mb-3 w-full min-w-0 text-center">
|
<div className="mb-3 w-full min-w-0 text-center">
|
||||||
<div className="inline-flex items-center gap-2 rounded-full border border-border bg-foreground/5 px-2.5 py-0.5 text-ui-label font-semibold uppercase tracking-wider text-muted-foreground backdrop-blur-sm">
|
<div
|
||||||
|
role="status"
|
||||||
|
className="inline-flex items-center gap-2 rounded-full border border-border bg-foreground/5 px-2.5 py-0.5 text-ui-label font-semibold uppercase tracking-wider text-muted-foreground backdrop-blur-sm"
|
||||||
|
>
|
||||||
<span
|
<span
|
||||||
className={`size-1.5 rounded-full ${
|
className={`size-1.5 rounded-full ${
|
||||||
running ? "bg-foreground/70" : "bg-success"
|
running ? "bg-foreground/70" : "bg-success"
|
||||||
@ -488,7 +486,6 @@ export function OoGroupStage({
|
|||||||
<h2 className="mt-1.5 truncate text-ui-title font-semibold tracking-tight text-foreground">
|
<h2 className="mt-1.5 truncate text-ui-title font-semibold tracking-tight text-foreground">
|
||||||
{groupName ?? t`Group`}
|
{groupName ?? t`Group`}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="truncate text-ui-body-sm text-muted-foreground">{headerSubtitle}</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{awarenessLabel && awarenessEvent && speaker ? (
|
{awarenessLabel && awarenessEvent && speaker ? (
|
||||||
|
|||||||
@ -20,7 +20,6 @@ export function VoiceAwarenessStrip({
|
|||||||
handoff: t`Handoff`,
|
handoff: t`Handoff`,
|
||||||
delegation: t`Delegated`,
|
delegation: t`Delegated`,
|
||||||
"peer-reply": t`Peer reply`,
|
"peer-reply": t`Peer reply`,
|
||||||
working: t`In progress`,
|
|
||||||
}[event.kind];
|
}[event.kind];
|
||||||
const Icon =
|
const Icon =
|
||||||
event.kind === "subagent"
|
event.kind === "subagent"
|
||||||
|
|||||||
@ -46,7 +46,7 @@ export interface BotPersonaProfile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface VoiceAwarenessEvent {
|
export interface VoiceAwarenessEvent {
|
||||||
kind: "subagent" | "handoff" | "delegation" | "peer-reply" | "working";
|
kind: "subagent" | "handoff" | "delegation" | "peer-reply";
|
||||||
actorName: string;
|
actorName: string;
|
||||||
detail: string;
|
detail: string;
|
||||||
targetName?: string;
|
targetName?: string;
|
||||||
@ -347,13 +347,6 @@ export function extractVoiceAwareness(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (snapshot.run && ["running", "queued", "leased"].includes(snapshot.run.status)) {
|
|
||||||
return {
|
|
||||||
kind: "working",
|
|
||||||
actorName: botName,
|
|
||||||
detail: "",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -372,8 +365,6 @@ export function narrateAwarenessForPersona(
|
|||||||
return narrateDelegation(profile.archetype, specialist, task);
|
return narrateDelegation(profile.archetype, specialist, task);
|
||||||
case "peer-reply":
|
case "peer-reply":
|
||||||
return narratePeerReply(profile.archetype, event.actorName, task);
|
return narratePeerReply(profile.archetype, event.actorName, task);
|
||||||
case "working":
|
|
||||||
return narrateWorking(profile.archetype, event.actorName);
|
|
||||||
default:
|
default:
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
@ -455,19 +446,6 @@ export function narrateGroupSpark(spark: GroupSpark, speakerArchetype?: PersonaA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function narrateWorking(archetype: PersonaArchetype, name: string): string {
|
|
||||||
switch (archetype) {
|
|
||||||
case "cto-executive":
|
|
||||||
return `${name} is in the work — give me a moment.`;
|
|
||||||
case "quant":
|
|
||||||
return `${name} is crunching — one beat.`;
|
|
||||||
case "engineer":
|
|
||||||
return `${name} is heads-down on this.`;
|
|
||||||
default:
|
|
||||||
return `${name} is working on your request.`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function truncate(text: string, max: number): string {
|
function truncate(text: string, max: number): string {
|
||||||
const trimmed = text.trim();
|
const trimmed = text.trim();
|
||||||
if (trimmed.length <= max) return trimmed;
|
if (trimmed.length <= max) return trimmed;
|
||||||
|
|||||||
@ -319,8 +319,6 @@ msgid "1:1 conversation with {title}"
|
|||||||
msgstr "1:1-Unterhaltung mit {title}"
|
msgstr "1:1-Unterhaltung mit {title}"
|
||||||
|
|
||||||
#. placeholder {0}: mainConversationEntry.bot.name
|
#. placeholder {0}: mainConversationEntry.bot.name
|
||||||
#. placeholder {0}: speaker.name
|
|
||||||
#: src/components/stage/OoStage.tsx
|
|
||||||
#: src/pages/Shell.tsx
|
#: src/pages/Shell.tsx
|
||||||
msgid "1:1 with {0}"
|
msgid "1:1 with {0}"
|
||||||
msgstr "1:1 mit {0}"
|
msgstr "1:1 mit {0}"
|
||||||
@ -2796,7 +2794,6 @@ msgstr "OpenAPI-JSON importieren"
|
|||||||
msgid "In my lane — happy to geek out or just execute, your call."
|
msgid "In my lane — happy to geek out or just execute, your call."
|
||||||
msgstr "In meinem Fachgebiet — ich gehe gern ins Detail oder setze einfach um, du entscheidest."
|
msgstr "In meinem Fachgebiet — ich gehe gern ins Detail oder setze einfach um, du entscheidest."
|
||||||
|
|
||||||
#: src/components/voice/VoiceAwarenessStrip.tsx
|
|
||||||
#: src/pages/ProjectSessionTimeline.tsx
|
#: src/pages/ProjectSessionTimeline.tsx
|
||||||
msgid "In progress"
|
msgid "In progress"
|
||||||
msgstr "In Arbeit"
|
msgstr "In Arbeit"
|
||||||
|
|||||||
@ -319,8 +319,6 @@ msgid "1:1 conversation with {title}"
|
|||||||
msgstr "1:1 conversation with {title}"
|
msgstr "1:1 conversation with {title}"
|
||||||
|
|
||||||
#. placeholder {0}: mainConversationEntry.bot.name
|
#. placeholder {0}: mainConversationEntry.bot.name
|
||||||
#. placeholder {0}: speaker.name
|
|
||||||
#: src/components/stage/OoStage.tsx
|
|
||||||
#: src/pages/Shell.tsx
|
#: src/pages/Shell.tsx
|
||||||
msgid "1:1 with {0}"
|
msgid "1:1 with {0}"
|
||||||
msgstr "1:1 with {0}"
|
msgstr "1:1 with {0}"
|
||||||
@ -2796,7 +2794,6 @@ msgstr "Import OpenAPI JSON"
|
|||||||
msgid "In my lane — happy to geek out or just execute, your call."
|
msgid "In my lane — happy to geek out or just execute, your call."
|
||||||
msgstr "In my lane — happy to geek out or just execute, your call."
|
msgstr "In my lane — happy to geek out or just execute, your call."
|
||||||
|
|
||||||
#: src/components/voice/VoiceAwarenessStrip.tsx
|
|
||||||
#: src/pages/ProjectSessionTimeline.tsx
|
#: src/pages/ProjectSessionTimeline.tsx
|
||||||
msgid "In progress"
|
msgid "In progress"
|
||||||
msgstr "In progress"
|
msgstr "In progress"
|
||||||
|
|||||||
@ -319,8 +319,6 @@ msgid "1:1 conversation with {title}"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. placeholder {0}: mainConversationEntry.bot.name
|
#. placeholder {0}: mainConversationEntry.bot.name
|
||||||
#. placeholder {0}: speaker.name
|
|
||||||
#: src/components/stage/OoStage.tsx
|
|
||||||
#: src/pages/Shell.tsx
|
#: src/pages/Shell.tsx
|
||||||
msgid "1:1 with {0}"
|
msgid "1:1 with {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2796,7 +2794,6 @@ msgstr "Importar JSON de OpenAPI"
|
|||||||
msgid "In my lane — happy to geek out or just execute, your call."
|
msgid "In my lane — happy to geek out or just execute, your call."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/voice/VoiceAwarenessStrip.tsx
|
|
||||||
#: src/pages/ProjectSessionTimeline.tsx
|
#: src/pages/ProjectSessionTimeline.tsx
|
||||||
msgid "In progress"
|
msgid "In progress"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@ -319,8 +319,6 @@ msgid "1:1 conversation with {title}"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. placeholder {0}: mainConversationEntry.bot.name
|
#. placeholder {0}: mainConversationEntry.bot.name
|
||||||
#. placeholder {0}: speaker.name
|
|
||||||
#: src/components/stage/OoStage.tsx
|
|
||||||
#: src/pages/Shell.tsx
|
#: src/pages/Shell.tsx
|
||||||
msgid "1:1 with {0}"
|
msgid "1:1 with {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2796,7 +2794,6 @@ msgstr "OpenAPI JSON इम्पोर्ट करें"
|
|||||||
msgid "In my lane — happy to geek out or just execute, your call."
|
msgid "In my lane — happy to geek out or just execute, your call."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/voice/VoiceAwarenessStrip.tsx
|
|
||||||
#: src/pages/ProjectSessionTimeline.tsx
|
#: src/pages/ProjectSessionTimeline.tsx
|
||||||
msgid "In progress"
|
msgid "In progress"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@ -319,8 +319,6 @@ msgid "1:1 conversation with {title}"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. placeholder {0}: mainConversationEntry.bot.name
|
#. placeholder {0}: mainConversationEntry.bot.name
|
||||||
#. placeholder {0}: speaker.name
|
|
||||||
#: src/components/stage/OoStage.tsx
|
|
||||||
#: src/pages/Shell.tsx
|
#: src/pages/Shell.tsx
|
||||||
msgid "1:1 with {0}"
|
msgid "1:1 with {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2796,7 +2794,6 @@ msgstr "OpenAPI JSON 가져오기"
|
|||||||
msgid "In my lane — happy to geek out or just execute, your call."
|
msgid "In my lane — happy to geek out or just execute, your call."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/voice/VoiceAwarenessStrip.tsx
|
|
||||||
#: src/pages/ProjectSessionTimeline.tsx
|
#: src/pages/ProjectSessionTimeline.tsx
|
||||||
msgid "In progress"
|
msgid "In progress"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@ -319,8 +319,6 @@ msgid "1:1 conversation with {title}"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. placeholder {0}: mainConversationEntry.bot.name
|
#. placeholder {0}: mainConversationEntry.bot.name
|
||||||
#. placeholder {0}: speaker.name
|
|
||||||
#: src/components/stage/OoStage.tsx
|
|
||||||
#: src/pages/Shell.tsx
|
#: src/pages/Shell.tsx
|
||||||
msgid "1:1 with {0}"
|
msgid "1:1 with {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2796,7 +2794,6 @@ msgstr "Importar OpenAPI JSON"
|
|||||||
msgid "In my lane — happy to geek out or just execute, your call."
|
msgid "In my lane — happy to geek out or just execute, your call."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/voice/VoiceAwarenessStrip.tsx
|
|
||||||
#: src/pages/ProjectSessionTimeline.tsx
|
#: src/pages/ProjectSessionTimeline.tsx
|
||||||
msgid "In progress"
|
msgid "In progress"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@ -319,8 +319,6 @@ msgid "1:1 conversation with {title}"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. placeholder {0}: mainConversationEntry.bot.name
|
#. placeholder {0}: mainConversationEntry.bot.name
|
||||||
#. placeholder {0}: speaker.name
|
|
||||||
#: src/components/stage/OoStage.tsx
|
|
||||||
#: src/pages/Shell.tsx
|
#: src/pages/Shell.tsx
|
||||||
msgid "1:1 with {0}"
|
msgid "1:1 with {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2796,7 +2794,6 @@ msgstr "Импортировать OpenAPI JSON"
|
|||||||
msgid "In my lane — happy to geek out or just execute, your call."
|
msgid "In my lane — happy to geek out or just execute, your call."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/voice/VoiceAwarenessStrip.tsx
|
|
||||||
#: src/pages/ProjectSessionTimeline.tsx
|
#: src/pages/ProjectSessionTimeline.tsx
|
||||||
msgid "In progress"
|
msgid "In progress"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@ -319,8 +319,6 @@ msgid "1:1 conversation with {title}"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. placeholder {0}: mainConversationEntry.bot.name
|
#. placeholder {0}: mainConversationEntry.bot.name
|
||||||
#. placeholder {0}: speaker.name
|
|
||||||
#: src/components/stage/OoStage.tsx
|
|
||||||
#: src/pages/Shell.tsx
|
#: src/pages/Shell.tsx
|
||||||
msgid "1:1 with {0}"
|
msgid "1:1 with {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2796,7 +2794,6 @@ msgstr "OpenAPI JSON içe aktar"
|
|||||||
msgid "In my lane — happy to geek out or just execute, your call."
|
msgid "In my lane — happy to geek out or just execute, your call."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/voice/VoiceAwarenessStrip.tsx
|
|
||||||
#: src/pages/ProjectSessionTimeline.tsx
|
#: src/pages/ProjectSessionTimeline.tsx
|
||||||
msgid "In progress"
|
msgid "In progress"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@ -319,8 +319,6 @@ msgid "1:1 conversation with {title}"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. placeholder {0}: mainConversationEntry.bot.name
|
#. placeholder {0}: mainConversationEntry.bot.name
|
||||||
#. placeholder {0}: speaker.name
|
|
||||||
#: src/components/stage/OoStage.tsx
|
|
||||||
#: src/pages/Shell.tsx
|
#: src/pages/Shell.tsx
|
||||||
msgid "1:1 with {0}"
|
msgid "1:1 with {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2796,7 +2794,6 @@ msgstr "导入 OpenAPI JSON"
|
|||||||
msgid "In my lane — happy to geek out or just execute, your call."
|
msgid "In my lane — happy to geek out or just execute, your call."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/voice/VoiceAwarenessStrip.tsx
|
|
||||||
#: src/pages/ProjectSessionTimeline.tsx
|
#: src/pages/ProjectSessionTimeline.tsx
|
||||||
msgid "In progress"
|
msgid "In progress"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { i18n } from "@lingui/core";
|
|||||||
import { t } from "@lingui/core/macro";
|
import { t } from "@lingui/core/macro";
|
||||||
import { Trans, useLingui } from "@lingui/react/macro";
|
import { Trans, useLingui } from "@lingui/react/macro";
|
||||||
import type { RunActivityRow } from "@rakazo/contracts";
|
import type { RunActivityRow } from "@rakazo/contracts";
|
||||||
|
import { runChatTitle } from "@rakazo/core";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { isNeedsYouStatus, runActivityLabel } from "../lib/agent-activity";
|
import { isNeedsYouStatus, runActivityLabel } from "../lib/agent-activity";
|
||||||
import { rpc } from "../lib/rpc";
|
import { rpc } from "../lib/rpc";
|
||||||
@ -93,7 +94,7 @@ export function ActivityList({ onOpenRun }: ActivityListProps) {
|
|||||||
|
|
||||||
function ActivityRow({ run, onOpen }: { run: RunActivityRow; onOpen: () => void }) {
|
function ActivityRow({ run, onOpen }: { run: RunActivityRow; onOpen: () => void }) {
|
||||||
const { t } = useLingui();
|
const { t } = useLingui();
|
||||||
const title = run.groupName ? `${run.botName} · ${run.groupName}` : run.botName;
|
const title = runChatTitle(run);
|
||||||
const label = statusLabel(run.status);
|
const label = statusLabel(run.status);
|
||||||
const activityLabel = t`${title}, ${label}`;
|
const activityLabel = t`${title}, ${label}`;
|
||||||
const tone = statusTone(run.status);
|
const tone = statusTone(run.status);
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import type {
|
|||||||
HabitToday,
|
HabitToday,
|
||||||
RunActivityRow,
|
RunActivityRow,
|
||||||
} from "@rakazo/contracts";
|
} from "@rakazo/contracts";
|
||||||
|
import { runChatTitle } from "@rakazo/core";
|
||||||
import { botColors } from "@rakazo/ui-tokens";
|
import { botColors } from "@rakazo/ui-tokens";
|
||||||
import { BotAvatar, Button, EvooMascot, GroupAvatar, Icon } from "@rakazo/ui-web";
|
import { BotAvatar, Button, EvooMascot, GroupAvatar, Icon } from "@rakazo/ui-web";
|
||||||
import { CircleCheck, Pause, Play, Square } from "lucide-react";
|
import { CircleCheck, Pause, Play, Square } from "lucide-react";
|
||||||
@ -347,7 +348,7 @@ function NeedsYouRow({
|
|||||||
|
|
||||||
if (item.kind === "run") {
|
if (item.kind === "run") {
|
||||||
const run = item.run;
|
const run = item.run;
|
||||||
const title = run.groupName ? `${run.botName} · ${run.groupName}` : run.botName;
|
const title = runChatTitle(run);
|
||||||
const subtitle = run.promptSnippet || runActivityLabel(run.status);
|
const subtitle = run.promptSnippet || runActivityLabel(run.status);
|
||||||
const label = t`${title}, ${runActivityLabel(run.status)}`;
|
const label = t`${title}, ${runActivityLabel(run.status)}`;
|
||||||
return (
|
return (
|
||||||
@ -418,7 +419,7 @@ function DoingRow({
|
|||||||
}) {
|
}) {
|
||||||
const { t } = useLingui();
|
const { t } = useLingui();
|
||||||
const run = item.run;
|
const run = item.run;
|
||||||
const title = run.groupName ? `${run.botName} · ${run.groupName}` : run.botName;
|
const title = runChatTitle(run);
|
||||||
const subtitle = run.promptSnippet || runActivityLabel(run.status);
|
const subtitle = run.promptSnippet || runActivityLabel(run.status);
|
||||||
const label = t`${title}, ${runActivityLabel(run.status)}`;
|
const label = t`${title}, ${runActivityLabel(run.status)}`;
|
||||||
|
|
||||||
@ -458,7 +459,7 @@ function DoneRow({
|
|||||||
}) {
|
}) {
|
||||||
const { t } = useLingui();
|
const { t } = useLingui();
|
||||||
const run = item.run;
|
const run = item.run;
|
||||||
const title = run.groupName ? `${run.botName} · ${run.groupName}` : run.botName;
|
const title = runChatTitle(run);
|
||||||
const subtitle = run.promptSnippet || runActivityLabel(run.status);
|
const subtitle = run.promptSnippet || runActivityLabel(run.status);
|
||||||
const label = t`${title}, ${runActivityLabel(run.status)}`;
|
const label = t`${title}, ${runActivityLabel(run.status)}`;
|
||||||
|
|
||||||
|
|||||||
@ -313,15 +313,16 @@ export const Transcript = memo(function Transcript({
|
|||||||
<div
|
<div
|
||||||
ref={scrollRef}
|
ref={scrollRef}
|
||||||
data-testid="transcript"
|
data-testid="transcript"
|
||||||
|
// A tap at the latest message keeps following; dragging away stops it in onScroll.
|
||||||
onPointerDown={(event) => {
|
onPointerDown={(event) => {
|
||||||
lastScrollTop.current = event.currentTarget.scrollTop;
|
lastScrollTop.current = event.currentTarget.scrollTop;
|
||||||
autoScrolling.current = false;
|
autoScrolling.current = false;
|
||||||
following.current = false;
|
following.current = transcriptIsNearEnd(event.currentTarget);
|
||||||
}}
|
}}
|
||||||
onTouchStart={(event) => {
|
onTouchStart={(event) => {
|
||||||
lastScrollTop.current = event.currentTarget.scrollTop;
|
lastScrollTop.current = event.currentTarget.scrollTop;
|
||||||
autoScrolling.current = false;
|
autoScrolling.current = false;
|
||||||
following.current = false;
|
following.current = transcriptIsNearEnd(event.currentTarget);
|
||||||
}}
|
}}
|
||||||
onWheel={(event) => {
|
onWheel={(event) => {
|
||||||
if (event.deltaY < 0) {
|
if (event.deltaY < 0) {
|
||||||
|
|||||||
@ -4104,6 +4104,7 @@ export function createRunExecutor(deps: ExecutorDeps) {
|
|||||||
? "Use list_secrets to discover saved names, secret_request to make authenticated requests without reading credentials, and forget_secret to revoke access."
|
? "Use list_secrets to discover saved names, secret_request to make authenticated requests without reading credentials, and forget_secret to revoke access."
|
||||||
: undefined,
|
: undefined,
|
||||||
"Never ask for a raw credential in chat or inject it into shell commands.",
|
"Never ask for a raw credential in chat or inject it into shell commands.",
|
||||||
|
"Tell the user what you do in plain words; never name your tools to them.",
|
||||||
exposedToolNames.has("remember") ? "Use remember for durable facts." : undefined,
|
exposedToolNames.has("remember") ? "Use remember for durable facts." : undefined,
|
||||||
exposedToolNames.has("scratchpad_add")
|
exposedToolNames.has("scratchpad_add")
|
||||||
? "Use scratchpad_add / scratchpad_update / scratchpad_complete for open work that should outlive this turn (not reminders — those are schedule_*)."
|
? "Use scratchpad_add / scratchpad_update / scratchpad_complete for open work that should outlive this turn (not reminders — those are schedule_*)."
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import * as fc from "fast-check";
|
import * as fc from "fast-check";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { assertTransition, canTransition } from "./run-state.js";
|
import { assertTransition, canTransition, runChatTitle } from "./run-state.js";
|
||||||
|
|
||||||
describe("run state machine", () => {
|
describe("run state machine", () => {
|
||||||
it("allows takeover resume onto a lease", () => {
|
it("allows takeover resume onto a lease", () => {
|
||||||
@ -30,3 +30,11 @@ describe("run state machine", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("runChatTitle", () => {
|
||||||
|
it("names the group unless it repeats the bot's name", () => {
|
||||||
|
expect(runChatTitle({ botName: "Writer", groupName: "Launch" })).toBe("Writer · Launch");
|
||||||
|
expect(runChatTitle({ botName: "Chief", groupName: "Chief" })).toBe("Chief");
|
||||||
|
expect(runChatTitle({ botName: "Chief", groupName: null })).toBe("Chief");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -41,3 +41,10 @@ export function isTerminal(status: RunStatus): boolean {
|
|||||||
export function nextFence(current: number): number {
|
export function nextFence(current: number): number {
|
||||||
return current + 1;
|
return current + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A run's chat as one label: the bot, and its group unless the group carries the bot's name. */
|
||||||
|
export function runChatTitle(run: { botName: string; groupName: string | null }): string {
|
||||||
|
return run.groupName && run.groupName !== run.botName
|
||||||
|
? `${run.botName} · ${run.groupName}`
|
||||||
|
: run.botName;
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user