You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
useAgentChat: cf_agent_chat_messages handler replaces the message array wholesale, dropping client-buffered sends on reconnect #1983
useAgentChat's transport applies the server's authoritative transcript frame (cf_agent_chat_messages) with a whole-array setMessages(next). On an idle connect/reconnect this replaces the local list with the server snapshot, dropping any optimistic message the client just added via sendMessage() that the snapshot doesn't yet contain.
Concretely: the user sends a message while the socket is reconnecting. PartySocket buffers the frame (so the turn is reliably delivered) and sendMessage renders the message optimistically — but the reconnect snapshot doesn't include it yet, so setMessages(next) erases it from the UI. It reappears only after the turn completes and the server rebroadcasts. So it's not data loss; it's a UI-consistency issue — the SDK optimistically shows a send it has committed to delivering, then its own replay wipes it mid-flight.
To Reproduce
Render a chat with existing messages via useAgentChat.
Drop the network (toggle Wi‑Fi off), then let the socket reconnect.
During the reconnect window (socket not yet OPEN), call sendMessage(...). The message renders optimistically; PartySocket buffers the frame.
On reconnect, the agent's idle-connect flow pushes the transcript as cf_agent_chat_messages (without the just-sent message).
setMessages(next) runs → the optimistic message disappears (the user sees the reply but not their own message), returning only after the turn ends.
Expected behavior
The reconnect transcript replay shouldn't drop an in-flight optimistic message the client just sent. Merge the snapshot with local state (preserve trailing local-only messages it doesn't include) instead of replacing wholesale — or expose a reconcile hook so hosts can merge it themselves.
Screenshots
The sent message is visible, disappears during reconnect, then reappears after the turn.
Version:
agents@0.18.0 (also reproduces on 0.17.3). Handler in agents/dist/chat/react.js, re-exported unchanged by @cloudflare/think.
Additional context
Location: useAgentChat → onAgentMessage, case "cf_agent_chat_messages":
The frame is pushed on every idle connect/reconnect (e.g. @cloudflare/think's _buildIdleConnectMessages when no stream is active; AIChatAgent has an equivalent replay).
Not data loss: the send is buffered and flushed on reconnect, so the turn runs and persists. Visible recovery depends on the server rebroadcasting the transcript to the sender after the turn; if the sender is excluded, it stays gone until reload.
Host-side workaround we use: merge trailing local-only role: "user" messages instead of replacing (scoped to user so a legitimate assistant replacement like regenerate isn't resurrected).
Activity
fix(agents): preserve optimistic send dropped on reconnect (cloudflar…
refactor(fix(agents)): detect buffered sends via transport, restore t…
fix(agents,think): keep buffered sends only across a marked connect t…
fix(agents): preserve optimistic send dropped on reconnect (#1983) (#…
fix(agui): port upstream transport fixes to the AG-UI WebSocket trans…