-
-
Notifications
You must be signed in to change notification settings - Fork 79.1k
Block streaming: block replies not delivered before tool execution (same-channel) #32868
Copy link
Copy link
Closed
Labels
P1High-priority user-facing bug, regression, or broken workflow.High-priority user-facing bug, regression, or broken workflow.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.ClawSweeper found an open linked pull request for this issue.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.ClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.ClawSweeper found a high-confidence source-level issue reproduction.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.Channel message delivery can be lost, duplicated, or misrouted.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.Very strong issue quality with high-confidence source-level or clear reproduction.
Metadata
Metadata
Assignees
Labels
P1High-priority user-facing bug, regression, or broken workflow.High-priority user-facing bug, regression, or broken workflow.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.ClawSweeper found an open linked pull request for this issue.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.ClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.ClawSweeper found a high-confidence source-level issue reproduction.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.Channel message delivery can be lost, duplicated, or misrouted.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.Very strong issue quality with high-confidence source-level or clear reproduction.
Type
Fields
Give feedbackNo fields configured for issues without a type.
Summary
When block streaming is enabled with
blockStreamingBreak: "text_end", text chunks between tool calls are not delivered to the channel in real-time. Instead, all block replies arrive together after the model turn completes, defeating the purpose of progressive output during multi-tool-call turns.Expected behavior
With
text_endbreak mode, each text block should be delivered to the channel before the next tool executes.handleToolExecutionStartcallsflushBlockReplyBuffer()+await onBlockReplyFlush(), which suggests this is the intended behavior.Actual behavior
All block reply messages arrive at the channel simultaneously after the entire model turn finishes. Tested with tool calls spanning ~48 seconds — all 5 block replies arrived at the same timestamp.
Root cause (source analysis)
In the reply pipeline (
reply-*.js), there are two separate send chains:createBlockReplyPipeline) — chainsonBlockReplycallbackscreateReplyDispatcher) — chainsoptions.deliver()for actual channel deliveryThe
onBlockReplycallback has two paths:dispatcher.sendBlockReply()callsenqueue("block", payload)which appends to the dispatcher'ssendChainbut returns a boolean (true), not a Promise. TheonBlockReplycallback resolves immediately without waiting for actual delivery.As a result,
handleToolExecutionStart'sawait onBlockReplyFlush()only awaits the pipeline sendChain (which resolves instantly), not the dispatcher sendChain that does the actual channel delivery.Why cross-channel works
shouldRouteToOriginatingistrueonly whenoriginatingChannel !== currentSurface(e.g., Telegram → WhatsApp routing). In this path,await sendPayloadAsync()properly awaits delivery. For same-channel replies (the common case — WhatsApp DM → WhatsApp), it always takes the non-awaited dispatcher path.Suggested fix
Make the
onBlockReplycallback await the dispatcher delivery before resolving. Either:dispatcher.sendBlockReply()return a Promise that resolves whenoptions.deliver()completesawait dispatcher.waitForIdle()in theonBlockReplycallback for the same-channel pathEnvironment
{ "blockStreamingDefault": "on", "blockStreamingBreak": "text_end", "blockStreamingChunk": { "minChars": 30, "maxChars": 600 }, "blockStreamingCoalesce": { "minChars": 20, "idleMs": 200 } }Reproduction
text_endbreak mode on WhatsAppWorkaround
Use the
messagetool (message send) for real-time progress updates — this bypasses the block streaming pipeline entirely and delivers immediately via direct API call.