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
- (Updated) Microsoft Teams frontline BYOD onboarding wizard [MC1184994]
![(Updated) Microsoft Teams frontline BYOD onboarding wizard [MC1184994] 2 pexels rdne 8522797](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==)
- (Updated) Microsoft 365 admin center – Usage reports: Agent usage (preview) [MC1148545]
![(Updated) Microsoft 365 admin center - Usage reports: Agent usage (preview) [MC1148545] 3 pexels thelazyartist 1623016](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==)
- (Updated) New policy to add watermarks to content generated or altered by using AI in Microsoft 365 [MC1221451]
![(Updated) New policy to add watermarks to content generated or altered by using AI in Microsoft 365 [MC1221451] 4 pexels cottonbro 8721343](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==)
- (Updated) Microsoft Viva Engage: Flexible targeting of Storyline Announcements [MC1183013]
![(Updated) Microsoft Viva Engage: Flexible targeting of Storyline Announcements [MC1183013] 5 pexels googledeepmind 25626442](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==)

![(Updated) Microsoft Teams frontline BYOD onboarding wizard [MC1184994] 2 pexels rdne 8522797](https://mwpro.co.uk/wp-content/uploads/2025/06/pexels-rdne-8522797-150x150.webp)
![(Updated) Microsoft 365 admin center - Usage reports: Agent usage (preview) [MC1148545] 3 pexels thelazyartist 1623016](https://mwpro.co.uk/wp-content/uploads/2025/06/pexels-thelazyartist-1623016-150x150.webp)
![(Updated) New policy to add watermarks to content generated or altered by using AI in Microsoft 365 [MC1221451] 4 pexels cottonbro 8721343](https://mwpro.co.uk/wp-content/uploads/2024/08/pexels-cottonbro-8721343-150x150.webp)
![(Updated) Microsoft Viva Engage: Flexible targeting of Storyline Announcements [MC1183013] 5 pexels googledeepmind 25626442](https://mwpro.co.uk/wp-content/uploads/2024/08/pexels-googledeepmind-25626442-150x150.webp)