← Back to PRs

#15920: fix: await coalescer flush before tool execution

by Bridgerz open 2026-02-14 02:17 View on GitHub →
agents stale size: M
## Summary - Await `onBlockReplyFlush` in `handleToolExecutionStart` instead of fire-and-forget, so the pipeline flush (including `sendChain` delivery) completes before tool events are emitted - Serialize event processing through a promise chain when a flush callback is provided, blocking subsequent `message_update` events until the flush completes - Add tests verifying async flush is awaited and that flush rejections don't break the handler chain ## Problem When the AI outputs short text (e.g., "On it."), then executes a tool, then outputs more text ("Done."), the user sees both messages simultaneously instead of getting the acknowledgment before the tool runs. Root cause: `void ctx.params.onBlockReplyFlush()` was fire-and-forget, allowing subsequent events to process before delivery completed. ## Test plan - [x] Existing flush tests pass (updated to handle async chain) - [x] New test: async flush blocks subsequent events until resolved - [x] New test: rejected flush doesn't break the handler chain - [x] All 67 pi-embedded-subscribe e2e tests pass (only pre-existing `https-proxy-agent` dep failure) 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- greptile_comment --> <h3>Greptile Summary</h3> Fixes race condition where short acknowledgment messages would appear simultaneously with post-tool text instead of being delivered sequentially. Changes `onBlockReplyFlush` from fire-and-forget to awaited in `handleToolExecutionStart`, and introduces promise chain serialization for event processing when a flush callback is provided. The chain blocks subsequent `message_update` events until the flush completes, preserving message boundaries. Includes comprehensive test coverage for async flush blocking, rejection handling, and buffer flushing. <h3>Confidence Score: 5/5</h3> - This PR is safe to merge with minimal risk - The fix addresses a well-defined race condition with a clean implementation. The dual-mode execution path (direct vs chain) preserves existing fast-path behavior while adding serialization only when needed. Error handling is defensive (try-catch around flush, catch on promise chain). Test coverage is comprehensive with 3 new tests covering async blocking, rejection handling, and buffer flushing, plus updates to existing tests for async compatibility. The only documentation issue (pendingCount decrement location) was already flagged in previous threads. - No files require special attention <sub>Last reviewed commit: 60e7ef9</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs