Repository navigation
fix(ai-chat): send the done frame after the transcript is persisted - #2334
Conversation
🦋 Changeset detectedLatest commit: 39dff48 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
⚪ agents import sizesMeasured 343 runtime imports as minified bundles. The primary size is gzip; raw minified size is included for diagnosis. An existing import growing by more than 10% is marked red. This report is informational.
Compared No import sizes changed. All 343 current runtime imports
Reported by agent-think[bot]. |
| this._broadcastChatMessage( | ||
| errorText !== undefined && | ||
| message.type === MessageType.CF_AGENT_USE_CHAT_RESPONSE && | ||
| !message.error | ||
| ? { ...message, body: errorText, error: true } | ||
| : message, | ||
| exclude |
There was a problem hiding this comment.
Yes, intended: an existing stream error is more specific than the persistence failure, so it is kept.
| this._heldTerminalFrames.set(id, null); | ||
| let persisted = false; |
There was a problem hiding this comment.
Agreed, no change needed.
agents
@cloudflare/ai-chat
@cloudflare/codemode
hono-agents
@cloudflare/shell
@cloudflare/think
@cloudflare/voice
@cloudflare/worker-bundler
commit: |
useAgentChat switches to ready on done, but AIChatAgent sent it before persisting the reply and broadcasting cf_agent_chat_messages, so on programmatic turns and in other tabs a message sent right after the reply could be replaced by the late snapshot. While _reply owns a request its terminal frame is held in _broadcastChatMessage and released after the persist; a failed persist releases it as an error frame. Same ordering fix as #2327 for Think. Co-authored-by: Cursor <cursoragent@cursor.com>
20f220d to
a56a8d7
Compare
… persisted An SSE error chunk is broadcast as error: true, done: false before the done frame, and clients end the stream on either. Hold every terminal frame for the request, in order, and release them after the persist. Co-authored-by: Cursor <cursoragent@cursor.com>
## pnpm-workspace.yaml (default) ## Dependency Updates | Package | From | To | Type | | --- | --- | --- | --- | | `@cloudflare/ai-chat` | 0.12.0 | 0.12.1 | patch | ## Release Notes <details> <summary><b>@<!---->cloudflare/ai-chat</b> (0.12.0 → 0.12.1)</summary> ### Patch Changes - [#2390](cloudflare/agents#2390) [`c55ec80`](cloudflare/agents@c55ec80) Thanks [@<!---->threepointone](https://github.com/threepointone)! - Report a failed agent-tool child as failed even when it was evicted before recording the failure. See [Agent tools](https://github.com/cloudflare/agents/blob/main/docs/agents/agent-tools.md). - [#2384](cloudflare/agents#2384) [`f904999`](cloudflare/agents@f904999) Thanks [@<!---->threepointone](https://github.com/threepointone)! - Fix agent-tool chunks being duplicated or dropped on reconnect, child re-attach, and fiber recovery. See [Agent tools](https://github.com/cloudflare/agents/blob/main/docs/agents/agent-tools.md). - [#2364](cloudflare/agents#2364) [`5e0507e`](cloudflare/agents@5e0507e) Thanks [@<!---->threepointone](https://github.com/threepointone)! - Add `eventDelivery: "terminal"` to `runAgentTool` to forward only lifecycle, progress, and milestone events for a run. See [Agent tools](https://github.com/cloudflare/agents/blob/main/docs/agents/agent-tools.md). - [#2334](cloudflare/agents#2334) [`7f564e7`](cloudflare/agents@7f564e7) Thanks [@<!---->threepointone](https://github.com/threepointone)! - AI Chat and Think send the terminal `done` frame after persisting and broadcasting the assistant reply, so later sends are not overwritten. See [Chat agents](https://github.com/cloudflare/agents/blob/main/docs/agents/chat-agents.md). - [#2352](cloudflare/agents#2352) [`449ac27`](cloudflare/agents@449ac27) Thanks [@<!---->threepointone](https://github.com/threepointone)! - Do not start an automatic continuation after an …[full notes](https://github.com/cloudflare/agents/releases/tag/%40cloudflare/ai-chat%400.12.1) </details> --- *This PR was auto-generated by [catalog-update-action](https://github.com/brandhaug/catalog-update-action).* Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
…ge ids and per-event seq Ports the server half of upstream cloudflare#2334, cloudflare#2365, cloudflare#2378, cloudflare#2383 and cloudflare#2348 into AGUIChatAgent. An in-band RUN_ERROR now marks its stream errored when the body ends (a settled stream cannot be re-marked, which had broken cloudflare#1575's errored replay) and rides an error frame the client transport still delivers as an event.
Problem
useAgentChatswitches toreadywhen it receives the terminaldoneframe, and a latercf_agent_chat_messagessnapshot replaces the client's message list.AIChatAgentsentdonefrom its stream helpers, before_replypersisted the assistant message and broadcast the transcript. A message the user sent right after the reply finished could therefore be wiped by the late snapshot until the next turn.This is the same ordering bug Think had in #2119 (fixed in #2327), but narrower. On a normal WebSocket turn, the sending connection is excluded from its own transcript broadcast, so it is not affected. These cases are affected:
saveMessages(), scheduled work, continuations), whose transcript goes to every connection.Fix
_broadcastChatMessageholds the terminaldoneframe for a request while_replyowns it, and_replyreleases it after the persist.donefor a reply that was not saved.design/chat-shared-layer.mdrecords the ordering rule.Validation:
terminal-frame-order.test.tscovers the transcript arriving beforedonefor SSE and plain-text replies seen from another tab, for a stream that throws mid-reply (the partial arrives before the error frame), and for a programmaticsaveMessages()turn, plus the error frame when persisting fails. All five fail without the fix.ai-chatsuite (740 tests) andpnpm run checkpassed.Follow-up to #2119. Independent of the Think stack in #2322 through #2331.