Repository navigation
fix(assistant): trailing-edge flush for throttled message updates - #221
Merged
Merged
Conversation
The 200 ms AssistantMessageUpdated throttle was leading-edge only: an update suppressed inside the window was never re-emitted, so a second parallel tool call persisted a few ms after the first stayed invisible until some later update, possibly minutes away. Replace the three copied throttle blocks (Claude, Codex, OpenCode) with one UpdateThrottle that holds the latest suppressed update and reports when it is due. Each read loop (Claude, Codex exec, Codex app-server, OpenCode) races its next read against that deadline and emits the held message on expiry. Error exits from a loop emit the held update; message finalization/rotation/steer-split drop it, since AssistantMessageCompleted carries the full content and the held message belongs to the old id.
… error exits A held AssistantMessageUpdated emitted after an AssistantDelta for the same message made the frontend drop its delta accumulator, hiding streamed text. Route every delta through emit_assistant_delta, which flushes the held update first. Also emit the held update on cancel-arm, child.wait and finalize failures, but not after a message is finalized.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When an agent makes parallel tool calls, both run, but the chat shows only the first one. The second appears only when the next batch of tool calls or the end of the turn arrives.
Root cause: the 200 ms throttle on
assistant_message_updated(Claude, Codex exec, Codex app-server and OpenCode paths inlocal_agent.rs) was leading-edge only. An update suppressed inside the window was never re-sent. The database row always had every tool call; only the live UI event was lost. Codex emits parallel tool calls 3–53 ms apart, and Claude'sadopt_complete_assistant_messagepersists several tool calls in a loop, so both paths hit it.Fix
UpdateThrottle<T>replaces three copy-pasted throttles. It emits immediately, or holds the latest update and releases it when the window ends.select!, so a held update reaches the UI within about 200 ms even while a tool runs for minutes. The rate stays capped at 5 updates per second.AssistantDelta(emit_assistant_delta/delta_events). Otherwise a late snapshot would wipe text that had already streamed, becauseupdateMessageContentresets the delta accumulator.child.wait(), and the finalize failures in cancel, loop-end, rotate and split all send the held update first.AssistantMessageCompletedalready carries the full content. Nothing is emitted after completion or under a stale message id.Tests
update_throttle*: covers holding, latest-wins, window restart, and two tool calls 3 ms apart where the last emit contains both.sleep_until_due: checked with paused tokio time.delta_events_emit_held_update_before_delta: checks the ordering.offerback to the old drop-on-hold behaviour fails 4 of the 5 throttle tests.No loop-level test exists, because the repo has no Tauri mock app.
Review
f37f05981.Manual check
With a Codex- or Claude-backed agent, ask for two parallel tool calls, such as two
workspace_assignTaskcalls. Both should appear in the chat immediately.