← Back to PRs

#9171: Fix: Route tool result deliveries through BlockReplyPipeline for proper message ordering

by vishaltandale00 open 2026-02-04 23:41 View on GitHub →
stale
## Summary Fixes #9135 - Messages were arriving out of order on channels like Telegram when an agent produced both tool result summaries and longer block replies in sequence. ## Problem When an agent produces a short summary (tool result) and a longer detailed reply (block reply) in sequence, the short message often arrived *after* the long one. This happened because: 1. Tool result summaries were sent as detached async tasks 2. These bypassed the `BlockReplyPipeline` which maintains message ordering 3. Block replies went through the pipeline with buffering/coalescing 4. The short message "raced ahead" while the long message was still buffering **User Impact:** - Confusing out-of-order messages on Telegram and other channels - Tool summaries appearing after the main response instead of before/during - Poor user experience with responses arriving in wrong sequence ## Root Cause In `agent-runner-execution.ts` (lines 437-461), the `onToolResult` callback created an async task that called `onToolResult({ text, mediaUrls })` directly, bypassing the `blockReplyPipeline`. Regular block replies used `blockReplyPipeline.enqueue()` which ensures sequential delivery via a promise chain (`sendChain` in `block-reply-pipeline.ts`). ## Solution Modified the `onToolResult` callback to route tool result deliveries through the `BlockReplyPipeline` when available: 1. Create the tool result payload 2. If `blockReplyPipeline` exists and block streaming is enabled, call `blockReplyPipeline.enqueue(toolResultPayload)` 3. Otherwise, fall back to direct delivery for backward compatibility This ensures ALL messages (tool results + block replies) flow through the same sequential pipeline, preserving order. ## Code Changes **File:** `src/auto-reply/reply/agent-runner-execution.ts` - Lines 449-463: Added routing logic to use `blockReplyPipeline.enqueue()` when available - Created explicit `toolResultPayload` object - Added detailed comments explaining the rationale - Maintained fallback to direct delivery when pipeline is unavailable ## Why This Approach Is Correct **Follows existing patterns:** - Block replies already use `blockReplyPipeline.enqueue()` for ordering - This PR extends that pattern to tool results - Uses the same `sendChain` mechanism for sequential delivery **Maintains backward compatibility:** - Falls back to direct `onToolResult()` call when pipeline is unavailable - No changes to pipeline behavior itself - Only affects the routing of tool result messages **Minimal, localized change:** - Single file modified - ~12 lines added - No API surface changes - No impact on other message delivery paths **Solves the race condition:** - Both message types now use the same sequential delivery chain - `sendChain` in the pipeline ensures proper ordering via promise chaining - No more "race ahead" for short messages ## Design Trade-offs **Alternative 1: Always await onToolResult calls** - Considered but rejected: Would block the entire agent turn on each tool result - Could impact performance for tools with slow delivery - Our approach: Queue through pipeline for async delivery while maintaining order **Alternative 2: Add sequence numbers to messages** - Considered but rejected: Would require changes to all channel implementations - More complex, wider scope - Our approach: Reuse existing pipeline ordering mechanism **Alternative 3: Make onToolResult synchronous** - Considered but rejected: The async task tracking (lines 442-471) is intentional to avoid typing loops - Our approach: Keep the async pattern but route through ordered pipeline ## Testing Verification **Manual verification needed:** 1. Configure agent with tool that produces summaries 2. Trigger a tool use that produces both a tool result summary and a longer block reply 3. Observe message order on Telegram or other channel 4. **Before fix**: Short summary arrives after long reply 5. **After fix**: Messages arrive in correct order **Code review checks:** ✅ Follows existing `blockReplyPipeline.enqueue()` pattern ✅ Maintains backward compatibility (fallback when pipeline unavailable) ✅ No breaking changes to pipeline or message delivery APIs ✅ Clear comments document the reasoning ## Impact Summary **Fixes:** ✅ Out-of-order message delivery on channels ✅ Tool summaries now arrive in proper sequence ✅ Better user experience with coherent message flow **Maintains:** ✅ Backward compatibility (fallback path) ✅ Existing pipeline behavior unchanged ✅ Async task tracking for typing loop prevention ✅ Performance (no blocking, async delivery) **Changes:** - 1 file modified - 12 lines added, 2 lines changed - 0 breaking changes - 0 new dependencies 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- greptile_comment --> <h2>Greptile Overview</h2> <h3>Greptile Summary</h3> This change routes tool-result “summary” deliveries through the existing `BlockReplyPipeline` (when available) instead of firing them as detached async sends. The goal is to ensure tool-result summaries and longer block replies share the same promise-chain ordering mechanism, fixing out-of-order delivery on channels like Telegram. The update is localized to `src/auto-reply/reply/agent-runner-execution.ts`, adjusting the `onToolResult` callback so it enqueues a tool-result payload into the block-reply pipeline under the same conditions used for block replies, with a fallback to the previous direct delivery path when the pipeline is absent. <h3>Confidence Score: 3/5</h3> - This PR is close, but one ordering-critical await is missing and can undermine the intended fix. - The change is small and well-scoped, but not awaiting `blockReplyPipeline.enqueue()` means the tool-result delivery may still race with later pipeline operations and the internal `pendingToolTasks` tracking can complete too early. Fixing that should make the behavior match the PR’s stated goal. - src/auto-reply/reply/agent-runner-execution.ts <!-- 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)) - Context from `dashboard` - AGENTS.md ([source](https://app.greptile.com/review/custom-context?memory=0d0c8278-ef8e-4d6c-ab21-f5527e322f13)) <!-- /greptile_comment -->

Most Similar PRs