From ec6719b04e5e088e9ce4333c6ea2de02889d10c7 Mon Sep 17 00:00:00 2001 From: Brooklyn Nicholson Date: Sun, 26 Jul 2026 16:03:49 -0500 Subject: [PATCH] fix(desktop): stop the tool row repeating its command three times The expanded terminal row printed the same string as the title, as the `$` transcript, and again as detail. shellCommand preferred the backend's display preview over the real `command` arg, so the transcript showed a summary of what ran rather than what ran; and a terminal call with no output fell through to the generic fallback, which echoes args.context under a transcript already showing it. --- .../assistant-ui/tool/fallback-model.test.ts | 22 +++++++++++++++++++ .../assistant-ui/tool/fallback-model/index.ts | 14 +++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/apps/desktop/src/components/assistant-ui/tool/fallback-model.test.ts b/apps/desktop/src/components/assistant-ui/tool/fallback-model.test.ts index 275cc13fb4f6..19a8cd61447c 100644 --- a/apps/desktop/src/components/assistant-ui/tool/fallback-model.test.ts +++ b/apps/desktop/src/components/assistant-ui/tool/fallback-model.test.ts @@ -340,6 +340,28 @@ describe('buildToolView title actions', () => { expect(view.titleAction).toEqual({ prefix: '', text: 'Running', suffix: ' pnpm run lint' }) }) + it('never stutters the verb or echoes the command when the backend context is a phrased label', () => { + // Older backends stamped tool.start with a *phrased* label + // ("Running sleep 70 + 2 commands") rather than a raw arg preview, and the + // desktop merges that into args.context. The row must still prepend its own + // verb exactly once, show the real command in the `$` transcript, and not + // repeat either string as detail. + const command = 'sleep 70; echo "a"; echo "b"' + + const view = buildToolView( + part({ + args: { command, context: 'Running sleep 70 + 2 commands' }, + result: { exit_code: 0 }, + toolName: 'terminal' + }), + '' + ) + + expect(view.title).toBe('Ran sleep 70 + 2 commands') + expect(view.terminalCommand).toBe(command) + expect(view.detail).toBe('') + }) + it('uses the runtime locale for title text and action placement', () => { setRuntimeI18nLocale('ja') diff --git a/apps/desktop/src/components/assistant-ui/tool/fallback-model/index.ts b/apps/desktop/src/components/assistant-ui/tool/fallback-model/index.ts index 5c6fd8c9b034..6bcd06a63a93 100644 --- a/apps/desktop/src/components/assistant-ui/tool/fallback-model/index.ts +++ b/apps/desktop/src/components/assistant-ui/tool/fallback-model/index.ts @@ -128,9 +128,14 @@ function readFileDisplayTarget(args: Record, result: Record): string { return ( - firstStringField(args, ['context', 'preview']) || firstStringField(args, ['command', 'code']) || contextValue(args) + firstStringField(args, ['command', 'code']) || firstStringField(args, ['context', 'preview']) || contextValue(args) ) } @@ -1079,6 +1084,13 @@ function toolDetailText( if (output || lines) { return [output, lines].filter(Boolean).join('\n') } + + // A terminal row with no output already shows its command in the `$` + // transcript above; the generic fallback would print the same string a + // second time. `execute_code` has no transcript, so it keeps the fallback. + if (part.toolName === 'terminal') { + return '' + } } if (part.toolName === 'web_extract') {