From 430edc436eb10dc8dec41343f9d9879924749b4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E9=98=BF=E6=B1=9F=28Relakkes?= =?UTF-8?q?=29?= Date: Tue, 19 May 2026 03:32:34 +0800 Subject: [PATCH] fix: Preserve readable agent result reports Agent task notifications carry the user-facing Markdown report, while some Agent tool results contain structured intermediate payloads. The UI now keeps the terminal report as the modal/preview source when it exists, and falls back to raw tool output only when there is no report. Constraint: Background Agent runs can emit structured tool_result payloads before the terminal task notification carries the readable Markdown report Rejected: Convert every structured Agent payload into display Markdown | risks inventing summaries and still lets raw transport data compete with the real report Confidence: high Scope-risk: narrow Directive: Do not let raw Agent tool_result content override terminal task result when both are present Tested: cd desktop && bun run test -- run src/components/chat/MessageList.test.tsx -t "prefers the terminal task report" Tested: cd desktop && bun run test -- run src/components/chat/MessageList.test.tsx -t agent Not-tested: Full desktop gate per request to avoid broad gate/test runs --- .../src/components/chat/MessageList.test.tsx | 59 +++++++++++++++++++ desktop/src/components/chat/ToolCallGroup.tsx | 4 +- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/desktop/src/components/chat/MessageList.test.tsx b/desktop/src/components/chat/MessageList.test.tsx index 93e97723..75ca1308 100644 --- a/desktop/src/components/chat/MessageList.test.tsx +++ b/desktop/src/components/chat/MessageList.test.tsx @@ -1075,6 +1075,65 @@ describe('MessageList nested tool calls', () => { expect(within(screen.getByRole('dialog')).getByText(resultText)).toBeTruthy() }) + it('prefers the terminal task report over structured agent tool result JSON', () => { + const markdownReport = '## 审查安全风险\n\n- 最终报告应该按 Markdown 展示。' + + useChatStore.setState({ + sessions: { + [ACTIVE_TAB]: makeSessionState({ + messages: [ + { + id: 'tool-agent', + type: 'tool_use', + toolName: 'Agent', + toolUseId: 'agent-1', + input: { description: '查看安全报告' }, + timestamp: 1, + }, + { + id: 'result-agent', + type: 'tool_result', + toolUseId: 'agent-1', + content: { + results: [ + { + file: 'git:v0.2.6..v0.2.7', + line: 0, + snippet: 'raw structured JSON should not be shown', + context: '结构化检索结果不是给用户看的最终报告。', + }, + ], + }, + isError: false, + timestamp: 2, + }, + ], + agentTaskNotifications: { + 'agent-1': { + taskId: 'agent-task-1', + toolUseId: 'agent-1', + status: 'completed', + summary: 'Agent "审查安全风险" completed', + result: markdownReport, + }, + }, + }), + }, + }) + + render() + + expect(screen.getByText(/最终报告应该按 Markdown 展示。/)).toBeTruthy() + expect(screen.queryByText(/raw structured JSON should not be shown/)).toBeNull() + + fireEvent.click(screen.getByRole('button', { name: 'View result' })) + + const dialog = screen.getByRole('dialog') + expect(within(dialog).getByRole('heading', { name: '审查安全风险' })).toBeTruthy() + expect(within(dialog).getByText('最终报告应该按 Markdown 展示。')).toBeTruthy() + expect(within(dialog).queryByText(/raw structured JSON should not be shown/)).toBeNull() + }) + it('renders copy controls for user messages and scopes assistant copy to a single reply', async () => { const writeText = vi.fn().mockResolvedValue(undefined) Object.assign(navigator, { diff --git a/desktop/src/components/chat/ToolCallGroup.tsx b/desktop/src/components/chat/ToolCallGroup.tsx index 3fdb5649..3a2671ee 100644 --- a/desktop/src/components/chat/ToolCallGroup.tsx +++ b/desktop/src/components/chat/ToolCallGroup.tsx @@ -517,7 +517,9 @@ function AgentCallCard({ result && !result.isError && !isLaunchResult && !isAgentLifecycleResult(result.content) ? extractAgentDisplayText(result.content).trim() : '' - const previewText = fullOutputText || (status === 'done' || status === 'stopped' ? taskResult || taskSummary : '') + const terminalTaskReport = status === 'done' || status === 'stopped' ? taskResult : '' + const terminalTaskSummary = status === 'done' || status === 'stopped' ? taskSummary : '' + const previewText = terminalTaskReport || fullOutputText || terminalTaskSummary const outputSummary = previewText ? getAgentOutputSummary(previewText) : '' const description = typeof input.description === 'string' ? input.description : ''