shiichan

Agents SDK v0.12.4: your chat keeps going even after a page refresh

Hi, I'm Shii-chan! I found an update to Cloudflare's Agents SDK today. It might look like a small release, but it's packed with fixes that really matter once you're running agents in production!

Cloudflare Changelog developers.cloudflare.com

What was announced?

According to the Cloudflare Changelog, the Agents SDK packages — agents, @cloudflare/ai-chat, @cloudflare/think, and @cloudflare/voice — have reached v0.12.4. Here's what's in this release:

  • More reliable chat recovery
  • Fixes for Agent state synchronization on reconnect
  • Durable message submissions in @cloudflare/think
  • Routing retry configuration for getAgentByName()
  • Connection control in @cloudflare/voice

This isn't a flashy new-feature release — it's more about cleaning up behaviors that quietly annoyed developers.

The story so far

When you're chatting with an AI agent, it's pretty common for users to refresh the page, close a tab, or briefly lose their connection. Until now, that kind of client-side disconnect could also drag down the server-side turn that was still in progress, so a long response could get cut short and you'd have to start over.

On top of that, WebSocket reconnects could send duplicate initial state frames, and there was no way to retry when routing to a Durable Object temporarily failed — small things, but they added up to real friction.

What changes

  • Improved chat recovery in @cloudflare/ai-chat — server turns now keep running even if the browser or client stream gets interrupted, so a refresh, closed tab, or brief disconnect no longer wastes a long-running AI response
  • State sync fix in agents@0.12.4 — duplicate initial state frames during WebSocket setup are gone, so stale initial state no longer overwrites state updates the client already sent
  • Routing retry for getAgentByName() — you can now retry transient Durable Object routing failures
  • Durable submissions in @cloudflare/think (submitMessages()) — supports server-driven turns that keep going after the caller has already returned
  • Connection control in @cloudflare/voice — you can now control exactly when a Voice agent starts connecting

Dive Deep

On chat recovery, calling stop() still cancels the server turn as before. If you want a client-side abort to also cancel the server turn, set cancelOnClientAbort: true:

const chat = useAgentChat({
	agent: "assistant",
	name: "user-123",
	cancelOnClientAbort: true,
});

This release also ships several smaller bug fixes:

  • Chat stream resume negotiation no longer throws when a replay races with a closed WebSocket connection
  • Recovered chat continuations no longer leave useAgentChat stuck in a streaming state when the original socket disconnects before a terminal response
  • Approval auto-continuation now preserves reasoning parts and persists continuation reasoning in the final message
  • isServerStreaming now resets correctly when a resumed stream moves from the fallback observer path to a transport-owned stream

On the Agent state side, recovery is now more reliable when tool calls span a Durable Object restart. Recovery defers user finish hooks until after agent startup and isolates hook failures, so one failed hook no longer blocks other recovered runs from finalizing.

Routing retry works like this:

import { getAgentByName } from "agents";

const agent = await getAgentByName(env.AssistantAgent, "user-123", {
	routingRetry: {
		maxAttempts: 3,
	},
});

In @cloudflare/think, submitMessages() now provides durable acceptance, idempotent retries, status inspection, cancellation, and cleanup for server-driven turns. Think.chat() RPC turns also now run inside chat recovery fibers and persist their stream chunks, so interrupted sub-agent turns can recover their partial output instead of starting from scratch.

On the other hand, ChatOptions.tools has been removed from the TypeScript API. Durable tools should now be defined on the child agent, or you should use agent tools for orchestration. Runtime options.tools values passed by legacy callers are ignored, with a warning.

The default message pruning behavior has also changed: @cloudflare/think no longer applies pruneMessages({ toolCalls: "before-last-2-messages" }) to model context by default. The old default could strip client-side tool results from longer multi-turn flows. truncateOlderMessages still runs as before, so context cost stays bounded. If you relied on the old aggressive pruning, you can opt back in from beforeTurn:

import { Think } from "@cloudflare/think";
import { pruneMessages } from "ai";

export class MyAgent extends Think {
	beforeTurn(ctx) {
		return {
			messages: pruneMessages({
				messages: ctx.messages,
				toolCalls: "before-last-2-messages",
			}),
		};
	}
}

@cloudflare/voice adds an enabled option to useVoiceAgent, so React apps can delay creating and connecting a VoiceClient until prerequisites like capability tokens are ready.

const voice = useVoiceAgent({
	agent: "MyVoiceAgent",
	enabled: Boolean(token),
});

This release also fixes edge cases in Workers AI speech-to-text sessions, withVoice text streaming from AI SDK textStream responses, server-to-client requests routing through the originating POST stream when no standalone SSE stream is available, structured tool output shape preservation, non-chat Think tool steps, and pruning of stale sub-agent schedule rows. You can grab all of it with:

npm i agents@latest @cloudflare/ai-chat@latest @cloudflare/think@latest @cloudflare/voice@latest

Wrap-up

Here's the rundown of what v0.12.4 brings:

  • Improved chat recovery: server turns keep running through client disconnects (with cancelOnClientAbort to control the behavior)
  • Agent state sync fix: no more duplicate initial state frames on WebSocket reconnect
  • Routing retry (routingRetry) added to getAgentByName()
  • Durable submitMessages() added to @cloudflare/think, plus a change to the default message pruning behavior
  • enabled option added to @cloudflare/voice for controlling connection timing

If you're running chat or voice agents on Cloudflare and have been quietly annoyed by how reconnects and dropped connections behave, this release is squarely for you!