The latest release of the Agents SDK exposes agent state as a readable property, prevents duplicate schedule rows across Durable Object restarts, brings full TypeScript inference to AgentClient, and migrates to Zod 4.
Readable state on useAgent and AgentClient
Both useAgent (React) and AgentClient (vanilla JS) now expose a state property that reflects the current agent state. Previously, reading state required manually tracking it through the onStateUpdate callback.
React (useAgent)
JavaScript
const agent = useAgent({agent: "game-agent",name: "room-123",});// Read state directly — no separate useState + onStateUpdate neededreturn <div>Score: {agent.state?.score}</div>;// Spread for partial updatesagent.setState({ ...agent.state, score: (agent.state?.score ?? 0) + 10 });TypeScript
const agent = useAgent<GameAgent, GameState>({agent: "game-agent",name: "room-123",});// Read state directly — no separate useState + onStateUpdate neededreturn <div>Score: {agent.state?.score}</div>;// Spread for partial updatesagent.setState({ ...agent.state, score: (agent.state?.score ?? 0) + 10 });
agent.state is reactive — the component re-renders when state changes from either the server or a client-side setState() call.
Vanilla JS (AgentClient)
JavaScript
const client = new AgentClient({agent: "game-agent",name: "room-123",host: "your-worker.workers.dev",});client.setState({ score: 100 });console.log(client.state); // { score: 100 }TypeScript
const client = new AgentClient<GameAgent>({agent: "game-agent",name: "room-123",host: "your-worker.workers.dev",});client.setState({ score: 100 });console.log(client.state); // { score: 100 }
State starts as undefined and is populated when the server sends the initial state on connect (from initialState) or when setState() is called. Use optional chaining (agent.state?.field) for safe access. The onStateUpdate callback continues to work as before — the new state property is additive.
Idempotent schedule()
schedule() now supports an idempotent option that deduplicates by (type, callback, payload), preventing duplicate rows from accumulating when called in places that run on every Durable Object restart such as onStart().
Cron schedules are idempotent by default. Calling schedule("0 * * * *", "tick") multiple times with the same callback, expression, and payload returns the existing schedule row instead of creating a new one. Pass { idempotent: false } to override.
Delayed and date-scheduled types support opt-in idempotency:
JavaScript
import { Agent } from "agents";class MyAgent extends Agent {async onStart() {// Safe across restarts — only one row is createdawait this.schedule(60, "maintenance", undefined, { idempotent: true });}}TypeScript
import { Agent } from "agents";class MyAgent extends Agent {async onStart() {// Safe across restarts — only one row is createdawait this.schedule(60, "maintenance", undefined, { idempotent: true });}}
Two new warnings help catch common foot-guns:
- Calling
schedule()insideonStart()without{ idempotent: true }emits aconsole.warnwith actionable guidance (once per callback; skipped for cron and whenidempotentis set explicitly). - If an alarm cycle processes 10 or more stale one-shot rows for the same callback, the SDK emits a
console.warnand aschedule:duplicate_warningdiagnostics channel event.
Typed AgentClient with call inference and stub proxy
AgentClient now accepts an optional agent type parameter for full type inference on RPC calls, matching the typed experience already available with useAgent.
JavaScript
const client = new AgentClient({agent: "my-agent",host: window.location.host,});// Typed call — method name autocompletes, args and return type inferredconst value = await client.call("getValue");// Typed stub — direct RPC-style proxyawait client.stub.getValue();await client.stub.add(1, 2);TypeScript
const client = new AgentClient<MyAgent>({agent: "my-agent",host: window.location.host,});// Typed call — method name autocompletes, args and return type inferredconst value = await client.call("getValue");// Typed stub — direct RPC-style proxyawait client.stub.getValue();await client.stub.add(1, 2);
State is automatically inferred from the agent type, so onStateUpdate is also typed:
JavaScript
const client = new AgentClient({agent: "my-agent",host: window.location.host,onStateUpdate: (state) => {// state is typed as MyAgent's state type},});TypeScript
const client = new AgentClient<MyAgent>({agent: "my-agent",host: window.location.host,onStateUpdate: (state) => {// state is typed as MyAgent's state type},});
Existing untyped usage continues to work without changes. The RPC type utilities (AgentMethods, AgentStub, RPCMethods) are now exported from agents/client for advanced typing scenarios.
agents, @cloudflare/ai-chat, and @cloudflare/codemode now require zod ^4.0.0. Zod v3 is no longer supported.
@cloudflare/ai-chat fixes
- Turn serialization —
onChatMessage()and_reply()work is now queued so user requests, tool continuations, andsaveMessages()never stream concurrently. - Duplicate messages on stop — Clicking stop during an active stream no longer splits the assistant message into two entries.
- Duplicate messages after tool calls — Orphaned client IDs no longer leak into persistent storage.
keepAlive() and keepAliveWhile() are no longer experimental
keepAlive() now uses a lightweight in-memory ref count instead of schedule rows. Multiple concurrent callers share a single alarm cycle. The @experimental tag has been removed from both keepAlive() and keepAliveWhile().
@cloudflare/codemode: TanStack AI integration
A new entry point @cloudflare/codemode/tanstack-ai adds support for TanStack AI’s chat() as an alternative to the Vercel AI SDK’s streamText():
JavaScript
import {createCodeTool,tanstackTools,} from "@cloudflare/codemode/tanstack-ai";import { chat } from "@tanstack/ai";const codeTool = createCodeTool({tools: [tanstackTools(myServerTools)],executor,});const stream = chat({ adapter, tools: [codeTool], messages });TypeScript
import { createCodeTool, tanstackTools } from "@cloudflare/codemode/tanstack-ai";import { chat } from "@tanstack/ai";const codeTool = createCodeTool({tools: [tanstackTools(myServerTools)],executor,});const stream = chat({ adapter, tools: [codeTool], messages });
Upgrade
To update to the latest version:
npm i agents@latest @cloudflare/ai-chat@latestSource: Cloudflare
Latest Posts
- Microsoft Copilot Studio – Read only Analytics Viewer role [MC1255508]
![Microsoft Copilot Studio - Read only Analytics Viewer role [MC1255508] 2 pexels steve 14003554](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==)
- (Updated) Microsoft Entra: Passkeys in Microsoft registration campaigns [MC1253746]
![(Updated) Microsoft Entra: Passkeys in Microsoft registration campaigns [MC1253746] 3 pexels tomfisk 3285716](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==)
- (Updated) Outlook Mobile: Follow a meeting option [MC1248393]
![(Updated) Outlook Mobile: Follow a meeting option [MC1248393] 4 man 4749237 1920](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==)
- (Updated) Purview Communication Compliance: Temporary delays in processing due to planned infrastructure migration [MC1214183]
![(Updated) Purview Communication Compliance: Temporary delays in processing due to planned infrastructure migration [MC1214183] 5 pexels thisisengineering 3862132](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==)

![Microsoft Copilot Studio - Read only Analytics Viewer role [MC1255508] 2 pexels steve 14003554](https://mwpro.co.uk/wp-content/uploads/2025/06/pexels-steve-14003554-150x150.webp)
![(Updated) Microsoft Entra: Passkeys in Microsoft registration campaigns [MC1253746] 3 pexels tomfisk 3285716](https://mwpro.co.uk/wp-content/uploads/2025/06/pexels-tomfisk-3285716-150x150.webp)
![(Updated) Outlook Mobile: Follow a meeting option [MC1248393] 4 man 4749237 1920](https://mwpro.co.uk/wp-content/uploads/2025/06/man-4749237_1920-150x150.webp)
![(Updated) Purview Communication Compliance: Temporary delays in processing due to planned infrastructure migration [MC1214183] 5 pexels thisisengineering 3862132](https://mwpro.co.uk/wp-content/uploads/2024/08/pexels-thisisengineering-3862132-150x150.webp)
![(Updated) Microsoft Viva Engage: Flexible targeting of Storyline Announcements [MC1183013] 7 (Updated) Microsoft Viva Engage: Flexible targeting of Storyline Announcements [MC1183013]](https://mwpro.co.uk/wp-content/uploads/2024/08/pexels-googledeepmind-25626442-150x150.webp)