From 83d2106633a83d9a07d530f53c57f94074d2f1e9 Mon Sep 17 00:00:00 2001 From: cynarAI <155274210+cynarAI@users.noreply.github.com> Date: Fri, 9 Oct 2026 17:48:18 +0200 Subject: [PATCH] Quieter chat chrome: no repeated names, no English working line, no tool names (#186) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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) 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) 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) 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) 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) Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ --------- Co-authored-by: Claude --- apps/mobile/app/index.tsx | 3 ++- apps/web/e2e/first-run-onboarding-ux.spec.ts | 2 +- apps/web/src/components/stage/OoStage.tsx | 13 ++++------ .../components/voice/VoiceAwarenessStrip.tsx | 1 - apps/web/src/lib/bot-persona.ts | 24 +------------------ apps/web/src/locales/de/messages.po | 3 --- apps/web/src/locales/en/messages.po | 3 --- apps/web/src/locales/es/messages.po | 3 --- apps/web/src/locales/hi/messages.po | 3 --- apps/web/src/locales/ko/messages.po | 3 --- apps/web/src/locales/pt-BR/messages.po | 3 --- apps/web/src/locales/ru/messages.po | 3 --- apps/web/src/locales/tr/messages.po | 3 --- apps/web/src/locales/zh-CN/messages.po | 3 --- apps/web/src/pages/ActivityList.tsx | 3 ++- apps/web/src/pages/HeuteBrief.tsx | 7 +++--- apps/web/src/pages/shell/transcript.tsx | 5 ++-- packages/adapters/src/executor.ts | 1 + packages/core/src/run-state.test.ts | 10 +++++++- packages/core/src/run-state.ts | 7 ++++++ 20 files changed, 35 insertions(+), 68 deletions(-) diff --git a/apps/mobile/app/index.tsx b/apps/mobile/app/index.tsx index 699f2a2..91c69f1 100644 --- a/apps/mobile/app/index.tsx +++ b/apps/mobile/app/index.tsx @@ -6,6 +6,7 @@ import { type SpaceBot, type SpaceGroup, } from "@rakazo/contracts"; +import { runChatTitle } from "@rakazo/core"; import { botColors } from "@rakazo/ui-tokens"; import { Redirect, useFocusEffect, useRouter } from "expo-router"; import { type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from "react"; @@ -828,7 +829,7 @@ function ActivityRow({ bot?: MobileBot; onPress: () => void; }) { - const title = run.groupName ? `${run.botName} · ${run.groupName}` : run.botName; + const title = runChatTitle(run); const status = activityStatusLabel(run.status); const preview = run.promptSnippet ? `${run.promptSnippet} · ${status}` : status; return ( diff --git a/apps/web/e2e/first-run-onboarding-ux.spec.ts b/apps/web/e2e/first-run-onboarding-ux.spec.ts index 2dfa45f..73c844a 100644 --- a/apps/web/e2e/first-run-onboarding-ux.spec.ts +++ b/apps/web/e2e/first-run-onboarding-ux.spec.ts @@ -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 completeOnboarding(page, undefined, { mainConversation: true }); 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", () => { diff --git a/apps/web/src/components/stage/OoStage.tsx b/apps/web/src/components/stage/OoStage.tsx index 586486e..9817ee8 100644 --- a/apps/web/src/components/stage/OoStage.tsx +++ b/apps/web/src/components/stage/OoStage.tsx @@ -171,6 +171,7 @@ export function OoStage({ {(hasMessages || phase !== "idle") && (
-
+
{groupName ?? t`Group`} -

{headerSubtitle}

{awarenessLabel && awarenessEvent && speaker ? ( diff --git a/apps/web/src/components/voice/VoiceAwarenessStrip.tsx b/apps/web/src/components/voice/VoiceAwarenessStrip.tsx index e299d11..f16b72a 100644 --- a/apps/web/src/components/voice/VoiceAwarenessStrip.tsx +++ b/apps/web/src/components/voice/VoiceAwarenessStrip.tsx @@ -20,7 +20,6 @@ export function VoiceAwarenessStrip({ handoff: t`Handoff`, delegation: t`Delegated`, "peer-reply": t`Peer reply`, - working: t`In progress`, }[event.kind]; const Icon = event.kind === "subagent" diff --git a/apps/web/src/lib/bot-persona.ts b/apps/web/src/lib/bot-persona.ts index 2645819..04b5dfa 100644 --- a/apps/web/src/lib/bot-persona.ts +++ b/apps/web/src/lib/bot-persona.ts @@ -46,7 +46,7 @@ export interface BotPersonaProfile { } export interface VoiceAwarenessEvent { - kind: "subagent" | "handoff" | "delegation" | "peer-reply" | "working"; + kind: "subagent" | "handoff" | "delegation" | "peer-reply"; actorName: string; detail: 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; } @@ -372,8 +365,6 @@ export function narrateAwarenessForPersona( return narrateDelegation(profile.archetype, specialist, task); case "peer-reply": return narratePeerReply(profile.archetype, event.actorName, task); - case "working": - return narrateWorking(profile.archetype, event.actorName); default: 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 { const trimmed = text.trim(); if (trimmed.length <= max) return trimmed; diff --git a/apps/web/src/locales/de/messages.po b/apps/web/src/locales/de/messages.po index 901cd15..260de3a 100644 --- a/apps/web/src/locales/de/messages.po +++ b/apps/web/src/locales/de/messages.po @@ -319,8 +319,6 @@ msgid "1:1 conversation with {title}" msgstr "1:1-Unterhaltung mit {title}" #. placeholder {0}: mainConversationEntry.bot.name -#. placeholder {0}: speaker.name -#: src/components/stage/OoStage.tsx #: src/pages/Shell.tsx msgid "1:1 with {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." msgstr "In meinem Fachgebiet — ich gehe gern ins Detail oder setze einfach um, du entscheidest." -#: src/components/voice/VoiceAwarenessStrip.tsx #: src/pages/ProjectSessionTimeline.tsx msgid "In progress" msgstr "In Arbeit" diff --git a/apps/web/src/locales/en/messages.po b/apps/web/src/locales/en/messages.po index 7bb7a10..a0b1b88 100644 --- a/apps/web/src/locales/en/messages.po +++ b/apps/web/src/locales/en/messages.po @@ -319,8 +319,6 @@ msgid "1:1 conversation with {title}" msgstr "1:1 conversation with {title}" #. placeholder {0}: mainConversationEntry.bot.name -#. placeholder {0}: speaker.name -#: src/components/stage/OoStage.tsx #: src/pages/Shell.tsx msgid "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." msgstr "In my lane — happy to geek out or just execute, your call." -#: src/components/voice/VoiceAwarenessStrip.tsx #: src/pages/ProjectSessionTimeline.tsx msgid "In progress" msgstr "In progress" diff --git a/apps/web/src/locales/es/messages.po b/apps/web/src/locales/es/messages.po index de2e52f..9f4e3b9 100644 --- a/apps/web/src/locales/es/messages.po +++ b/apps/web/src/locales/es/messages.po @@ -319,8 +319,6 @@ msgid "1:1 conversation with {title}" msgstr "" #. placeholder {0}: mainConversationEntry.bot.name -#. placeholder {0}: speaker.name -#: src/components/stage/OoStage.tsx #: src/pages/Shell.tsx msgid "1:1 with {0}" msgstr "" @@ -2796,7 +2794,6 @@ msgstr "Importar JSON de OpenAPI" msgid "In my lane — happy to geek out or just execute, your call." msgstr "" -#: src/components/voice/VoiceAwarenessStrip.tsx #: src/pages/ProjectSessionTimeline.tsx msgid "In progress" msgstr "" diff --git a/apps/web/src/locales/hi/messages.po b/apps/web/src/locales/hi/messages.po index d434e10..cf85a9c 100644 --- a/apps/web/src/locales/hi/messages.po +++ b/apps/web/src/locales/hi/messages.po @@ -319,8 +319,6 @@ msgid "1:1 conversation with {title}" msgstr "" #. placeholder {0}: mainConversationEntry.bot.name -#. placeholder {0}: speaker.name -#: src/components/stage/OoStage.tsx #: src/pages/Shell.tsx msgid "1:1 with {0}" msgstr "" @@ -2796,7 +2794,6 @@ msgstr "OpenAPI JSON इम्पोर्ट करें" msgid "In my lane — happy to geek out or just execute, your call." msgstr "" -#: src/components/voice/VoiceAwarenessStrip.tsx #: src/pages/ProjectSessionTimeline.tsx msgid "In progress" msgstr "" diff --git a/apps/web/src/locales/ko/messages.po b/apps/web/src/locales/ko/messages.po index cb7e027..0da0535 100644 --- a/apps/web/src/locales/ko/messages.po +++ b/apps/web/src/locales/ko/messages.po @@ -319,8 +319,6 @@ msgid "1:1 conversation with {title}" msgstr "" #. placeholder {0}: mainConversationEntry.bot.name -#. placeholder {0}: speaker.name -#: src/components/stage/OoStage.tsx #: src/pages/Shell.tsx msgid "1:1 with {0}" msgstr "" @@ -2796,7 +2794,6 @@ msgstr "OpenAPI JSON 가져오기" msgid "In my lane — happy to geek out or just execute, your call." msgstr "" -#: src/components/voice/VoiceAwarenessStrip.tsx #: src/pages/ProjectSessionTimeline.tsx msgid "In progress" msgstr "" diff --git a/apps/web/src/locales/pt-BR/messages.po b/apps/web/src/locales/pt-BR/messages.po index f0a5865..219e406 100644 --- a/apps/web/src/locales/pt-BR/messages.po +++ b/apps/web/src/locales/pt-BR/messages.po @@ -319,8 +319,6 @@ msgid "1:1 conversation with {title}" msgstr "" #. placeholder {0}: mainConversationEntry.bot.name -#. placeholder {0}: speaker.name -#: src/components/stage/OoStage.tsx #: src/pages/Shell.tsx msgid "1:1 with {0}" msgstr "" @@ -2796,7 +2794,6 @@ msgstr "Importar OpenAPI JSON" msgid "In my lane — happy to geek out or just execute, your call." msgstr "" -#: src/components/voice/VoiceAwarenessStrip.tsx #: src/pages/ProjectSessionTimeline.tsx msgid "In progress" msgstr "" diff --git a/apps/web/src/locales/ru/messages.po b/apps/web/src/locales/ru/messages.po index cfd504e..c47c8c4 100644 --- a/apps/web/src/locales/ru/messages.po +++ b/apps/web/src/locales/ru/messages.po @@ -319,8 +319,6 @@ msgid "1:1 conversation with {title}" msgstr "" #. placeholder {0}: mainConversationEntry.bot.name -#. placeholder {0}: speaker.name -#: src/components/stage/OoStage.tsx #: src/pages/Shell.tsx msgid "1:1 with {0}" msgstr "" @@ -2796,7 +2794,6 @@ msgstr "Импортировать OpenAPI JSON" msgid "In my lane — happy to geek out or just execute, your call." msgstr "" -#: src/components/voice/VoiceAwarenessStrip.tsx #: src/pages/ProjectSessionTimeline.tsx msgid "In progress" msgstr "" diff --git a/apps/web/src/locales/tr/messages.po b/apps/web/src/locales/tr/messages.po index c45dc4a..e4a4afb 100644 --- a/apps/web/src/locales/tr/messages.po +++ b/apps/web/src/locales/tr/messages.po @@ -319,8 +319,6 @@ msgid "1:1 conversation with {title}" msgstr "" #. placeholder {0}: mainConversationEntry.bot.name -#. placeholder {0}: speaker.name -#: src/components/stage/OoStage.tsx #: src/pages/Shell.tsx msgid "1:1 with {0}" msgstr "" @@ -2796,7 +2794,6 @@ msgstr "OpenAPI JSON içe aktar" msgid "In my lane — happy to geek out or just execute, your call." msgstr "" -#: src/components/voice/VoiceAwarenessStrip.tsx #: src/pages/ProjectSessionTimeline.tsx msgid "In progress" msgstr "" diff --git a/apps/web/src/locales/zh-CN/messages.po b/apps/web/src/locales/zh-CN/messages.po index 3b41a24..1ede177 100644 --- a/apps/web/src/locales/zh-CN/messages.po +++ b/apps/web/src/locales/zh-CN/messages.po @@ -319,8 +319,6 @@ msgid "1:1 conversation with {title}" msgstr "" #. placeholder {0}: mainConversationEntry.bot.name -#. placeholder {0}: speaker.name -#: src/components/stage/OoStage.tsx #: src/pages/Shell.tsx msgid "1:1 with {0}" msgstr "" @@ -2796,7 +2794,6 @@ msgstr "导入 OpenAPI JSON" msgid "In my lane — happy to geek out or just execute, your call." msgstr "" -#: src/components/voice/VoiceAwarenessStrip.tsx #: src/pages/ProjectSessionTimeline.tsx msgid "In progress" msgstr "" diff --git a/apps/web/src/pages/ActivityList.tsx b/apps/web/src/pages/ActivityList.tsx index 41f0b06..0cb4e5b 100644 --- a/apps/web/src/pages/ActivityList.tsx +++ b/apps/web/src/pages/ActivityList.tsx @@ -2,6 +2,7 @@ import { i18n } from "@lingui/core"; import { t } from "@lingui/core/macro"; import { Trans, useLingui } from "@lingui/react/macro"; import type { RunActivityRow } from "@rakazo/contracts"; +import { runChatTitle } from "@rakazo/core"; import { useEffect, useState } from "react"; import { isNeedsYouStatus, runActivityLabel } from "../lib/agent-activity"; import { rpc } from "../lib/rpc"; @@ -93,7 +94,7 @@ export function ActivityList({ onOpenRun }: ActivityListProps) { function ActivityRow({ run, onOpen }: { run: RunActivityRow; onOpen: () => void }) { const { t } = useLingui(); - const title = run.groupName ? `${run.botName} · ${run.groupName}` : run.botName; + const title = runChatTitle(run); const label = statusLabel(run.status); const activityLabel = t`${title}, ${label}`; const tone = statusTone(run.status); diff --git a/apps/web/src/pages/HeuteBrief.tsx b/apps/web/src/pages/HeuteBrief.tsx index 9379b9b..99660bc 100644 --- a/apps/web/src/pages/HeuteBrief.tsx +++ b/apps/web/src/pages/HeuteBrief.tsx @@ -8,6 +8,7 @@ import type { HabitToday, RunActivityRow, } from "@rakazo/contracts"; +import { runChatTitle } from "@rakazo/core"; import { botColors } from "@rakazo/ui-tokens"; import { BotAvatar, Button, EvooMascot, GroupAvatar, Icon } from "@rakazo/ui-web"; import { CircleCheck, Pause, Play, Square } from "lucide-react"; @@ -347,7 +348,7 @@ function NeedsYouRow({ if (item.kind === "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 label = t`${title}, ${runActivityLabel(run.status)}`; return ( @@ -418,7 +419,7 @@ function DoingRow({ }) { const { t } = useLingui(); 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 label = t`${title}, ${runActivityLabel(run.status)}`; @@ -458,7 +459,7 @@ function DoneRow({ }) { const { t } = useLingui(); 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 label = t`${title}, ${runActivityLabel(run.status)}`; diff --git a/apps/web/src/pages/shell/transcript.tsx b/apps/web/src/pages/shell/transcript.tsx index e3ba975..a19aa1d 100644 --- a/apps/web/src/pages/shell/transcript.tsx +++ b/apps/web/src/pages/shell/transcript.tsx @@ -313,15 +313,16 @@ export const Transcript = memo(function Transcript({
{ lastScrollTop.current = event.currentTarget.scrollTop; autoScrolling.current = false; - following.current = false; + following.current = transcriptIsNearEnd(event.currentTarget); }} onTouchStart={(event) => { lastScrollTop.current = event.currentTarget.scrollTop; autoScrolling.current = false; - following.current = false; + following.current = transcriptIsNearEnd(event.currentTarget); }} onWheel={(event) => { if (event.deltaY < 0) { diff --git a/packages/adapters/src/executor.ts b/packages/adapters/src/executor.ts index 43bc0da..6d1b5c5 100644 --- a/packages/adapters/src/executor.ts +++ b/packages/adapters/src/executor.ts @@ -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." : undefined, "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("scratchpad_add") ? "Use scratchpad_add / scratchpad_update / scratchpad_complete for open work that should outlive this turn (not reminders — those are schedule_*)." diff --git a/packages/core/src/run-state.test.ts b/packages/core/src/run-state.test.ts index e82cbe1..6b61fe2 100644 --- a/packages/core/src/run-state.test.ts +++ b/packages/core/src/run-state.test.ts @@ -1,6 +1,6 @@ import * as fc from "fast-check"; 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", () => { 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"); + }); +}); diff --git a/packages/core/src/run-state.ts b/packages/core/src/run-state.ts index ec2ce84..99fc6b7 100644 --- a/packages/core/src/run-state.ts +++ b/packages/core/src/run-state.ts @@ -41,3 +41,10 @@ export function isTerminal(status: RunStatus): boolean { export function nextFence(current: number): number { 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; +}