The first minor release of @cloudflare/ai-chat — a major step up from the agents/ai-chat-agent re-export. This release refactors the internals (extracting ResumableStream, adding a WebSocket ChatTransport, simplifying SSE parsing) and ships a wave of bug fixes for streaming, tool continuations, and message persistence. New features include maxPersistedMessages for storage caps, body for custom request data, row size protection, incremental persistence, and data parts — typed JSON blobs that can be attached to messages alongside text for citations, progress indicators, and usage metadata. Tool approval (needsApproval) now persists across page refresh, client tools survive DO hibernation, and autoContinueAfterToolResult defaults to true so the LLM responds after tool results without explicit opt-in.
Minor Changes
-
#899
04c6411Thanks @threepointone! - Refactor AIChatAgent: extract ResumableStream class, add WebSocket ChatTransport, simplify SSE parsing.Bug fixes:
- Fix
setMessagesfunctional updater sending empty array to server - Fix
_sendPlaintextReplycreating multiple text parts instead of one - Fix uncaught exception on empty/invalid request body
- Fix
CF_AGENT_MESSAGE_UPDATEDnot broadcast for streaming messages - Fix stream resumption race condition (client-initiated resume request + replay flag)
- Fix
_streamCompletionPromisenot resolved on error (tool continuations could hang) - Fix
bodylost during tool continuations (now preserved alongsideclientTools) - Fix
clearAll()not clearing in-memory chunk buffer (orphaned chunks could flush after clear) - Fix errored streams never cleaned up by garbage collector
- Fix
reasoning-deltasilently dropping data whenreasoning-startwas missed (stream resumption) - Fix row size guard using
string.lengthinstead of UTF-8 byte count for SQLite limits - Fix
completedguard on abort listener to prevent redundant cancel after stream completion
New features:
maxPersistedMessages— cap SQLite message storage with automatic oldest-message deletionbodyoption onuseAgentChat— send custom data with every request (static or dynamic)- Incremental persistence with hash-based cache to skip redundant SQL writes
- Row size guard — automatic two-pass compaction when messages approach SQLite 2MB limit
onFinishis now optional — framework handles abort controller cleanup and observability- Stream chunk size guard in ResumableStream (skip oversized chunks for replay)
- Full tool streaming lifecycle in message-builder (tool-input-start/delta/error, tool-output-error)
Docs:
- New
docs/chat-agents.md— comprehensive AIChatAgent and useAgentChat reference - Rewritten README, migration guides, human-in-the-loop, resumable streaming, client tools docs
- New
examples/ai-chat/example with modern patterns and Workers AI
Deprecations (with console.warn):
createToolsFromClientSchemas(),extractClientToolSchemas(),detectToolsRequiringConfirmation()tools,toolsRequiringConfirmation,experimental_automaticToolResolutionoptionsaddToolResult()(useaddToolOutput())
- Fix
-
#919
6b6497cThanks @threepointone! - ChangeautoContinueAfterToolResultdefault fromfalsetotrue.Client-side tool results and tool approvals now automatically trigger a server continuation by default, matching the behavior of server-executed tools (which auto-continue via
streamText's multi-step). This eliminates the most common setup friction with client tools — the LLM now responds after receiving tool results without requiring explicit opt-in.To restore the previous behavior, set
autoContinueAfterToolResult: falseinuseAgentChat.
Patch Changes
-
#900
16b2dcaThanks @deathbyknowledge! - Add support fordata-*stream parts (developer-defined typed JSON blobs) in the shared message builder and client hook.Data part handling:
applyChunkToPartsnow handlesdata-*prefixed chunk types, covering both server persistence and client reconstruction (stream resume, cross-tab broadcast). Transient parts (transient: true) are broadcast to connected clients but excluded frommessage.partsand SQLite persistence. Non-transient parts support reconciliation by type+id — a second chunk with the same type and id updates the existing part's data in-place instead of appending a duplicate.onDatacallback forwarding:useAgentChatnow invokes theonDatacallback fordata-*chunks on the stream resumption and cross-tab broadcast codepaths, which bypass the AI SDK's internal pipeline. For new messages sent via the transport, the AI SDK already invokesonDatainternally. This is the correct way to consume transient data parts on the client since they are not added tomessage.parts. -
#922
c8e5244Thanks @threepointone! - Fix tool approval UI not surviving page refresh, and fix invalid prompt error after approval- Handle
tool-approval-requestandtool-output-deniedstream chunks in the server-side message builder. Previously these were only handled client-side, so the server never transitioned tool parts toapproval-requestedoroutput-deniedstate. - Persist the streaming message to SQLite (without broadcasting) when a tool enters
approval-requestedstate. The stream is paused waiting for user approval, so this is a natural persistence point. Without this, refreshing the page would reload from SQLite where the tool was still ininput-availablestate, showing "Running..." instead of the Approve/Reject UI. - On stream completion, update the early-persisted message in place rather than appending a duplicate.
- Fix
_applyToolApprovalto merge with existing approval data instead of replacing it. Previouslyapproval: { approved }would overwrite the entire object, losing theidfield thatconvertToModelMessagesneeds to produce a validtool-approval-requestcontent part. This caused anInvalidPromptErroron the continuation stream after approval.
- Handle
-
#897
994a808Thanks @alexanderjacobsen! - Fix client tool schemas lost after DO restart by re-sending them with CF_AGENT_TOOL_RESULT -
#916
24e16e0Thanks @threepointone! - Widen peer dependency ranges across packages to prevent cascading major bumps during 0.x minor releases. Mark@cloudflare/ai-chatand@cloudflare/codemodeas optional peer dependencies ofagentsto fix unmet peer dependency warnings during installation. -
#912
baa87ccThanks @threepointone! - Persist request context across Durable Object hibernation.Persist
_lastBodyand_lastClientToolsto SQLite so custom body fields and client tool schemas survive Durable Object hibernation during tool continuation flows (issue #887). Add test coverage for body forwarding during tool auto-continuation, and update JSDoc forOnChatMessageOptions.bodyto document tool continuation and hibernation behavior. -
#913
bc91c9aThanks @threepointone! - Sync_lastClientToolscache and SQLite when client tools arrive viaCF_AGENT_TOOL_RESULT, and align the wire type withClientToolSchema(JSONSchema7instead ofRecord<string, unknown>) -
#919
6b6497cThanks @threepointone! - Add auto-continuation support for tool approval (needsApproval).When a tool with
needsApproval: trueis approved viaCF_AGENT_TOOL_APPROVAL, the server can now automatically continue the conversation (matching the existingautoContinuebehavior ofCF_AGENT_TOOL_RESULT). The client hook passesautoContinuewith approval messages whenautoContinueAfterToolResultis enabled. Also fixes silent data loss wheretool-output-availableevents for tool calls in previous assistant messages were dropped during continuation streams by adding a cross-message fallback search in_streamSSEReply. -
#910
a668155Thanks @threepointone! - Add structural message validation and fix message metadata on broadcast/resume path.Structural message validation:
Messages loaded from SQLite are now validated for required structure (non-empty
idstring, validrole,partsis an array). Malformed rows — from corruption, manual tampering, or schema drift — are logged with a warning and silently skipped instead of crashing the agent. This is intentionally lenient: emptypartsarrays are allowed (streams that errored mid-flight), and no tool/data schema validation is performed at load time (that remains a userland concern viasafeValidateUIMessagesfrom the AI SDK).Message metadata on broadcast/resume path:
The server already captures
messageMetadatafromstart,finish, andmessage-metadatastream chunks and persists it onmessage.metadata. However, the client-side broadcast path (multi-tab sync) and stream resume path (reconnection) did not propagate metadata — theactiveStreamRefonly trackedparts. Now it also tracksmetadata, andflushActiveStreamToMessagesincludes it in the partial message flushed to React state. This means cross-tab clients and reconnecting clients see metadata (model info, token usage, timestamps) during streaming, not just after the finalCF_AGENT_CHAT_MESSAGESbroadcast.