fix(site/src/pages/AgentsPage): stop gating chat stream parts on client status - #28207
Conversation
…nt status The chat stream handler dropped message_part events whenever the client-side chat status read "waiting". The server only forwards parts for the chat's current episode, so the gate was redundant with the server-side episode filtering. Its only effect was to convert client/server status skew into permanent output loss: dropped parts are never re-sent, and once the status caught up to "running" with no stream state, the UI showed a "Thinking" indicator that never progressed until the page was reloaded. The typical trigger was a reconnect replaying a stale waiting status from a just-completed turn, followed by a fast re-send whose first parts arrived before the new status event. Apply parts whenever they arrive. Interrupt semantics are preserved: status:waiting still discards parts buffered before it in the stream, and the durable message commit still clears applied stream state. 🤖 Generated with Coder Agents
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 79b492643b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ale waiting status Adds a page-level story for the scenario fixed in the previous commit: a chat record still reading "waiting" when a message_part arrives over the stream. The play function asserts the thinking disclosure renders, which fails against the pre-fix code and passes with it. The play asserts on the disclosure header rather than body text because the smoothing engine's requestAnimationFrame loop is suspended in the test iframe, so smoothed body text never reveals there.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4b56c112bc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…d waiting The ungated stream applied any arriving part, and a closed episode can keep draining parts to the client for up to 15s server-side. With a truly idle chat, those drain parts repopulated the stream and stayed visible until the next turn or a reload. Gate parts on a latch instead of on the status value alone: the latch sets when the stream delivers an authoritative "waiting" status and clears on any other stream status or error, and never moves on REST hydration. So a part is dropped only when the server itself said the turn ended, and still flows while a stale REST status lags a live turn.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 36aeb2c489
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…nstraint FE4: drop the scenario restatement from the story doc; the fixture itself is the code. Keep the one non-obvious constraint a future editor needs: why the assertion targets the disclosure header rather than streamed body text.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4c61ee0ce5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
An optimistic setChatStatus("running") on send flips the store status
before the stream's authoritative status event arrives. With the gate
keyed on chatStatus, that reopened the window while the latch was still
set, letting drain parts from the just-closed episode append to the new
turn. Key the gate on the latch alone: an optimistic write carries no
signal about episode liveness, and only a stream status event clears it.
|
@codex review |
|
Codex Review: Didn't find any major issues. Delightful! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Problem
When messaging an agent, the "Thinking" indicator would sometimes appear and never progress to a visible thinking block, even though the agent was working. Reloading the page fixed it.
The chat stream handler dropped
message_partevents whenever the client-side chat status readwaiting. The server only forwards parts for the chat's current episode (filtered by history version and generation attempt instreamLoop.part), so the gate was redundant. Its only effect was to convert client/server status skew into permanent output loss: dropped parts are never re-sent. Once the status caught up torunningwith no stream state accumulated, the UI showed the "Thinking" indicator indefinitely.A typical trigger: the WebSocket reconnects and replays a stale
waitingstatus from a just-completed turn, the user re-sends, and the new turn's first parts arrive before the newrunningstatus event.Fix
Apply parts whenever they arrive, and let the server's episode filtering stand as the single source of truth for staleness. Interrupt semantics are preserved:
status:waitingstill discards parts buffered before it in the same ordered stream, and the durable message commit still clears applied stream state.The existing test asserting the gate's behavior is inverted to assert parts are now applied under a stale
waitingstatus, so it fails if the gate is ever reintroduced.Generated with Coder Agents