agentHost: retire the optimistic turn start on the first backend action - #332122
Open
Ryan Ewen (RyanEwen) wants to merge 1 commit into
Open
agentHost: retire the optimistic turn start on the first backend action#332122Ryan Ewen (RyanEwen) wants to merge 1 commit into
Ryan Ewen (RyanEwen) wants to merge 1 commit into
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes delayed agent-host response rendering by retiring stale optimistic turn starts as soon as the backend acknowledges the turn.
Changes:
- Promotes pending turn starts on the first backend action containing the matching turn ID.
- Adds regression coverage for streamed markdown visibility.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
agentSubscription.ts |
Updates optimistic turn reconciliation. |
agentSubscription.test.ts |
Tests incremental response-part rendering. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
The client holds its own chat/turnStarted in _pendingActions until the backend echoes it with the originating clientSeq, which the agent host does not send. The start stays pending for the whole turn, and _recomputeOptimistic replays it over confirmed state on every action, resetting activeTurn to a fresh empty turn. The UI reads the optimistic state, so a streaming response renders nothing until completion moves the turn into turns[] and the whole reply appears at once. Promote the pending start on the first backend action naming the turn rather than only on a terminal one.
Ryan Ewen (RyanEwen)
force-pushed
the
fix/optimistic-turn-start-masks-streaming
branch
from
August 28, 2026 14:05
20d9914 to
d256017
Compare
This was referenced Aug 28, 2026
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.
Fixes #332087
Problem
ChatStateSubscriptionapplies the client's ownchat/turnStartedoptimistically and holds it in_pendingActionsuntil the backend echoes it back carrying the originatingclientSeq. The agent host does not send thatclientSeq, so the reconcile takes the non-origin branch and the pending start is never retired._recomputeOptimistic()rebuilds the optimistic state on every confirmed action by replaying the pending list over confirmed state:Replaying a turn start resets
activeTurnto a fresh empty turn, soactiveTurn.responsePartsreads as empty no matter how many deltas have already been folded into confirmed state. The UI reads the optimistic state, so_observeTurnresolves the turn by id but sees no parts,autorunPerKeyedItemnever creates a per-part observer, and_setupMarkdownPartis never called. On completion the turn moves intostate.turns, which a replayed turn start does not touch, and the whole reply appears in a single step.Measured in a live window with the renderer instrumented, on a prompt producing about 2.4 KB of markdown and no tool calls: 17
chat/deltaenvelopes arrived between 8.0s and 19.4s and all 17 passed the channel filter, confirmedactiveTurn.responseParts.lengthwas 1 from 8.0s onward, and the optimistic rebase rewrote it to 0 on all 19 recomputes. The markdown observer was created at 19.4s and emitted all 2,432 characters at once._promotePendingTurnStartIfTerminalalready exists because the echo can arrive without theclientSeq, but it fires only forchat/turnComplete,chat/turnCancelledandchat/error. That is why the text lands all at once at the end: the promotion happens, just not until the turn is over.Change
Promote the pending turn start on the first backend action naming the turn instead of only on a terminal one, and rename the method to match.
Once any backend action for a turn arrives, the server has that turn, so the optimistic start is redundant and holding it can only mask confirmed state. The body is otherwise unchanged: the start is still applied to confirmed state when the backend has not created the turn itself, so an action that arrives before any server-side
chat/turnStartedstill has a turn to land on.Nothing changes on the origin branch, where the echo does carry the
clientSeqand the pending action is spliced as before, and nothing changes for a chat action without aturnId.How to test
The text appears incrementally as it is generated. On
mainthe response area stays empty for the whole turn and the entire reply appears at once when the turn completes. Same prompt and build, before and after: 2 DOM updates becomes 13, the markdown observer is created at 7.8s instead of 19.4s, and the renderer emits 16 times across 12 seconds instead of once.Unit coverage adds one test asserting that a
chat/responsePartandchat/deltaarriving while the optimistic turn start is still pending are visible in the subscription value. It fails onmainand passes with this change.AI disclosure: this pull request and the related code were written with the assistance of AI.