#20052: fix(agents): suppress narration text when assistant message has tool calls (#20005)
agents
size: S
experienced-contributor
Cluster:
Tool Result Handling Improvements
## 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
#23164: fix: suppress narration text when assistant message contains tool c...
by ahdernasr · 2026-02-22
84.8%
#19932: feat(agents): suppressPreToolText config + onBlockReply buffering
by Milofax · 2026-02-18
79.0%
#4495: Fix: emit final assistant event when reply tags hide stream
by ukeate · 2026-01-30
76.9%
#16966: fix: strip tool_use blocks from aborted/errored assistant messages
by StressTestor · 2026-02-15
76.7%
#19235: fix(telegram): tool error warnings no longer overwrite streamed rep...
by gatewaybuddy · 2026-02-17
75.9%
#17552: fix(agents): suppress tool error warnings when assistant already re...
by AytuncYildizli · 2026-02-15
75.9%
#14328: fix: strip incomplete tool_use blocks from errored/aborted messages...
by Kropiunig · 2026-02-12
75.8%
#14946: fix(webchat): accumulate text across blocks in streaming buffer
by mcaxtr · 2026-02-12
75.8%
#15996: fix(agents): messages arrive out of order — tool output beats narra...
by yinghaosang · 2026-02-14
75.6%
#16733: fix(ui): avoid injected newlines when tool output is hidden
by jp117 · 2026-02-15
75.4%