fix(web): suppress Task tool prompt text from leaking into chat#372
fix(web): suppress Task tool prompt text from leaking into chat#372hqhq1025 wants to merge 2 commits intotiann:mainfrom
Conversation
When Claude calls the Task/Agent tool, it often writes the prompt text as a regular text block before the tool_use block in the same message. This causes the prompt to appear twice: once as a standalone message and once inside the tool card. Skip text blocks in agent messages that contain a Task tool_use, since the prompt is already visible in the tool card.
There was a problem hiding this comment.
Findings
- [Major] Task tool-call suppresses all text blocks, not only the duplicated prompt — any non-prompt assistant text in the same message is dropped, losing user-visible content. Evidence
web/src/chat/reducerTimeline.ts:76
Suggested fix:const taskTool = msg.content.find( (content) => content.type === 'tool-call' && content.name === 'Task' ) const promptText = taskTool && typeof taskTool.input === 'object' && taskTool.input !== null && 'prompt' in taskTool.input && typeof (taskTool.input as { prompt: unknown }).prompt === 'string' ? (taskTool.input as { prompt: string }).prompt : null if (promptText && c.text.trim() === promptText.trim()) continue
Summary
Review mode: initial. 1 issue. Potential loss of non-prompt text in Task tool-call messages.
Testing
Not run (automation). Suggested: manual Task tool-call that includes extra assistant text; verify only prompt suppressed.
| for (let idx = 0; idx < msg.content.length; idx += 1) { | ||
| const c = msg.content[idx] | ||
| if (c.type === 'text') { | ||
| if (hasTaskToolUse) continue |
There was a problem hiding this comment.
[MAJOR] Any agent message that contains a Task tool-call will now drop all text blocks, not just the prompt text. That can hide non-prompt assistant content in the same message. Evidence web/src/chat/reducerTimeline.ts:76.
Suggested fix:
const taskTool = msg.content.find(
(content) => content.type === 'tool-call' && content.name === 'Task'
)
const promptText = taskTool
&& typeof taskTool.input === 'object'
&& taskTool.input !== null
&& 'prompt' in taskTool.input
&& typeof (taskTool.input as { prompt: unknown }).prompt === 'string'
? (taskTool.input as { prompt: string }).prompt
: null
if (promptText && c.text.trim() === promptText.trim()) continue…ages Task notifications, command caveats, and system reminders are logged as type:'user' in the JSONL but are not text the human actually typed. The CLI fix (279f758) changed the socket role to 'agent', but the web normalizer still saw data.type='user' and created user-role messages. Filter these out in normalizeUserOutput by checking for known XML tag prefixes (<task-notification>, <command-name>, <system-reminder>, etc.)
| const trimmed = messageContent.trimStart() | ||
| if ( | ||
| trimmed.startsWith('<task-notification>') || | ||
| trimmed.startsWith('<command-name>') || |
There was a problem hiding this comment.
[MAJOR] CLI output cards suppressed
normalizeUserOutput now drops messages starting with <command-name> / <local-command-caveat>, but those tags are what the CLI output renderer parses for terminal command cards. This removes command output from chat.
Suggested fix:
if (
trimmed.startsWith('<task-notification>') ||
trimmed.startsWith('<system-reminder>')
) {
return null
}
Summary
textcontent block before thetool_useblock in the same assistant messagetool_use, since the prompt is already visible in the tool cardBefore
The Agent tool's full prompt text (description + detailed instructions) renders as a standalone markdown message below the tool card, duplicating the content already shown inside the card.
After
Only the Agent tool card is shown. The prompt text is accessible by clicking the card to open the detail dialog.
Test plan