← Back to PRs

#20052: fix(agents): suppress narration text when assistant message has tool calls (#20005)

by lailoo open 2026-02-18 13:13 View on GitHub →
agents size: S experienced-contributor
## Summary - **Bug**: Text between tool calls (narration) leaks to user as messages - **Root cause**: `emitBlockChunk` pushes text to `assistantTexts` during streaming, but when the message contains tool calls, this text should be suppressed - **Fix**: Clear text added during current message when tool calls are detected in `handleMessageEnd` Fixes #20005 ## Problem When an assistant message contains interleaved text and `tool_use` blocks, the text blocks are "narration" (e.g., "Let me search for that information.") and should NOT be delivered to the user. However, `emitBlockChunk` pushes this text to `assistantTexts` during streaming, and `finalizeAssistantTexts` only updates the baseline without clearing the already-pushed text. **Before fix (reproduced on main):** ``` ❌ BUG CONFIRMED: Narration leaked to assistantTexts assistantTexts: [ 'Let me search for that information.' ] ``` ## Changes - `src/agents/pi-embedded-subscribe.handlers.messages.ts` — Import `hasToolCall` and splice out text added during current message when tool calls are detected **After fix:** ``` ✅ PASS: No narration leak ``` ## Test plan - [x] New test: `pi-embedded-subscribe.narration-leak.test.ts` - verifies narration is suppressed when tool calls present - [x] New test: verifies normal replies (no tool calls) are preserved - [x] All 45 existing pi-embedded-subscribe tests pass - [x] Lint passes ## Effect on User Experience **Before:** Users see internal reasoning like "Let me search for that..." as separate messages before tool results **After:** Only the final answer (after tool execution) is delivered to users <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR fixes #20005 by suppressing narration text (e.g., "Let me search for that...") from the `assistantTexts` array when an assistant message contains tool calls. The fix uses `hasToolCall()` to detect tool-use blocks and splices out text added during the current message before `finalizeAssistantTexts` runs. - The `assistantTexts` array is correctly cleaned by splicing entries added since the baseline - New regression tests verify the fix for `assistantTexts` and confirm normal (non-tool-call) messages are preserved - Changelog entry is clear and well-placed **Concerns:** - The `onBlockReply` callback path (lines 320–367 of `handleMessageEnd`) still uses the `text` variable directly, which contains narration regardless of the splice. For `blockReplyBreak: "message_end"` mode, narration text is still delivered to the user via `onBlockReply` - `emitAgentEvent`/`onAgentEvent` at lines 266–285 fires narration before the splice for non-streaming providers - Test contains leftover `console.error`/`console.log` debugging output <h3>Confidence Score: 2/5</h3> - The fix partially addresses the narration leak but has remaining delivery paths that bypass the suppression. - The `assistantTexts` array splice is correct, but narration text can still reach the user through `onBlockReply` (lines 320-367) and `emitAgentEvent` (lines 266-285) which operate on the `text` variable independently of the array. The fix is incomplete for `blockReplyBreak: "message_end"` mode. - src/agents/pi-embedded-subscribe.handlers.messages.ts — the `onBlockReply` and `emitAgentEvent` paths in `handleMessageEnd` still deliver narration text when tool calls are present <sub>Last reviewed commit: ff4d91f</sub> <!-- greptile_other_comments_section --> **Context used:** - Context from `dashboard` - CLAUDE.md ([source](https://app.greptile.com/review/custom-context?memory=fd949e91-5c3a-4ab5-90a1-cbe184fd6ce8)) <!-- /greptile_comment -->

Most Similar PRs