The AIChatAgent class provides automatic resumable streaming out of the box. When a client disconnects and reconnects during an active stream, the response automatically resumes from where it left off.
When you use AIChatAgent with useAgentChat:
- During streaming: All chunks are automatically persisted to SQLite
- On disconnect: The stream continues server-side, buffering chunks
- On reconnect: Client requests a resume, receives all buffered chunks, and continues streaming
No extra code is needed -- it just works.
import { AIChatAgent } from "@cloudflare/ai-chat";
import { createWorkersAI } from "workers-ai-provider";
import { streamText, convertToModelMessages } from "ai";
export class ChatAgent extends AIChatAgent {
async onChatMessage() {
const workersai = createWorkersAI({ binding: this.env.AI });
const result = streamText({
model: workersai("@cf/moonshotai/kimi-k2.6"),
messages: await convertToModelMessages(this.messages)
});
// Automatic resumable streaming - no extra code needed
return result.toUIMessageStreamResponse();
}
}import { useAgent } from "agents/react";
import { useAgentChat } from "@cloudflare/ai-chat/react";
function Chat() {
const agent = useAgent({
agent: "ChatAgent",
name: "my-chat"
});
const { messages, sendMessage, status } = useAgentChat({
agent
// resume: true is the default - streams automatically resume on reconnect
});
// ... render your chat UI
}- Creates SQLite tables for stream chunks and metadata on construction
- Each stream gets a unique ID and tracks chunk indices
- Chunks are batched (every 10 chunks) and flushed to SQLite for performance
- When a client sends
CF_AGENT_STREAM_RESUME_REQUEST, the server checks for active streams and responds withCF_AGENT_STREAM_RESUMING - Stale streams (older than 5 minutes) are cleaned up on restore
- Completed streams older than 24 hours are periodically garbage collected
- After the message handler is registered in
useEffect, sendsCF_AGENT_STREAM_RESUME_REQUESTto the server - This avoids a race condition where the server's
onConnectnotification could arrive before the client's handler is ready - On receiving
CF_AGENT_STREAM_RESUMING, sendsCF_AGENT_STREAM_RESUME_ACK - Receives all buffered chunks with
replay: trueflag and applies them in a single batch - Continues receiving live chunks as they arrive from the ongoing stream
Replayed chunks include replay: true to distinguish them from live chunks. The client uses this to batch-apply all replayed chunks before rendering, which prevents intermediate states (like reasoning "Thinking..." indicators) from flashing briefly during replay. During a live stream, chunks arrive gradually and React renders each intermediate state naturally.
If you do not want automatic resume (for example, for short responses), disable it:
const { messages } = useAgentChat({
agent,
resume: false // Disable automatic stream resumption
});See examples/resumable-stream-chat for a complete working example. Start a long response, refresh the page mid-stream, and watch it resume automatically.
- Chat Agents — Full
AIChatAgentanduseAgentChatreference - Client Tools Continuation — Client-side tool execution and auto-continuation