---
title: Agents Changelog
image: https://edgetunnel-b2h.pages.dev/cf-twitter-card.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://edgetunnel-b2h.pages.dev/changelog/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Changelog

New updates and improvements at Cloudflare.

[ Subscribe to RSS ](https://edgetunnel-b2h.pages.dev/changelog/rss/index.xml) [ View RSS feeds ](https://edgetunnel-b2h.pages.dev/fundamentals/new-features/available-rss-feeds/) 

Agents

![hero image](https://edgetunnel-b2h.pages.dev/_astro/hero.CVYJHPAd_26AMqX.svg) 

Jul 13, 2026
1. ### [Agents can respond to MCP elicitation requests](https://edgetunnel-b2h.pages.dev/changelog/post/2026-07-13-mcp-client-elicitation/)  
[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)[ Workers ](https://edgetunnel-b2h.pages.dev/workers/)  
Agents connected to Model Context Protocol (MCP) servers with [addMcpServer](https://edgetunnel-b2h.pages.dev/agents/model-context-protocol/apis/client-api/) can now handle [elicitation ↗](https://modelcontextprotocol.io/specification/2025-11-25/client/elicitation) requests.  
Elicitation lets an MCP server request user input while it handles a tool call. Form mode collects structured, non-sensitive data. URL mode asks for consent before opening an out-of-band flow, such as third-party authorization or payment.  
sequenceDiagram  
    participant User  
    participant Agent as Agent (MCP client)  
    participant Server as MCP server  
    participant Browser  
    Server->>Agent: elicitation/create  
    Agent->>User: Show server, reason, and input or URL  
    User->>Agent: Submit, open, decline, or cancel  
    Agent->>Browser: Open URL after consent (URL mode)  
    Agent->>Server: accept, decline, or cancel  
    Server-->>Agent: Optional URL completion notification  
Register a handler for each mode your Agent supports in `onStart()`:

  * [  JavaScript ](#tab-panel-3153)
  * [  TypeScript ](#tab-panel-3154)

**JavaScript**  
```js  
import { Agent } from "agents";  
export class MyAgent extends Agent {  
  onStart() {  
    this.mcp.configureElicitationHandlers({  
      form: (request, serverId) => this.forwardToUser(request, serverId),  
      url: (request, serverId) => this.forwardToUser(request, serverId),  
    });  
  }  
  forwardToUser(request, serverId) {  
    // Show the request in your UI and resolve after the user responds.  
    throw new Error(  
      `Implement elicitation for ${serverId}: ${request.params.message}`,  
    );  
  }  
}  
```

**TypeScript**  
```ts  
import { Agent } from "agents";  
import type { ElicitRequest, ElicitResult } from "agents/mcp";  
export class MyAgent extends Agent<Env> {  
  onStart() {  
    this.mcp.configureElicitationHandlers({  
      form: (request, serverId) => this.forwardToUser(request, serverId),  
      url: (request, serverId) => this.forwardToUser(request, serverId),  
    });  
  }  
  private forwardToUser(  
    request: ElicitRequest,  
    serverId: string,  
  ): Promise<ElicitResult> {  
    // Show the request in your UI and resolve after the user responds.  
    throw new Error(  
      `Implement elicitation for ${serverId}: ${request.params.message}`,  
    );  
  }  
}  
```  
Connections advertise only the modes with configured handlers. An Agent without handlers advertises no elicitation capability, which lets the server use its fallback. The SDK stores the advertised modes with each MCP server registration so they survive Durable Object hibernation. Callback functions remain in memory and reattach when `onStart()` runs.  
For implementation details and a browser forwarding pattern, refer to [MCP client elicitation](https://edgetunnel-b2h.pages.dev/agents/model-context-protocol/apis/client-api/#elicitation). The [mcp-client ↗](https://github.com/cloudflare/agents/tree/main/examples/mcp-client) and [mcp-elicitation ↗](https://github.com/cloudflare/agents/tree/main/examples/mcp-elicitation) examples implement both sides.  
#### Upgrade  
To update to this release:  
 npm  yarn  pnpm  bun  
```  
npm i agents@latest  
```  
```  
yarn add agents@latest  
```  
```  
pnpm add agents@latest  
```  
```  
bun add agents@latest  
```

Jun 26, 2026
1. ### [Agents SDK adds background sub-agents and a unified turn entry point](https://edgetunnel-b2h.pages.dev/changelog/post/2026-06-26-agents-sdk-v0170/)  
[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)[ Workers ](https://edgetunnel-b2h.pages.dev/workers/)  
The latest release of the [Agents SDK ↗](https://github.com/cloudflare/agents) makes it easier to run long work in the background, drive turns through one entry point, and keep chat agents working through deploys, evictions, and reconnects.  
This release adds first-class detached (background) sub-agent runs with live progress and durable milestones, a single `runTurn` turn-admission entry point, and a large round of recovery and reliability fixes that continue converging `@cloudflare/think` and `@cloudflare/ai-chat` onto one model.  
#### Background sub-agents with progress and milestones  
`runAgentTool` can now dispatch a sub-agent without blocking the calling turn. A detached run returns a handle immediately and is owned by a durable, eviction-surviving backbone instead of being abandoned when the dispatching turn ends.

  * [  JavaScript ](#tab-panel-3149)
  * [  TypeScript ](#tab-panel-3150)

**JavaScript**  
```js  
class OrdersAgent extends Think {  
  async startImport(input) {  
    // Fire-and-forget, or wire a durable completion callback  
    // (by method name, like schedule()):  
    await this.runAgentTool(ImportAgent, {  
      input,  
      detached: { onFinish: "onImportDone", maxBudgetMs: 60 * 60 * 1000 },  
    });  
  }  
  // result.status: "completed" | "error" | "aborted" | "interrupted"  
  async onImportDone(run, result) {}  
}  
```

**TypeScript**  
```ts  
class OrdersAgent extends Think {  
  async startImport(input) {  
    // Fire-and-forget, or wire a durable completion callback  
    // (by method name, like schedule()):  
    await this.runAgentTool(ImportAgent, {  
      input,  
      detached: { onFinish: "onImportDone", maxBudgetMs: 60 * 60 * 1000 },  
    });  
  }  
  // result.status: "completed" | "error" | "aborted" | "interrupted"  
  async onImportDone(run, result) {}  
}  
```  
Highlights:

  * **Durable, exactly-once-on-the-happy-path completion** via a warm fast path plus a self-scheduling reconcile backbone that survives eviction and deploys.
  * **Bounded.** An absolute `maxBudgetMs` ceiling (default 24h) and `cancelAgentTool(runId)` keep abandoned runs from holding a concurrency slot forever.
  * **`detached: { notify: true }`** lets a finished background run inject a message back into the chat so the model reacts to the result — no hand-wired `onFinish` needed.  
Sub-agents can also report mid-run progress that rides their own turn stream back to the parent's connected clients:

  * [  JavaScript ](#tab-panel-3145)
  * [  TypeScript ](#tab-panel-3146)

**JavaScript**  
```js  
// Inside the child sub-agent:  
await this.reportProgress({  
  fraction: 0.6,  
  phase: "deploying",  
  message: "Generating menu page…",  
});  
```

**TypeScript**  
```ts  
// Inside the child sub-agent:  
await this.reportProgress({  
  fraction: 0.6,  
  phase: "deploying",  
  message: "Generating menu page…",  
});  
```  
Progress surfaces on `AgentToolRunState.progress` via `useAgentToolEvents`, so a background-runs tray can render a live bar without drilling in, and the latest snapshot is persisted for inspection after eviction. Naming a `milestone` promotes a signal to a durable, replayable row, and `detached: { onMilestones }` can surface a milestone as a synthetic chat message (`"narrate"` for a cheap status line, or `"react"` to drive a model turn).  
#### One entry point for turns: `runTurn`  
`@cloudflare/think` adds a public `runTurn(options)` facade that unifies turn admission behind a single `mode`:

  * [  JavaScript ](#tab-panel-3143)
  * [  TypeScript ](#tab-panel-3144)

**JavaScript**  
```js  
await this.runTurn({ mode: "wait", messages }); // saveMessages / continueLastTurn  
await this.runTurn({ mode: "submit", messages }); // durable submitMessages  
await this.runTurn({ mode: "stream", messages }); // chat()  
```

**TypeScript**  
```ts  
await this.runTurn({ mode: "wait", messages }); // saveMessages / continueLastTurn  
await this.runTurn({ mode: "submit", messages }); // durable submitMessages  
await this.runTurn({ mode: "stream", messages }); // chat()  
```  
`stream` mode accepts array and function inputs to match `wait` mode, and all entry points now route through a shared internal admission path that throws a clear error on nested blocking admissions that previously could deadlock.  
#### Recovery and reliability  
A large part of this release continues hardening recovery and converging `@cloudflare/think` and `@cloudflare/ai-chat` onto one model:

  * **Stream stall watchdog.** `AIChatAgent` can detect and recover from a hung model/transport stream via the opt-in `chatStreamStallTimeoutMs` watchdog. With `chatRecovery` enabled the stall routes into the same bounded-recovery machinery a deploy or eviction uses; otherwise it surfaces as a terminal stream error so the spinner clears.
  * **Interrupted tool-call repair.** `AIChatAgent` now repairs a transcript with a dead server-tool call before re-entering inference (parity with `@cloudflare/think`), so a recovered turn no longer fails with `AI_MissingToolResultsError`. An overridable `repairInterruptedToolPart(part)` hook lets apps customize the repaired shape.
  * **Stuck status after reconnect.** Fixed AI SDK `status` getting stuck when a reconnect races a turn that has been accepted but has not started streaming yet, so the UI now renders the in-flight turn instead of settling on `ready`.
  * **Live "recovering…" on connect.** `AIChatAgent` now replays the recovering status to a client that connects mid-recovery, so `useAgentChat`'s `isRecovering` reflects in-progress recovery immediately instead of appearing frozen.
  * **Terminal connection failures.** The client stops reconnecting on terminal WebSocket close events and exposes them via `connectionError` / `onConnectionError` on `AgentClient`, `useAgent`, and `useAgentChat`.
  * **Agent-tool child recovery.** A healthy long-running sub-agent run is no longer abandoned as `interrupted` after a deploy (both `@cloudflare/think` and `AIChatAgent`).
  * **Workflows from sub-agent facets.** Agent Workflows can now start from sub-agent facets, with callbacks and Workflow RPC routed back to the originating facet.
  * Plus forward-progress crediting convergence, broadcast-first give-up ordering, an event-driven auto-continuation barrier, and structured row-size compaction in `AIChatAgent`.  
#### Other improvements

  * **Shared chat React core.** A new `agents/chat/react` entry exposes `useAgentChat`, transport helpers, and shared wire types, with `syncMessagesToServer` for server-authoritative transcript storage. `@cloudflare/think/react` and `@cloudflare/ai-chat/react` are now thin wrappers over it.
  * **Optional `ai` peer.** The root `agents` and `@cloudflare/codemode` runtimes no longer reference AI SDK types, so they bundle without `ai` / `zod` installed; AI-specific entry points still require the peer when imported. `just-bash` likewise moves to an optional peer used only by the skills bash runner.
  * **Code Mode.** The default `DynamicWorkerExecutor` timeout increases from 30s to 60s, executions now dispose the dynamically-loaded Worker and its RPC stub after each run (fixing a flaky isolate-shutdown assertion), connector imports are cleaned up, and the outer MCP tool-call context is passed to `openApiMcpServer` request callbacks.
  * **Voice.** Voice turns now support AI SDK `fullStream` responses (and warn when `textStream` is used).
  * **MCP.** `McpAgent` server-to-client requests can now be sent from callbacks that do not inherit the agent's async context, including callbacks reached through Worker Loader RPC.
  * **Experimental: server actions and channels.** This release lays groundwork for guarded server actions (`action()` / `getActions()` with a durable replay ledger and approvals) and a unified channels surface (`configureChannels()`, `deliverNotice()`). Both are experimental and their APIs may change, so we don't recommend depending on them yet.  
#### Upgrade  
To update to the latest version:  
 npm  yarn  pnpm  bun  
```  
npm i agents@latest @cloudflare/think@latest @cloudflare/ai-chat@latest @cloudflare/codemode@latest @cloudflare/voice@latest  
```  
```  
yarn add agents@latest @cloudflare/think@latest @cloudflare/ai-chat@latest @cloudflare/codemode@latest @cloudflare/voice@latest  
```  
```  
pnpm add agents@latest @cloudflare/think@latest @cloudflare/ai-chat@latest @cloudflare/codemode@latest @cloudflare/voice@latest  
```  
```  
bun add agents@latest @cloudflare/think@latest @cloudflare/ai-chat@latest @cloudflare/codemode@latest @cloudflare/voice@latest  
```  
Refer to the [Think documentation](https://edgetunnel-b2h.pages.dev/agents/harnesses/think/), [Code Mode documentation](https://edgetunnel-b2h.pages.dev/agents/tools/codemode/), and [Agents documentation](https://edgetunnel-b2h.pages.dev/agents/) for more information.

Jun 16, 2026
1. ### [Agents SDK improves browser automation, code execution, and recovery](https://edgetunnel-b2h.pages.dev/changelog/post/2026-06-16-agents-sdk-v0161/)  
[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)[ Workers ](https://edgetunnel-b2h.pages.dev/workers/)  
The latest release of the [Agents SDK ↗](https://github.com/cloudflare/agents) makes it easier to build agents that can safely interact with real systems and keep working through interruptions.  
Agents can now browse websites through Browser Run, write code against external tools through Code Mode, use client-provided tools when delegating to Think sub-agents, and recover more reliably from deploys, Durable Object evictions, and connection churn.  
#### Safer browser automation  
Agents can now use [Browser Run](https://edgetunnel-b2h.pages.dev/browser-run/) through a single durable `browser_execute` tool. Instead of choosing from a fixed list of actions, the model writes code against the Chrome DevTools Protocol (CDP) and can inspect pages, capture screenshots, read rendered content, debug frontend behavior, and interact with live browser sessions.

  * [  JavaScript ](#tab-panel-3147)
  * [  TypeScript ](#tab-panel-3148)

**JavaScript**  
```js  
const browserTools = createBrowserTools({  
  ctx: this.ctx,  
  browser: this.env.BROWSER,  
  loader: this.env.LOADER,  
  session: { mode: "dynamic" },  
});  
```

**TypeScript**  
```ts  
const browserTools = createBrowserTools({  
  ctx: this.ctx,  
  browser: this.env.BROWSER,  
  loader: this.env.LOADER,  
  session: { mode: "dynamic" },  
});  
```  
Browser sessions can be one-time, reused, or promoted from one-time to persistent during a run. This is useful when an agent needs a human to log in, complete MFA, or approve a sensitive action. The run can pause, keep the same tabs and cookies, and resume after approval.  
The browser tools also add Live View URLs, optional session recording, and quick actions such as `browser_markdown`, `browser_extract`, `browser_links`, and `browser_scrape` for one-shot browsing tasks.  
#### Resumable code execution with approvals  
Code Mode now uses `createCodemodeRuntime`, connectors, and a durable execution log. This lets you give a model one `codemode` tool instead of a large prompt full of tool definitions. The model can discover the capabilities it needs, write code against typed globals, and reuse saved snippets.

  * [  JavaScript ](#tab-panel-3155)
  * [  TypeScript ](#tab-panel-3156)

**JavaScript**  
```js  
const runtime = createCodemodeRuntime({  
  ctx: this.ctx,  
  executor: new DynamicWorkerExecutor({ loader: this.env.LOADER }),  
  connectors: [new GithubConnector(this.ctx, this.env, connection)],  
});  
const result = streamText({  
  model,  
  messages,  
  tools: { codemode: runtime.tool() },  
});  
```

**TypeScript**  
```ts  
const runtime = createCodemodeRuntime({  
  ctx: this.ctx,  
  executor: new DynamicWorkerExecutor({ loader: this.env.LOADER }),  
  connectors: [new GithubConnector(this.ctx, this.env, connection)],  
});  
const result = streamText({  
  model,  
  messages,  
  tools: { codemode: runtime.tool() },  
});  
```  
When the code reaches an approval-gated action, the runtime pauses execution and returns a pending approval. After approval, completed calls replay from the durable log, the approved action runs, and the same code continues. This makes it practical to build agents that create issues, update external systems, or perform other side effects without custom pause-and-resume logic for every tool.  
#### Better Think delegation  
Think sub-agents can now use client-defined tools over the RPC `chat()` path. A parent agent can pass tool schemas with `clientTools` and resolve tool calls through `onClientToolCall`. This lets delegated agents use caller-provided capabilities without requiring a browser WebSocket.

  * [  JavaScript ](#tab-panel-3157)
  * [  TypeScript ](#tab-panel-3158)

**JavaScript**  
```js  
await child.chat(message, callback, {  
  signal,  
  clientTools: [  
    {  
      name: "get_user_timezone",  
      description: "Get the caller's timezone",  
      parameters: { type: "object" },  
    },  
  ],  
  onClientToolCall: async ({ toolName, input }) => {  
    return runClientTool(toolName, input);  
  },  
});  
```

**TypeScript**  
```ts  
await child.chat(message, callback, {  
  signal,  
  clientTools: [  
    {  
      name: "get_user_timezone",  
      description: "Get the caller's timezone",  
      parameters: { type: "object" },  
    },  
  ],  
  onClientToolCall: async ({ toolName, input }) => {  
    return runClientTool(toolName, input);  
  },  
});  
```  
Think Workflows also improve `step.prompt()`. A prompt step now runs a full agentic turn before returning structured output, so the agent can call tools before producing the typed result. This makes Workflow steps more useful for durable triage, research, and approval flows.  
The unified Think execute tool can also include `cdp.*` browser capabilities alongside `state.*` and `tools.*` when Browser Run is bound.  
#### Voice output device selection  
Voice clients can route assistant audio to a specific output device. Use `outputDeviceId` with `useVoiceAgent`, or call `client.setOutputDevice()` from the framework-agnostic client.

  * [  JavaScript ](#tab-panel-3151)
  * [  TypeScript ](#tab-panel-3152)

**JavaScript**  
```js  
const voice = useVoiceAgent({  
  agent: "MyVoiceAgent",  
  outputDeviceId: selectedSpeakerId,  
});  
```

**TypeScript**  
```ts  
const voice = useVoiceAgent({  
  agent: "MyVoiceAgent",  
  outputDeviceId: selectedSpeakerId,  
});  
```  
Browsers without speaker-selection support continue playing through the default output device and report a non-fatal `outputDeviceError`.  
#### Reliability fixes  
This release includes several fixes for production agents:

  * `useAgent` and `AgentClient` handle WebSocket replacement more reliably during reconnects and configuration changes.
  * Chat stream replay is more reliable after reconnects, deploys, and provider errors.
  * Fiber recovery continues across multi-pass scans and backs off when recovery hooks keep failing.
  * Agent teardown continues even when the request that started teardown is canceled.
  * Large session histories use byte-budgeted reads to reduce memory pressure during startup.  
#### Upgrade  
To update to the latest version:  
 npm  yarn  pnpm  bun  
```  
npm i agents@latest @cloudflare/think@latest @cloudflare/codemode@latest @cloudflare/ai-chat@latest @cloudflare/voice@latest  
```  
```  
yarn add agents@latest @cloudflare/think@latest @cloudflare/codemode@latest @cloudflare/ai-chat@latest @cloudflare/voice@latest  
```  
```  
pnpm add agents@latest @cloudflare/think@latest @cloudflare/codemode@latest @cloudflare/ai-chat@latest @cloudflare/voice@latest  
```  
```  
bun add agents@latest @cloudflare/think@latest @cloudflare/codemode@latest @cloudflare/ai-chat@latest @cloudflare/voice@latest  
```  
Refer to the [Code Mode documentation](https://edgetunnel-b2h.pages.dev/agents/tools/codemode/), [Browser tools documentation](https://edgetunnel-b2h.pages.dev/agents/tools/browser/), [Think tools documentation](https://edgetunnel-b2h.pages.dev/agents/harnesses/think/tools/), and [Voice documentation](https://edgetunnel-b2h.pages.dev/agents/communication-channels/voice/) for more information.

Jun 16, 2026
1. ### [Introducing GLM-5.2 on Workers AI](https://edgetunnel-b2h.pages.dev/changelog/post/2026-06-16-glm-52-workers-ai/)  
[ Workers ](https://edgetunnel-b2h.pages.dev/workers/)[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)[ Workers AI ](https://edgetunnel-b2h.pages.dev/workers-ai/)  
We are excited to announce **GLM-5.2** on Workers AI, Z.ai's flagship agentic coding model.  
[@cf/zai-org/glm-5.2](https://edgetunnel-b2h.pages.dev/workers-ai/models/glm-5.2/) is a text generation model built for agentic coding workflows. With function calling and reasoning support, it can handle long codebases, multi-step planning, and tool-augmented agents.

**Key features and use cases:**

  * **Agentic coding**: Designed for autonomous coding tasks, long-horizon planning, and complex software engineering workflows
  * **Large context window**: GLM-5.2 supports up to a 1,048,576 token context window. Workers AI is launching the model with a 262,144 token context window and plans to increase this in the future
  * **Function calling**: Build agents that invoke tools and APIs across multiple conversation turns
  * **Reasoning**: Tackles complex problem-solving and step-by-step reasoning tasks  
Use GLM-5.2 through the [Workers AI binding](https://edgetunnel-b2h.pages.dev/workers-ai/configuration/bindings/) (`env.AI.run()`), the REST API at `/run` or `/v1/chat/completions`, or [AI Gateway](https://edgetunnel-b2h.pages.dev/ai-gateway/).  
Pricing is available on the [model page](https://edgetunnel-b2h.pages.dev/workers-ai/models/glm-5.2/) or [pricing page](https://edgetunnel-b2h.pages.dev/workers-ai/platform/pricing/).

Jun 02, 2026
1. ### [Agents SDK v0.14.0: Agent Skills, messengers, scheduled tasks, Workflows, and hardened chat recovery](https://edgetunnel-b2h.pages.dev/changelog/post/2026-06-02-agents-sdk-v0140/)  
[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)[ Workers ](https://edgetunnel-b2h.pages.dev/workers/)  
The latest release of the [Agents SDK ↗](https://github.com/cloudflare/agents) adds four new ways to build with `@cloudflare/think`: on-demand Agent Skills, chat messengers (starting with Telegram), declarative scheduled tasks, and durable reasoning steps inside Workflows. This release also significantly hardens durable chat recovery, so turns reliably ride through deploys, evictions, and stalled model streams in production.  
#### Agent Skills (experimental)  
Give an agent a catalog of on-demand instructions, resources, and scripts. A skill source adds a catalog to the system prompt, and the model activates a skill only when a task matches — so a large library of capabilities does not bloat every prompt.

  * [  JavaScript ](#tab-panel-3159)
  * [  TypeScript ](#tab-panel-3160)

**JavaScript**  
```js  
import { Think, skills } from "@cloudflare/think";  
import bundledSkills from "agents:skills";  
export class SkillsAgent extends Think {  
  getSkills() {  
    return [  
      bundledSkills,  
      skills.r2(this.env.SKILLS_BUCKET, { prefix: "skills/" }),  
    ];  
  }  
}  
```

**TypeScript**  
```ts  
import { Think, skills } from "@cloudflare/think";  
import bundledSkills from "agents:skills";  
export class SkillsAgent extends Think<Env> {  
  getSkills() {  
    return [  
      bundledSkills,  
      skills.r2(this.env.SKILLS_BUCKET, { prefix: "skills/" }),  
    ];  
  }  
}  
```  
The `agents:skills` import bundles a local `./skills` directory through the Agents Vite plugin (one directory per skill, each with a `SKILL.md`). Skills can also load from R2 or a manifest. When skills are available, Think exposes `activate_skill`, `read_skill_resource`, and an optional `run_skill_script` tool. Skill loading is resilient: a duplicate or failing source is skipped with a warning instead of breaking the agent.  
Agent Skills are **experimental**, and script execution in particular is early. The API may change in a future release. We would love your feedback — tell us what you are building and what is missing in the [Agents repository ↗](https://github.com/cloudflare/agents/discussions).  
#### Messengers  
Connect a Think agent directly to a chat platform. Think owns the webhook route, conversation routing, durable reply fiber, and streamed delivery back to the provider. Telegram ships as the first provider.

  * [  JavaScript ](#tab-panel-3171)
  * [  TypeScript ](#tab-panel-3172)

**JavaScript**  
```js  
import { Think } from "@cloudflare/think";  
import {  
  defineMessengers,  
  ThinkMessengerStateAgent,  
} from "@cloudflare/think/messengers";  
import telegramMessenger from "@cloudflare/think/messengers/telegram";  
export { ThinkMessengerStateAgent };  
export class SupportAgent extends Think {  
  getMessengers() {  
    return defineMessengers({  
      telegram: telegramMessenger({  
        token: this.env.TELEGRAM_BOT_TOKEN,  
        userName: "support_bot",  
        secretToken: this.env.TELEGRAM_WEBHOOK_SECRET_TOKEN,  
      }),  
    });  
  }  
}  
```

**TypeScript**  
```ts  
import { Think } from "@cloudflare/think";  
import {  
  defineMessengers,  
  ThinkMessengerStateAgent,  
} from "@cloudflare/think/messengers";  
import telegramMessenger from "@cloudflare/think/messengers/telegram";  
export { ThinkMessengerStateAgent };  
export class SupportAgent extends Think<Env> {  
  getMessengers() {  
    return defineMessengers({  
      telegram: telegramMessenger({  
        token: this.env.TELEGRAM_BOT_TOKEN,  
        userName: "support_bot",  
        secretToken: this.env.TELEGRAM_WEBHOOK_SECRET_TOKEN,  
      }),  
    });  
  }  
}  
```  
Each Chat SDK thread maps to its own Think sub-agent by default, so group chats and direct messages do not share memory. Multiple bots, custom conversation routing, and custom providers are all supported.  
#### Scheduled tasks  
Declare recurring, timezone-aware prompts and handlers with a typed domain-specific language (DSL). Think reconciles the declarations on startup and re-arms the next occurrence after each run, backed by durable idempotent submissions.

  * [  JavaScript ](#tab-panel-3167)
  * [  TypeScript ](#tab-panel-3168)

**JavaScript**  
```js  
import { Think, defineScheduledTasks } from "@cloudflare/think";  
export class DigestAgent extends Think {  
  getScheduledTasks() {  
    return defineScheduledTasks({  
      weeklyCommitReport: {  
        schedule: "every week on monday at 09:00",  
        prompt:  
          "Compile my GitHub commits for the last week and summarize them.",  
      },  
      workout: {  
        schedule: "every day at 08:00 in Europe/London",  
        prompt: "Start my workout.",  
      },  
    });  
  }  
}  
```

**TypeScript**  
```ts  
import { Think, defineScheduledTasks } from "@cloudflare/think";  
export class DigestAgent extends Think<Env> {  
  getScheduledTasks() {  
    return defineScheduledTasks({  
      weeklyCommitReport: {  
        schedule: "every week on monday at 09:00",  
        prompt:  
          "Compile my GitHub commits for the last week and summarize them.",  
      },  
      workout: {  
        schedule: "every day at 08:00 in Europe/London",  
        prompt: "Start my workout.",  
      },  
    });  
  }  
}  
```  
#### Think Workflows  
Run a model-driven reasoning step inside a Cloudflare Workflow with `ThinkWorkflow` and `step.prompt()`, with durable typed structured output, long waits, and approval gates.

  * [  JavaScript ](#tab-panel-3177)
  * [  TypeScript ](#tab-panel-3178)

**JavaScript**  
```js  
import { z } from "zod";  
import { ThinkWorkflow } from "@cloudflare/think/workflows";  
const draftSchema = z.object({  
  title: z.string(),  
  summary: z.string(),  
  labels: z.array(z.string()),  
});  
export class TriageWorkflow extends ThinkWorkflow {  
  async run(event, step) {  
    const draft = await step.prompt("triage-issue", {  
      prompt: `Triage issue #${event.payload.issueNumber}`,  
      output: draftSchema,  
      timeout: "3 days",  
    });  
    await step.do("apply-labels", async () => {  
      await this.agent.applyLabels(draft.labels);  
    });  
  }  
}  
```

**TypeScript**  
```ts  
import { z } from "zod";  
import { ThinkWorkflow } from "@cloudflare/think/workflows";  
import type { ThinkWorkflowStep } from "@cloudflare/think/workflows";  
import type { AgentWorkflowEvent } from "agents/workflows";  
const draftSchema = z.object({  
  title: z.string(),  
  summary: z.string(),  
  labels: z.array(z.string()),  
});  
export class TriageWorkflow extends ThinkWorkflow<TriageAgent, Params> {  
  async run(event: AgentWorkflowEvent<Params>, step: ThinkWorkflowStep) {  
    const draft = await step.prompt("triage-issue", {  
      prompt: `Triage issue #${event.payload.issueNumber}`,  
      output: draftSchema,  
      timeout: "3 days",  
    });  
    await step.do("apply-labels", async () => {  
      await this.agent.applyLabels(draft.labels);  
    });  
  }  
}  
```  
#### Production hardening for durable chat recovery  
Durable chat turns have always been designed to survive a mid-turn deploy or Durable Object eviction. This release is a major hardening pass on that machinery for production.

  * **Better recovery during deploys.** Turns now ride through continuous deploys and evictions without losing completed work or re-running tools that already ran.
  * **A live "recovering…" signal.** `useAgentChat` exposes a new `isRecovering` flag, so a recovering turn shows progress instead of looking frozen. Most UIs render `isStreaming || isRecovering` as "busy".
  * **Stalled streams recover.** Set `chatStreamStallTimeoutMs` to route a hung provider stream into the same recovery path instead of leaving an infinite spinner.
  * **Sub-agents re-attach.** On parent recovery, an in-flight `agentTool()` child is re-attached to its result rather than abandoned and re-run, so long-running children no longer lose work under deploys.  
#### MCP transport improvements

  * **Resumable streams** — In-flight tool calls over Server-Sent Events (SSE) survive a dropped connection. Clients reconnect with `Last-Event-ID` and replay anything they missed.
  * **Readable server IDs** — `addMcpServer` accepts an optional `id`, so tools surface as readable keys (for example `tool_github_create_pull_request`) instead of opaque connection IDs.
  * **Better handling of concurrent requests** — Overlapping JSON-RPC requests are now correctly correlated to their responses across the HTTP and RPC transports.  
#### Other improvements

  * **Compaction** — A `Session`'s `tokenCounter` now also drives the compaction boundary decision ("what to compress"), not just the fire/no-fire trigger.
  * **`@cloudflare/worker-bundler`** — Adds a `virtualModules` option to `createWorker` to provide in-memory module source during bundling.
  * **Client-tool continuations** — Parallel tool results now coalesce into a single continuation, immediate resume requests attach to the pending continuation, and server-side `needsApproval` continuations resume reliably after approval.  
#### Upgrade  
To update to the latest version:  
 npm  yarn  pnpm  bun  
```  
npm i agents@latest @cloudflare/think@latest @cloudflare/ai-chat@latest  
```  
```  
yarn add agents@latest @cloudflare/think@latest @cloudflare/ai-chat@latest  
```  
```  
pnpm add agents@latest @cloudflare/think@latest @cloudflare/ai-chat@latest  
```  
```  
bun add agents@latest @cloudflare/think@latest @cloudflare/ai-chat@latest  
```  
Refer to the [Agents API reference](https://edgetunnel-b2h.pages.dev/agents/runtime/) and [Chat agents documentation](https://edgetunnel-b2h.pages.dev/agents/communication-channels/chat/chat-agents/) for more information.

May 29, 2026
1. ### [Share sandbox previews through Cloudflare Tunnel](https://edgetunnel-b2h.pages.dev/changelog/post/2026-05-29-sandbox-named-tunnels/)  
[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)  
[Sandboxes](https://edgetunnel-b2h.pages.dev/sandbox/) can expose a service running inside the container on a public preview URL through the `sandbox.tunnels` namespace. The SDK uses `cloudflared` inside the sandbox so you can share a running service without configuring `exposePort()` or a custom domain.  
By default, `sandbox.tunnels.get(port)` creates a [quick tunnel ↗](https://try.cloudflare.com/) on a zero-config `*.trycloudflare.com` URL — no Cloudflare account, DNS record, or custom domain required. This is perfect for quick development and for `.workers.dev` deployments.

  * [  JavaScript ](#tab-panel-3163)
  * [  TypeScript ](#tab-panel-3164)

**JavaScript**  
```js  
import { getSandbox } from "@cloudflare/sandbox";  
const sandbox = getSandbox(env.Sandbox, "my-sandbox");  
await sandbox.startProcess("python -m http.server 8080");  
const tunnel = await sandbox.tunnels.get(8080);  
console.log(tunnel.url); // → https://random-words-here.trycloudflare.com  
```

**TypeScript**  
```ts  
import { getSandbox } from "@cloudflare/sandbox";  
const sandbox = getSandbox(env.Sandbox, "my-sandbox");  
await sandbox.startProcess("python -m http.server 8080");  
const tunnel = await sandbox.tunnels.get(8080);  
console.log(tunnel.url); // → https://random-words-here.trycloudflare.com  
```  
#### Named tunnels  
For more control you can create a named tunnel through `sandbox.tunnels.get(port, { name })`. A named tunnel binds a hostname (`<name>.<your-zone>`) backed by a [Cloudflare Tunnel](https://edgetunnel-b2h.pages.dev/cloudflare-one/networks/connectors/cloudflare-tunnel/) and a CNAME record on your zone resulting in something like [https://my-app-preview.example.com ↗](https://my-app-preview.example.com).  
Unlike quick tunnels, which generate a new random URL each time, a named tunnel produces a persistent URL that survives container restarts. This makes named tunnels suitable for production use cases where you want control over the tunnel and it's origin.

  * [  JavaScript ](#tab-panel-3161)
  * [  TypeScript ](#tab-panel-3162)

**JavaScript**  
```js  
const tunnel = await sandbox.tunnels.get(8080, { name: "my-app-preview" });  
console.log(tunnel.url); // → https://my-app-preview.example.com  
```

**TypeScript**  
```ts  
const tunnel = await sandbox.tunnels.get(8080, { name: "my-app-preview" });  
console.log(tunnel.url); // → https://my-app-preview.example.com  
```  
Calling `sandbox.destroy()` tears down the Cloudflare Tunnel and the associated DNS record alongside the container, so you do not leave dangling tunnels or records behind.  
#### Upgrade  
To update to the latest version:  
 npm  yarn  pnpm  bun  
```  
npm i @cloudflare/sandbox@latest  
```  
```  
yarn add @cloudflare/sandbox@latest  
```  
```  
pnpm add @cloudflare/sandbox@latest  
```  
```  
bun add @cloudflare/sandbox@latest  
```  
For full API details, refer to the [Sandbox tunnels reference](https://edgetunnel-b2h.pages.dev/sandbox/api/tunnels/).

May 13, 2026
1. ### [Agents SDK v0.12.4: chat recovery, routing retries, durable Think submissions, and Voice connection control](https://edgetunnel-b2h.pages.dev/changelog/post/2026-05-13-agents-sdk-v0124/)  
[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)[ Workers ](https://edgetunnel-b2h.pages.dev/workers/)  
The latest release of the [Agents SDK ↗](https://github.com/cloudflare/agents) brings more reliable chat recovery, fixes Agent state synchronization during reconnects, adds durable submissions for Think, exposes routing retry configuration, and adds connection control for Voice agents.  
#### Chat recovery improvements  
`@cloudflare/ai-chat` now keeps server turns running when a browser or client stream is interrupted. This is useful for long-running AI responses where users refresh the page, close a tab, or temporarily lose connection. Calling `stop()` still cancels the server turn.  
Set `cancelOnClientAbort: true` if browser or client aborts should also cancel the server turn:

  * [  JavaScript ](#tab-panel-3165)
  * [  TypeScript ](#tab-panel-3166)

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

**TypeScript**  
```ts  
const chat = useAgentChat({  
  agent: "assistant",  
  name: "user-123",  
  cancelOnClientAbort: true,  
});  
```  
Notable bug fixes:

  * Chat stream resume negotiation no longer throws when 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 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.  
#### Agent state and routing fixes  
`agents@0.12.4` prevents duplicate initial state frames during WebSocket connection setup. This avoids stale initial state messages overwriting state updates already sent by the client.  
Agent recovery is also more reliable when tool calls span a Durable Object restart. Recovery now defers user finish hooks until after agent startup and isolates hook failures, so one failed hook does not block other recovered runs from finalizing.  
`getAgentByName()` now supports `routingRetry` for transient Durable Object routing failures:

  * [  JavaScript ](#tab-panel-3169)
  * [  TypeScript ](#tab-panel-3170)

**JavaScript**  
```js  
import { getAgentByName } from "agents";  
const agent = await getAgentByName(env.AssistantAgent, "user-123", {  
  routingRetry: {  
    maxAttempts: 3,  
  },  
});  
```

**TypeScript**  
```ts  
import { getAgentByName } from "agents";  
const agent = await getAgentByName(env.AssistantAgent, "user-123", {  
  routingRetry: {  
    maxAttempts: 3,  
  },  
});  
```  
#### Durable Think submissions  
`@cloudflare/think` now supports durable programmatic submissions. `submitMessages()` provides durable acceptance, idempotent retries, status inspection, cancellation, and cleanup for server-driven turns that should continue after the caller returns.  
`Think.chat()` RPC turns now run inside chat recovery fibers and persist their stream chunks. Interrupted sub-agent turns can recover partial output instead of starting over.  
`ChatOptions.tools` has been removed from the TypeScript API. Define durable tools on the child agent or use agent tools for orchestration. Runtime `options.tools` values passed by legacy callers are ignored with a warning.  
#### Think message pruning behavior change  
`@cloudflare/think` no longer applies `pruneMessages({ toolCalls: "before-last-2-messages" })` to model context by default. The previous default could strip client-side tool results from longer multi-turn flows.  
`truncateOlderMessages` still runs as before, so context cost remains bounded. Subclasses that relied on the old aggressive pruning can opt back in from `beforeTurn`:

  * [  JavaScript ](#tab-panel-3175)
  * [  TypeScript ](#tab-panel-3176)

**JavaScript**  
```js  
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",  
      }),  
    };  
  }  
}  
```

**TypeScript**  
```ts  
import { Think } from "@cloudflare/think";  
import { pruneMessages } from "ai";  
export class MyAgent extends Think<Env> {  
  beforeTurn(ctx) {  
    return {  
      messages: pruneMessages({  
        messages: ctx.messages,  
        toolCalls: "before-last-2-messages",  
      }),  
    };  
  }  
}  
```  
#### Voice agent connection control  
`@cloudflare/voice` adds an `enabled` option to `useVoiceAgent`. React apps can now delay creating and connecting a `VoiceClient` until prerequisites such as capability tokens are ready.

  * [  JavaScript ](#tab-panel-3173)
  * [  TypeScript ](#tab-panel-3174)

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

**TypeScript**  
```ts  
const voice = useVoiceAgent({  
  agent: "MyVoiceAgent",  
  enabled: Boolean(token),  
});  
```  
This release also fixes Workers AI speech-to-text session edge cases and `withVoice` text streaming from AI SDK `textStream` responses.  
#### Other improvements

  * **Streamable HTTP routing** — Server-to-client requests now route through the originating POST stream when no standalone SSE stream is available.
  * **Structured tool output** — Tool output shapes are preserved when truncating older messages or oversized persisted rows.
  * **Non-chat Think tool steps** — Think agent-tool children can complete without emitting assistant text and can return structured output through `getAgentToolOutput`.
  * **Sub-agent schedules** — Stale sub-agent schedule rows are pruned when their owning facet registry entry no longer exists.
  * **`@cloudflare/codemode`** — Adds a browser-safe export with an iframe sandbox executor and resolves OpenAPI specs inside the sandbox to avoid Worker Loader RPC size limits.  
#### Upgrade  
To update to the latest version:  
```sh  
npm i agents@latest @cloudflare/ai-chat@latest @cloudflare/think@latest @cloudflare/voice@latest  
```  
Refer to the [Agents API reference](https://edgetunnel-b2h.pages.dev/agents/runtime/) and [Chat agents documentation](https://edgetunnel-b2h.pages.dev/agents/communication-channels/chat/chat-agents/) for more information.

Apr 15, 2026
1. ### [Agent Lee adds Write Operations and Generative UI](https://edgetunnel-b2h.pages.dev/changelog/post/2026-04-15-agentlee-writeops-genui/)  
[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)  
#### Agent Lee adds Write Operations and Generative UI  
We are excited to announce two major capability upgrades for **Agent Lee**, the AI co-pilot built directly into the Cloudflare dashboard. Agent Lee is designed to understand your specific account configuration, and with this release, it moves from a passive advisor to an active assistant that can help you manage your infrastructure and visualize your data through natural language.  
#### Take action with Write Operations  
Agent Lee can now perform changes on your behalf across your Cloudflare account. Whether you need to update DNS records, modify SSL/TLS settings, or configure Workers routes, you can simply ask.  
To ensure security and accuracy, every write operation requires **explicit user approval**. Before any change is committed, Agent Lee will present a summary of the proposed action in plain language. No action is taken until you select **Confirm**, and this approval requirement is enforced at the infrastructure level to prevent unauthorized changes.

**Example requests:**

  * _"Add an A record for blog.example.com pointing to 192.0.2.10."_
  * _"Enable Always Use HTTPS on my zone."_
  * _"Set the SSL mode for example.com to Full (strict)."_  
#### Visualize data with Generative UI  
Understanding your traffic and security trends is now as easy as asking a question. Agent Lee now features **Generative UI**, allowing it to render inline charts and structured data visualizations directly within the chat interface using your actual account telemetry.

**Example requests:**

  * _"Show me a chart of my traffic over the last 7 days."_
  * _"What does my error rate look like for the past 24 hours?"_
  * _"Graph my cache hit rate for example.com this week."_

---  
#### Availability  
These features are currently available in **Beta** for all users on the **Free plan**. To get started, log in to the [Cloudflare dashboard ↗](https://dash.cloudflare.com) and select **Ask AI** in the upper right corner.  
To learn more about how to interact with your account using AI, refer to the [Agent Lee documentation](https://edgetunnel-b2h.pages.dev/agent-lee/).

Apr 13, 2026
1. ### [Secure credential injection and dynamic egress policies for Sandboxes](https://edgetunnel-b2h.pages.dev/changelog/post/2026-04-13-sandbox-outbound-workers-tls-auth/)  
[ Containers ](https://edgetunnel-b2h.pages.dev/containers/)[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)  
Outbound Workers for [Sandboxes](https://edgetunnel-b2h.pages.dev/sandbox/) and [Containers](https://edgetunnel-b2h.pages.dev/containers/) now support zero-trust credential injection, TLS interception, allow/deny lists, and dynamic per-instance egress policies. These features give platforms running agentic workloads full control over what leaves the sandbox, without exposing secrets to untrusted workloads, like user-generated code or coding agents.  
#### Credential injection  
Because outbound handlers run in the Workers runtime, outside the sandbox, they can hold secrets the sandbox never sees. A sandboxed workload can make a plain request, and credentials are transparently attached before a request is forwarded upstream.  
For instance, you could run an agent in a sandbox and ensure that any requests it makes to Github are authenticated. But it will never be able to access the credentials:

**TypeScript**  
```ts  
export class MySandbox extends Sandbox {}  
MySandbox.outboundByHost = {  
  "github.com": (request: Request, env: Env, ctx: OutboundHandlerContext) => {  
    const requestWithAuth = new Request(request);  
    requestWithAuth.headers.set("x-auth-token", env.SECRET);  
    return fetch(requestWithAuth);  
  },  
};  
```  
You can easily inject unique credentials for different instances by using `ctx.containerId`:

**TypeScript**  
```ts  
MySandbox.outboundByHost = {  
  "my-internal-vcs.dev": async (  
    request: Request,  
    env: Env,  
    ctx: OutboundHandlerContext,  
  ) => {  
    const authKey = await env.KEYS.get(ctx.containerId);  
    const requestWithAuth = new Request(request);  
    requestWithAuth.headers.set("x-auth-token", authKey);  
    return fetch(requestWithAuth);  
  },  
};  
```  
No token is ever passed into the sandbox. You can rotate secrets in the Worker environment and every request will pick them up immediately.  
#### TLS interception  
Outbound Workers now intercept HTTPS traffic. A unique ephemeral certificate authority (CA) and private key are created for each sandbox instance. The CA is placed into the sandbox and trusted by default. The ephemeral private key never leaves the container runtime sidecar process and is never shared across instances.  
With TLS interception active, outbound Workers can act as a transparent proxy for both HTTP and HTTPS traffic.  
#### Allow and deny hosts  
Easily filter outbound traffic with `allowedHosts` and `deniedHosts`. When `allowedHosts` is set, it becomes a deny-by-default allowlist. Both properties support glob patterns.

**TypeScript**  
```ts  
export class MySandbox extends Sandbox {  
  allowedHosts = ["github.com", "npmjs.org"];  
}  
```  
#### Dynamic outbound handlers  
Define named outbound handlers then apply or remove them at runtime using `setOutboundHandler()` or `setOutboundByHost()`. This lets you change egress policy for a running sandbox without restarting it.

**TypeScript**  
```ts  
export class MySandbox extends Sandbox {}  
MySandbox.outboundHandlers = {  
  allowHosts: async (req: Request, env: Env, ctx: OutboundHandlerContext ) => {  
    const url = new URL(req.url);  
    if (ctx.params.allowedHostnames.includes(url.hostname)) {  
      return fetch(req);  
    }  
    return new Response(null, { status: 403 });  
  },  
  noHttp: async () => {  
    return new Response(null, { status: 403 });  
  },  
};  
```  
Apply handlers programmatically from your Worker:

**TypeScript**  
```ts  
const sandbox = getSandbox(env.Sandbox, userId);  
// Open network for setup  
await sandbox.setOutboundHandler("allowHosts", {  
  allowedHostnames: ["github.com", "npmjs.org"],  
});  
await sandbox.exec("npm install");  
// Lock down after setup  
await sandbox.setOutboundHandler("noHttp");  
```  
Handlers accept `params`, so you can customize behavior per instance without defining separate handler functions.  
#### Get started  
Upgrade to `@cloudflare/containers@0.3.0` or `@cloudflare/sandbox@0.8.9` to use these features.  
For more details, refer to [Sandbox outbound traffic](https://edgetunnel-b2h.pages.dev/sandbox/guides/outbound-traffic/) and [Container outbound traffic](https://edgetunnel-b2h.pages.dev/containers/platform-details/outbound-traffic/).

Mar 23, 2026
1. ### [Agents SDK v0.8.0: readable state, idempotent schedules, typed AgentClient, and Zod 4](https://edgetunnel-b2h.pages.dev/changelog/post/2026-03-23-agents-sdk-v080/)  
[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)[ Workers ](https://edgetunnel-b2h.pages.dev/workers/)  
The latest release of the [Agents SDK ↗](https://github.com/cloudflare/agents) 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 ](#tab-panel-3179)
  * [  TypeScript ](#tab-panel-3180)

**JavaScript**  
```js  
const agent = useAgent({  
  agent: "game-agent",  
  name: "room-123",  
});  
// Read state directly — no separate useState + onStateUpdate needed  
return <div>Score: {agent.state?.score}</div>;  
// Spread for partial updates  
agent.setState({ ...agent.state, score: (agent.state?.score ?? 0) + 10 });  
```

**TypeScript**  
```ts  
const agent = useAgent<GameAgent, GameState>({  
  agent: "game-agent",  
  name: "room-123",  
});  
// Read state directly — no separate useState + onStateUpdate needed  
return <div>Score: {agent.state?.score}</div>;  
// Spread for partial updates  
agent.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 ](#tab-panel-3181)
  * [  TypeScript ](#tab-panel-3182)

**JavaScript**  
```js  
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**  
```ts  
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 ](#tab-panel-3183)
  * [  TypeScript ](#tab-panel-3184)

**JavaScript**  
```js  
import { Agent } from "agents";  
class MyAgent extends Agent {  
  async onStart() {  
    // Safe across restarts — only one row is created  
    await this.schedule(60, "maintenance", undefined, { idempotent: true });  
  }  
}  
```

**TypeScript**  
```ts  
import { Agent } from "agents";  
class MyAgent extends Agent {  
  async onStart() {  
    // Safe across restarts — only one row is created  
    await this.schedule(60, "maintenance", undefined, { idempotent: true });  
  }  
}  
```  
Two new warnings help catch common foot-guns:

  * Calling `schedule()` inside `onStart()` without `{ idempotent: true }` emits a `console.warn` with actionable guidance (once per callback; skipped for cron and when `idempotent` is set explicitly).
  * If an alarm cycle processes 10 or more stale one-shot rows for the same callback, the SDK emits a `console.warn` and a `schedule:duplicate_warning` diagnostics 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 ](#tab-panel-3187)
  * [  TypeScript ](#tab-panel-3188)

**JavaScript**  
```js  
const client = new AgentClient({  
  agent: "my-agent",  
  host: window.location.host,  
});  
// Typed call — method name autocompletes, args and return type inferred  
const value = await client.call("getValue");  
// Typed stub — direct RPC-style proxy  
await client.stub.getValue();  
await client.stub.add(1, 2);  
```

**TypeScript**  
```ts  
const client = new AgentClient<MyAgent>({  
  agent: "my-agent",  
  host: window.location.host,  
});  
// Typed call — method name autocompletes, args and return type inferred  
const value = await client.call("getValue");  
// Typed stub — direct RPC-style proxy  
await client.stub.getValue();  
await client.stub.add(1, 2);  
```  
State is automatically inferred from the agent type, so `onStateUpdate` is also typed:

  * [  JavaScript ](#tab-panel-3185)
  * [  TypeScript ](#tab-panel-3186)

**JavaScript**  
```js  
const client = new AgentClient({  
  agent: "my-agent",  
  host: window.location.host,  
  onStateUpdate: (state) => {  
    // state is typed as MyAgent's state type  
  },  
});  
```

**TypeScript**  
```ts  
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, and `saveMessages()` 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 ↗](https://tanstack.com/ai) `chat()` as an alternative to the Vercel AI SDK's `streamText()`:

  * [  JavaScript ](#tab-panel-3193)
  * [  TypeScript ](#tab-panel-3194)

**JavaScript**  
```js  
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**  
```ts  
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:  
```sh  
npm i agents@latest @cloudflare/ai-chat@latest  
```

Mar 17, 2026
1. ### [@cloudflare/codemode v0.2.1: MCP barrel export, zero-dependency main entry point, and custom sandbox modules](https://edgetunnel-b2h.pages.dev/changelog/post/2026-03-17-codemode-sdk-v021/)  
[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)[ Workers ](https://edgetunnel-b2h.pages.dev/workers/)  
The latest releases of [@cloudflare/codemode ↗](https://www.npmjs.com/package/@cloudflare/codemode) add a new MCP barrel export, remove `ai` and `zod` as required peer dependencies from the main entry point, and give you more control over the sandbox.  
#### New `@cloudflare/codemode/mcp` export  
A new `@cloudflare/codemode/mcp` entry point provides two functions that wrap MCP servers with Code Mode:

  * **`codeMcpServer({ server, executor })`** — wraps an existing MCP server with a single `code` tool where each upstream tool becomes a typed `codemode.*` method.
  * **`openApiMcpServer({ spec, executor, request })`** — creates `search` and `execute` MCP tools from an OpenAPI spec with host-side request proxying and automatic `$ref` resolution.

  * [  JavaScript ](#tab-panel-3191)
  * [  TypeScript ](#tab-panel-3192)

**JavaScript**  
```js  
import { codeMcpServer } from "@cloudflare/codemode/mcp";  
import { DynamicWorkerExecutor } from "@cloudflare/codemode";  
const executor = new DynamicWorkerExecutor({ loader: env.LOADER });  
// Wrap an existing MCP server — all its tools become  
// typed methods the LLM can call from generated code  
const server = await codeMcpServer({ server: upstreamMcp, executor });  
```

**TypeScript**  
```ts  
import { codeMcpServer } from "@cloudflare/codemode/mcp";  
import { DynamicWorkerExecutor } from "@cloudflare/codemode";  
const executor = new DynamicWorkerExecutor({ loader: env.LOADER });  
// Wrap an existing MCP server — all its tools become  
// typed methods the LLM can call from generated code  
const server = await codeMcpServer({ server: upstreamMcp, executor });  
```  
#### Zero-dependency main entry point

**Breaking change in v0.2.0:** `generateTypes` and the `ToolDescriptor` / `ToolDescriptors` types have moved to `@cloudflare/codemode/ai`:

  * [  JavaScript ](#tab-panel-3189)
  * [  TypeScript ](#tab-panel-3190)

**JavaScript**  
```js  
// Before  
import { generateTypes } from "@cloudflare/codemode";  
// After  
import { generateTypes } from "@cloudflare/codemode/ai";  
```

**TypeScript**  
```ts  
// Before  
import { generateTypes } from "@cloudflare/codemode";  
// After  
import { generateTypes } from "@cloudflare/codemode/ai";  
```  
The main entry point (`@cloudflare/codemode`) no longer requires the `ai` or `zod` peer dependencies. It now exports:

| Export                      | Description                                                 |
| --------------------------- | ----------------------------------------------------------- |
| sanitizeToolName            | Sanitize tool names into valid JS identifiers               |
| normalizeCode               | Normalize LLM-generated code into async arrow functions     |
| generateTypesFromJsonSchema | Generate TypeScript type definitions from plain JSON Schema |
| jsonSchemaToType            | Convert a single JSON Schema to a TypeScript type string    |
| DynamicWorkerExecutor       | Sandboxed code execution via Dynamic Worker Loader          |
| ToolDispatcher              | RPC target for dispatching tool calls from sandbox to host  |  
The `ai` and `zod` peer dependencies are now optional — only required when importing from `@cloudflare/codemode/ai`.  
#### Custom sandbox modules  
`DynamicWorkerExecutor` now accepts an optional `modules` option to inject custom ES modules into the sandbox:

  * [  JavaScript ](#tab-panel-3195)
  * [  TypeScript ](#tab-panel-3196)

**JavaScript**  
```js  
const executor = new DynamicWorkerExecutor({  
  loader: env.LOADER,  
  modules: {  
    "utils.js": `export function add(a, b) { return a + b; }`,  
  },  
});  
// Sandbox code can then: import { add } from "utils.js"  
```

**TypeScript**  
```ts  
const executor = new DynamicWorkerExecutor({  
  loader: env.LOADER,  
  modules: {  
    "utils.js": `export function add(a, b) { return a + b; }`,  
  },  
});  
// Sandbox code can then: import { add } from "utils.js"  
```  
#### Internal normalization and sanitization  
`DynamicWorkerExecutor` now normalizes code and sanitizes tool names internally. You no longer need to call `normalizeCode()` or `sanitizeToolName()` before passing code and functions to `execute()`.  
#### Upgrade  
```sh  
npm i @cloudflare/codemode@latest  
```  
See the [Code Mode documentation](https://edgetunnel-b2h.pages.dev/agents/tools/codemode/) for the full API reference.

Mar 03, 2026
1. ### [Real-time file watching in Sandboxes](https://edgetunnel-b2h.pages.dev/changelog/post/2026-03-03-sandbox-watch-file-events/)  
[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)  
[Sandboxes](https://edgetunnel-b2h.pages.dev/sandbox/) now support real-time filesystem watching via `sandbox.watch()`. The method returns a [Server-Sent Events ↗](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent%5Fevents) stream backed by native inotify, so your Worker receives `create`, `modify`, `delete`, and `move` events as they happen inside the container.  
#### `sandbox.watch(path, options)`  
Pass a directory path and optional filters. The returned stream is a standard `ReadableStream` you can proxy directly to a browser client or consume server-side.

  * [  JavaScript ](#tab-panel-3197)
  * [  TypeScript ](#tab-panel-3198)

**JavaScript**  
```js  
// Stream events to a browser client  
const stream = await sandbox.watch("/workspace/src", {  
  recursive: true,  
  include: ["*.ts", "*.js"],  
});  
return new Response(stream, {  
  headers: { "Content-Type": "text/event-stream" },  
});  
```

**TypeScript**  
```ts  
// Stream events to a browser client  
const stream = await sandbox.watch("/workspace/src", {  
  recursive: true,  
  include: ["*.ts", "*.js"],  
});  
return new Response(stream, {  
  headers: { "Content-Type": "text/event-stream" },  
});  
```  
#### Server-side consumption with `parseSSEStream`  
Use `parseSSEStream` to iterate over events inside a Worker without forwarding them to a client.

  * [  JavaScript ](#tab-panel-3199)
  * [  TypeScript ](#tab-panel-3200)

**JavaScript**  
```js  
import { parseSSEStream } from "@cloudflare/sandbox";  
const stream = await sandbox.watch("/workspace/src", { recursive: true });  
for await (const event of parseSSEStream(stream)) {  
  console.log(event.type, event.path);  
}  
```

**TypeScript**  
```ts  
import { parseSSEStream } from "@cloudflare/sandbox";  
import type { FileWatchSSEEvent } from "@cloudflare/sandbox";  
const stream = await sandbox.watch("/workspace/src", { recursive: true });  
for await (const event of parseSSEStream<FileWatchSSEEvent>(stream)) {  
  console.log(event.type, event.path);  
}  
```  
Each event includes a `type` field (`create`, `modify`, `delete`, or `move`) and the affected `path`. Move events also include a `from` field with the original path.  
#### Options

| Option    | Type       | Description                                                 |
| --------- | ---------- | ----------------------------------------------------------- |
| recursive | boolean    | Watch subdirectories. Defaults to false.                    |
| include   | string\[\] | Glob patterns to filter events. Omit to receive all events. |  
#### Upgrade  
To update to the latest version:  
```sh  
npm i @cloudflare/sandbox@latest  
```  
For full API details, refer to the [Sandbox file watching reference](https://edgetunnel-b2h.pages.dev/sandbox/api/file-watching/).

Mar 02, 2026
1. ### [Agents SDK v0.7.0: Observability rewrite, keepAlive, and waitForMcpConnections](https://edgetunnel-b2h.pages.dev/changelog/post/2026-03-02-agents-sdk-v070/)  
[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)[ Workers ](https://edgetunnel-b2h.pages.dev/workers/)  
The latest release of the [Agents SDK ↗](https://github.com/cloudflare/agents) rewrites observability from scratch with `diagnostics_channel`, adds `keepAlive()` to prevent Durable Object eviction during long-running work, and introduces `waitForMcpConnections` so MCP tools are always available when `onChatMessage` runs.  
#### Observability rewrite  
The previous observability system used `console.log()` with a custom `Observability.emit()` interface. v0.7.0 replaces it with structured events published to [diagnostics channels](https://edgetunnel-b2h.pages.dev/workers/runtime-apis/nodejs/diagnostics-channel/) — silent by default, zero overhead when nobody is listening.  
Every event has a `type`, `payload`, and `timestamp`. Events are routed to seven named channels:

| Channel          | Event types                                                                                                                                      |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| agents:state     | state:update                                                                                                                                     |
| agents:rpc       | rpc, rpc:error                                                                                                                                   |
| agents:message   | message:request, message:response, message:clear, message:cancel, message:error, tool:result, tool:approval                                      |
| agents:schedule  | schedule:create, schedule:execute, schedule:cancel, schedule:retry, schedule:error, queue:retry, queue:error                                     |
| agents:lifecycle | connect, destroy                                                                                                                                 |
| agents:workflow  | workflow:start, workflow:event, workflow:approved, workflow:rejected, workflow:terminated, workflow:paused, workflow:resumed, workflow:restarted |
| agents:mcp       | mcp:client:preconnect, mcp:client:connect, mcp:client:authorize, mcp:client:discover                                                             |  
Use the typed `subscribe()` helper from `agents/observability` for type-safe access:

  * [  JavaScript ](#tab-panel-3209)
  * [  TypeScript ](#tab-panel-3210)

**JavaScript**  
```js  
import { subscribe } from "agents/observability";  
const unsub = subscribe("rpc", (event) => {  
  if (event.type === "rpc") {  
    console.log(`RPC call: ${event.payload.method}`);  
  }  
  if (event.type === "rpc:error") {  
    console.error(  
      `RPC failed: ${event.payload.method} — ${event.payload.error}`,  
    );  
  }  
});  
// Clean up when done  
unsub();  
```

**TypeScript**  
```ts  
import { subscribe } from "agents/observability";  
const unsub = subscribe("rpc", (event) => {  
  if (event.type === "rpc") {  
    console.log(`RPC call: ${event.payload.method}`);  
  }  
  if (event.type === "rpc:error") {  
    console.error(  
      `RPC failed: ${event.payload.method} — ${event.payload.error}`,  
    );  
  }  
});  
// Clean up when done  
unsub();  
```  
In production, all diagnostics channel messages are automatically forwarded to [Tail Workers](https://edgetunnel-b2h.pages.dev/workers/observability/logs/tail-workers/) — no subscription code needed in the agent itself:

  * [  JavaScript ](#tab-panel-3205)
  * [  TypeScript ](#tab-panel-3206)

**JavaScript**  
```js  
export default {  
  async tail(events) {  
    for (const event of events) {  
      for (const msg of event.diagnosticsChannelEvents) {  
        // msg.channel is "agents:rpc", "agents:workflow", etc.  
        console.log(msg.timestamp, msg.channel, msg.message);  
      }  
    }  
  },  
};  
```

**TypeScript**  
```ts  
export default {  
  async tail(events) {  
    for (const event of events) {  
      for (const msg of event.diagnosticsChannelEvents) {  
        // msg.channel is "agents:rpc", "agents:workflow", etc.  
        console.log(msg.timestamp, msg.channel, msg.message);  
      }  
    }  
  },  
};  
```  
The custom `Observability` override interface is still supported for users who need to filter or forward events to external services.  
For the full event reference, refer to the [Observability documentation](https://edgetunnel-b2h.pages.dev/agents/runtime/operations/observability/).  
#### `keepAlive()` and `keepAliveWhile()`  
Durable Objects are evicted after a period of inactivity (typically 70-140 seconds with no incoming requests, WebSocket messages, or alarms). During long-running operations — streaming LLM responses, waiting on external APIs, running multi-step computations — the agent can be evicted mid-flight.  
`keepAlive()` prevents this by creating a 30-second heartbeat schedule. The alarm firing resets the inactivity timer. Returns a disposer function that cancels the heartbeat when called.

  * [  JavaScript ](#tab-panel-3203)
  * [  TypeScript ](#tab-panel-3204)

**JavaScript**  
```js  
const dispose = await this.keepAlive();  
try {  
  const result = await longRunningComputation();  
  await sendResults(result);  
} finally {  
  dispose();  
}  
```

**TypeScript**  
```ts  
const dispose = await this.keepAlive();  
try {  
  const result = await longRunningComputation();  
  await sendResults(result);  
} finally {  
  dispose();  
}  
```  
`keepAliveWhile()` wraps an async function with automatic cleanup — the heartbeat starts before the function runs and stops when it completes:

  * [  JavaScript ](#tab-panel-3201)
  * [  TypeScript ](#tab-panel-3202)

**JavaScript**  
```js  
const result = await this.keepAliveWhile(async () => {  
  const data = await longRunningComputation();  
  return data;  
});  
```

**TypeScript**  
```ts  
const result = await this.keepAliveWhile(async () => {  
  const data = await longRunningComputation();  
  return data;  
});  
```  
Key details:

  * **Multiple concurrent callers** — Each `keepAlive()` call returns an independent disposer. Disposing one does not affect others.
  * **AIChatAgent built-in** — `AIChatAgent` automatically calls `keepAlive()` during streaming responses. You do not need to add it yourself.
  * **Uses the scheduling system** — The heartbeat does not conflict with your own schedules. It shows up in `getSchedules()` if you need to inspect it.  
Note  
`keepAlive()` is marked `@experimental` and may change between releases.  
For the full API reference and when-to-use guidance, refer to [Schedule tasks — Keeping the agent alive](https://edgetunnel-b2h.pages.dev/agents/runtime/execution/schedule-tasks/#keeping-the-agent-alive).  
#### `waitForMcpConnections`  
`AIChatAgent` now waits for MCP server connections to settle before calling `onChatMessage`. This ensures `this.mcp.getAITools()` returns the full set of tools, especially after Durable Object hibernation when connections are being restored in the background.

  * [  JavaScript ](#tab-panel-3211)
  * [  TypeScript ](#tab-panel-3212)

**JavaScript**  
```js  
export class ChatAgent extends AIChatAgent {  
  // Default — waits up to 10 seconds  
  // waitForMcpConnections = { timeout: 10_000 };  
  // Wait forever  
  waitForMcpConnections = true;  
  // Disable waiting  
  waitForMcpConnections = false;  
}  
```

**TypeScript**  
```ts  
export class ChatAgent extends AIChatAgent {  
  // Default — waits up to 10 seconds  
  // waitForMcpConnections = { timeout: 10_000 };  
  // Wait forever  
  waitForMcpConnections = true;  
  // Disable waiting  
  waitForMcpConnections = false;  
}  
```

| Value                | Behavior                                      |
| -------------------- | --------------------------------------------- |
| { timeout: 10\_000 } | Wait up to 10 seconds (default)               |
| { timeout: N }       | Wait up to N milliseconds                     |
| true                 | Wait indefinitely until all connections ready |
| false                | Do not wait (old behavior before 0.2.0)       |  
For lower-level control, call `this.mcp.waitForConnections()` directly inside `onChatMessage` instead.  
#### Other improvements

  * **MCP deduplication by name and URL** — `addMcpServer` with HTTP transport now deduplicates on both server name and URL. Calling it with the same name but a different URL creates a new connection. URLs are normalized before comparison (trailing slashes, default ports, hostname case).
  * **`callbackHost` optional for non-OAuth servers** — `addMcpServer` no longer requires `callbackHost` when connecting to MCP servers that do not use OAuth.
  * **MCP URL security** — Server URLs are validated before connection to prevent SSRF. Private IP ranges, loopback addresses, link-local addresses, and cloud metadata endpoints are blocked.
  * **Custom denial messages** — `addToolOutput` now supports `state: "output-error"` with `errorText` for custom denial messages in human-in-the-loop tool approval flows.
  * **`requestId` in chat options** — `onChatMessage` options now include a `requestId` for logging and correlating events.  
#### Upgrade  
To update to the latest version:  
```sh  
npm i agents@latest @cloudflare/ai-chat@latest  
```

Feb 25, 2026
1. ### [Agents SDK v0.6.0: RPC transport for MCP, optional OAuth, hardened schema conversion, and @cloudflare/ai-chat fixes](https://edgetunnel-b2h.pages.dev/changelog/post/2026-02-25-agents-sdk-v060/)  
[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)[ Workers ](https://edgetunnel-b2h.pages.dev/workers/)  
The latest release of the [Agents SDK ↗](https://github.com/cloudflare/agents) lets you define an Agent and an McpAgent in the same Worker and connect them over RPC — no HTTP, no network overhead. It also makes OAuth opt-in for simple MCP connections, hardens the schema converter for production workloads, and ships a batch of `@cloudflare/ai-chat` reliability fixes.  
#### RPC transport for MCP  
You can now connect an Agent to an McpAgent in the same Worker using a Durable Object binding instead of an HTTP URL. The connection stays entirely within the Cloudflare runtime — no network round-trips, no serialization overhead.  
Pass the Durable Object namespace directly to `addMcpServer`:

  * [  JavaScript ](#tab-panel-3213)
  * [  TypeScript ](#tab-panel-3214)

**JavaScript**  
```js  
import { Agent } from "agents";  
export class MyAgent extends Agent {  
  async onStart() {  
    // Connect via DO binding — no HTTP, no network overhead  
    await this.addMcpServer("counter", env.MY_MCP);  
    // With props for per-user context  
    await this.addMcpServer("counter", env.MY_MCP, {  
      props: { userId: "user-123", role: "admin" },  
    });  
  }  
}  
```

**TypeScript**  
```ts  
import { Agent } from "agents";  
export class MyAgent extends Agent {  
  async onStart() {  
    // Connect via DO binding — no HTTP, no network overhead  
    await this.addMcpServer("counter", env.MY_MCP);  
    // With props for per-user context  
    await this.addMcpServer("counter", env.MY_MCP, {  
      props: { userId: "user-123", role: "admin" },  
    });  
  }  
}  
```  
The `addMcpServer` method now accepts `string | DurableObjectNamespace` as the second parameter with full TypeScript overloads, so HTTP and RPC paths are type-safe and cannot be mixed.  
Key capabilities:

  * **Hibernation support** — RPC connections survive Durable Object hibernation automatically. The binding name and props are persisted to storage and restored on wake-up, matching the behavior of HTTP MCP connections.
  * **Deduplication** — Calling `addMcpServer` with the same server name returns the existing connection instead of creating duplicates. Connection IDs are stable across hibernation restore.
  * **Smaller surface area** — The RPC transport internals have been rewritten and reduced from 609 lines to 245 lines. `RPCServerTransport` now uses `JSONRPCMessageSchema` from the MCP SDK for validation instead of hand-written checks.  
Note  
RPC transport is experimental. The API may change based on feedback. Refer to [the tracking issue ↗](https://github.com/cloudflare/agents/issues/565) for updates.  
#### Optional OAuth for MCP connections  
`addMcpServer()` no longer eagerly creates an OAuth provider for every connection. For servers that do not require authentication, a simple call is all you need:

  * [  JavaScript ](#tab-panel-3207)
  * [  TypeScript ](#tab-panel-3208)

**JavaScript**  
```js  
// No callbackHost, no OAuth config — just works  
await this.addMcpServer("my-server", "https://mcp.example.com");  
```

**TypeScript**  
```ts  
// No callbackHost, no OAuth config — just works  
await this.addMcpServer("my-server", "https://mcp.example.com");  
```  
If the server responds with a 401, the SDK throws a clear error: `"This MCP server requires OAuth authentication. Provide callbackHost in addMcpServer options to enable the OAuth flow."` The restore-from-storage flow also handles missing callback URLs gracefully, skipping auth provider creation for non-OAuth servers.  
#### Hardened JSON Schema to TypeScript converter  
The schema converter used by `generateTypes()` and `getAITools()` now handles edge cases that previously caused crashes in production:

  * **Depth and circular reference guards** — Prevents stack overflows on recursive or deeply nested schemas
  * **`$ref` resolution** — Supports internal JSON Pointers (`#/definitions/...`, `#/$defs/...`, `#`)
  * **Tuple support** — `prefixItems` (JSON Schema 2020-12) and array `items` (draft-07)
  * **OpenAPI 3.0 `nullable: true`** — Supported across all schema branches
  * **Per-tool error isolation** — One malformed schema cannot crash the full pipeline in `generateTypes()` or `getAITools()`
  * **Missing `inputSchema` fallback** — `getAITools()` falls back to `{ type: "object" }` instead of throwing  
#### `@cloudflare/ai-chat` fixes

  * **Tool denial flow** — Denied tool approvals (`approved: false`) now transition to `output-denied` with a `tool_result`, fixing Anthropic provider compatibility. Custom denial messages are supported via `state: "output-error"` and `errorText`.
  * **Abort/cancel support** — Streaming responses now properly cancel the reader loop when the abort signal fires and send a done signal to the client.
  * **Duplicate message persistence** — `persistMessages()` now reconciles assistant messages by content and order, preventing duplicate rows when clients resend full history.
  * **`requestId` in `OnChatMessageOptions`** — Handlers can now send properly-tagged error responses for pre-stream failures.
  * **`redacted_thinking` preservation** — The message sanitizer no longer strips Anthropic `redacted_thinking` blocks.
  * **`/get-messages` reliability** — Endpoint handling moved from a prototype `onRequest()` override to a constructor wrapper, so it works even when users override `onRequest` without calling `super.onRequest()`.
  * **Client tool APIs undeprecated** — `createToolsFromClientSchemas`, `clientTools`, `AITool`, `extractClientToolSchemas`, and the `tools` option on `useAgentChat` are restored for SDK use cases where tools are defined dynamically at runtime.
  * **`jsonSchema` initialization** — Fixed `jsonSchema not initialized` error when calling `getAITools()` in `onChatMessage`.  
#### Upgrade  
To update to the latest version:  
```sh  
npm i agents@latest @cloudflare/ai-chat@latest  
```

Feb 23, 2026
1. ### [Backup and restore API for Sandbox SDK](https://edgetunnel-b2h.pages.dev/changelog/post/2026-02-23-sandbox-backup-restore-api/)  
[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)[ R2 ](https://edgetunnel-b2h.pages.dev/r2/)[ Containers ](https://edgetunnel-b2h.pages.dev/containers/)  
[Sandboxes](https://edgetunnel-b2h.pages.dev/sandbox/) now support `createBackup()` and `restoreBackup()` methods for creating and restoring point-in-time snapshots of directories.  
This allows you to restore environments quickly. For instance, in order to develop in a sandbox, you may need to include a user's codebase and run a build step. Unfortunately `git clone` and `npm install` can take minutes, and you don't want to run these steps every time the user starts their sandbox.  
Now, after the initial setup, you can just call `createBackup()`, then `restoreBackup()` the next time this environment is needed. This makes it practical to pick up exactly where a user left off, even after days of inactivity, without repeating expensive setup steps.

**TypeScript**  
```ts  
const sandbox = getSandbox(env.Sandbox, "my-sandbox");  
// Make non-trivial changes to the file system  
await sandbox.gitCheckout(endUserRepo, { targetDir: "/workspace" });  
await sandbox.exec("npm install", { cwd: "/workspace" });  
// Create a point-in-time backup of the directory  
const backup = await sandbox.createBackup({ dir: "/workspace" });  
// Store the handle for later use  
await env.KV.put(`backup:${userId}`, JSON.stringify(backup));  
// ... in a future session...  
// Restore instead of re-cloning and reinstalling  
await sandbox.restoreBackup(backup);  
```  
Backups are stored in [R2](https://edgetunnel-b2h.pages.dev/r2) and can take advantage of [R2 object lifecycle rules](https://edgetunnel-b2h.pages.dev/sandbox/guides/backup-restore/#configure-r2-lifecycle-rules-for-automatic-cleanup) to ensure they do not persist forever.  
Key capabilities:

  * **Persist and reuse across sandbox sessions** — Easily store backup handles in KV, D1, or Durable Object storage for use in subsequent sessions
  * **Usable across multiple instances** — Fork a backup across many sandboxes for parallel work
  * **Named backups** — Provide optional human-readable labels for easier management
  * **TTLs** — Set time-to-live durations so backups are automatically removed from storage once they are no longer needed  
Note  
Backup and restore currently uses a FUSE overlay. Soon, native snapshotting at a lower level will be added to Containers and Sandboxes, improving speed and ergonomics. The current backup functionality provides a significant speed improvement over manually recreating a file system, but it will be further optimized in the future. The new snapshotting system will use a similar API, so changing to this system will be simple once it is available.  
To get started, refer to the [backup and restore guide](https://edgetunnel-b2h.pages.dev/sandbox/guides/backup-restore/) for setup instructions and usage patterns, or the [Backups API reference](https://edgetunnel-b2h.pages.dev/sandbox/api/backups/) for full method documentation.

Feb 20, 2026
1. ### [@cloudflare/codemode v0.1.0: a new runtime agnostic modular architecture](https://edgetunnel-b2h.pages.dev/changelog/post/2026-02-20-codemode-sdk-rewrite/)  
[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)[ Workers ](https://edgetunnel-b2h.pages.dev/workers/)  
The [@cloudflare/codemode ↗](https://www.npmjs.com/package/@cloudflare/codemode) package has been rewritten into a modular, runtime-agnostic SDK.  
[Code Mode ↗](https://blog.cloudflare.com/code-mode/) enables LLMs to write and execute code that orchestrates your tools, instead of calling them one at a time. This can (and does) yield significant token savings, reduces context window pressure and improves overall model performance on a task.  
The new `Executor` interface is runtime agnostic and comes with a prebuilt `DynamicWorkerExecutor` to run generated code in a [Dynamic Worker Loader](https://edgetunnel-b2h.pages.dev/workers/runtime-apis/bindings/worker-loader/).  
#### Breaking changes

  * Removed `experimental_codemode()` and `CodeModeProxy` — the package no longer owns an LLM call or model choice
  * New import path: `createCodeTool()` is now exported from `@cloudflare/codemode/ai`  
#### New features

  * **`createCodeTool()`** — Returns a standard AI SDK `Tool` to use in your AI agents.
  * **`Executor` interface** — Minimal `execute(code, fns)` contract. Implement for any code sandboxing primitive or runtime.  
#### `DynamicWorkerExecutor`  
Runs code in a [Dynamic Worker](https://edgetunnel-b2h.pages.dev/workers/runtime-apis/bindings/worker-loader/). It comes with the following features:

  * **Network isolation** — `fetch()` and `connect()` blocked by default (`globalOutbound: null`) when using `DynamicWorkerExecutor`
  * **Console capture** — `console.log/warn/error` captured and returned in `ExecuteResult.logs`
  * **Execution timeout** — Configurable via `timeout` option (default 30s)  
#### Usage

  * [  JavaScript ](#tab-panel-3217)
  * [  TypeScript ](#tab-panel-3218)

**JavaScript**  
```js  
import { createCodeTool } from "@cloudflare/codemode/ai";  
import { DynamicWorkerExecutor } from "@cloudflare/codemode";  
import { streamText } from "ai";  
const executor = new DynamicWorkerExecutor({ loader: env.LOADER });  
const codemode = createCodeTool({ tools: myTools, executor });  
const result = streamText({  
  model,  
  tools: { codemode },  
  messages,  
});  
```

**TypeScript**  
```ts  
import { createCodeTool } from "@cloudflare/codemode/ai";  
import { DynamicWorkerExecutor } from "@cloudflare/codemode";  
import { streamText } from "ai";  
const executor = new DynamicWorkerExecutor({ loader: env.LOADER });  
const codemode = createCodeTool({ tools: myTools, executor });  
const result = streamText({  
  model,  
  tools: { codemode },  
  messages,  
});  
```  
#### Wrangler configuration

  * [  wrangler.jsonc ](#tab-panel-3141)
  * [  wrangler.toml ](#tab-panel-3142)

**JSONC**  
```jsonc  
{  
  "worker_loaders": [{ "binding": "LOADER" }],  
}  
```

**TOML**  
```toml  
[[worker_loaders]]  
binding = "LOADER"  
```  
See the [Code Mode documentation](https://edgetunnel-b2h.pages.dev/agents/tools/codemode/) for full API reference and examples.  
#### Upgrade  
```sh  
npm i @cloudflare/codemode@latest  
```

Feb 17, 2026
1. ### [Agents SDK v0.5.0: Protocol message control, retry utilities, data parts, and @cloudflare/ai-chat v0.1.0](https://edgetunnel-b2h.pages.dev/changelog/post/2026-02-17-agents-sdk-v050/)  
[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)[ Workers ](https://edgetunnel-b2h.pages.dev/workers/)  
The latest release of the [Agents SDK ↗](https://github.com/cloudflare/agents) adds built-in retry utilities, per-connection protocol message control, and a fully rewritten `@cloudflare/ai-chat` with data parts, tool approval persistence, and zero breaking changes.  
#### Retry utilities  
A new `this.retry()` method lets you retry any async operation with exponential backoff and jitter. You can pass an optional `shouldRetry` predicate to bail early on non-retryable errors.

  * [  JavaScript ](#tab-panel-3215)
  * [  TypeScript ](#tab-panel-3216)

**JavaScript**  
```js  
class MyAgent extends Agent {  
  async onRequest(request) {  
    const data = await this.retry(() => callUnreliableService(), {  
      maxAttempts: 4,  
      shouldRetry: (err) => !(err instanceof PermanentError),  
    });  
    return Response.json(data);  
  }  
}  
```

**TypeScript**  
```ts  
class MyAgent extends Agent {  
  async onRequest(request: Request) {  
    const data = await this.retry(() => callUnreliableService(), {  
      maxAttempts: 4,  
      shouldRetry: (err) => !(err instanceof PermanentError),  
    });  
    return Response.json(data);  
  }  
}  
```  
Retry options are also available per-task on `queue()`, `schedule()`, `scheduleEvery()`, and `addMcpServer()`:

  * [  JavaScript ](#tab-panel-3229)
  * [  TypeScript ](#tab-panel-3230)

**JavaScript**  
```js  
// Per-task retry configuration, persisted in SQLite alongside the task  
await this.schedule(  
  Date.now() + 60_000,  
  "sendReport",  
  { userId: "abc" },  
  {  
    retry: { maxAttempts: 5 },  
  },  
);  
// Class-level retry defaults  
class MyAgent extends Agent {  
  static options = {  
    retry: { maxAttempts: 3 },  
  };  
}  
```

**TypeScript**  
```ts  
// Per-task retry configuration, persisted in SQLite alongside the task  
await this.schedule(Date.now() + 60_000, "sendReport", { userId: "abc" }, {  
  retry: { maxAttempts: 5 },  
});  
// Class-level retry defaults  
class MyAgent extends Agent {  
  static options = {  
    retry: { maxAttempts: 3 },  
  };  
}  
```  
Retry options are validated eagerly at enqueue/schedule time, and invalid values throw immediately. Internal retries have also been added for workflow operations (`terminateWorkflow`, `pauseWorkflow`, and others) with Durable Object-aware error detection.  
#### Per-connection protocol message control  
Agents automatically send JSON text frames (identity, state, MCP server lists) to every WebSocket connection. You can now suppress these per-connection for clients that cannot handle them — binary-only devices, MQTT clients, or lightweight embedded systems.

  * [  JavaScript ](#tab-panel-3219)
  * [  TypeScript ](#tab-panel-3220)

**JavaScript**  
```js  
class MyAgent extends Agent {  
  shouldSendProtocolMessages(connection, ctx) {  
    // Suppress protocol messages for MQTT clients  
    const subprotocol = ctx.request.headers.get("Sec-WebSocket-Protocol");  
    return subprotocol !== "mqtt";  
  }  
}  
```

**TypeScript**  
```ts  
class MyAgent extends Agent {  
  shouldSendProtocolMessages(connection: Connection, ctx: ConnectionContext) {  
    // Suppress protocol messages for MQTT clients  
    const subprotocol = ctx.request.headers.get("Sec-WebSocket-Protocol");  
    return subprotocol !== "mqtt";  
  }  
}  
```  
Connections with protocol messages disabled still fully participate in RPC and regular messaging. Use `isConnectionProtocolEnabled(connection)` to check a connection's status at any time. The flag persists across Durable Object hibernation.  
See [Protocol messages](https://edgetunnel-b2h.pages.dev/agents/runtime/communication/protocol-messages/) for full documentation.  
#### `@cloudflare/ai-chat` v0.1.0  
The first stable release of `@cloudflare/ai-chat` ships alongside this release with a major refactor of `AIChatAgent` internals — new `ResumableStream` class, WebSocket `ChatTransport`, and simplified SSE parsing — with zero breaking changes. Existing code using `AIChatAgent` and `useAgentChat` works as-is.  
Key new features:

  * **Data parts** — Attach typed JSON blobs (`data-*`) to messages alongside text. Supports reconciliation (type+id updates in-place), append, and transient parts (ephemeral via `onData` callback). See [Data parts](https://edgetunnel-b2h.pages.dev/agents/communication-channels/chat/chat-agents/#data-parts).
  * **Tool approval persistence** — The `needsApproval` approval UI now survives page refresh and DO hibernation. The streaming message is persisted to SQLite when a tool enters `approval-requested` state.
  * **`maxPersistedMessages`** — Cap SQLite message storage with automatic oldest-message deletion.
  * **`body` option on `useAgentChat`** — Send custom data with every request (static or dynamic).
  * **Incremental persistence** — Hash-based cache to skip redundant SQL writes.
  * **Row size guard** — Automatic two-pass compaction when messages approach the SQLite 2 MB limit.
  * **`autoContinueAfterToolResult` defaults to `true`** — Client-side tool results and tool approvals now automatically trigger a server continuation, matching server-executed tool behavior. Set `autoContinueAfterToolResult: false` in `useAgentChat` to restore the previous behavior.  
Notable bug fixes:

  * Resolved stream resumption race conditions
  * Resolved an issue where `setMessages` functional updater sent empty arrays
  * Resolved an issue where client tool schemas were lost after DO hibernation
  * Resolved `InvalidPromptError` after tool approval (`approval.id` was dropped)
  * Resolved an issue where message metadata was not propagated on broadcast/resume paths
  * Resolved an issue where `clearAll()` did not clear in-memory chunk buffers
  * Resolved an issue where `reasoning-delta` silently dropped data when `reasoning-start` was missed during stream resumption  
#### Synchronous queue and schedule getters  
`getQueue()`, `getQueues()`, `getSchedule()`, `dequeue()`, `dequeueAll()`, and `dequeueAllByCallback()` were unnecessarily `async` despite only performing synchronous SQL operations. They now return values directly instead of wrapping them in Promises. This is backward compatible — existing code using `await` on these methods will continue to work.  
#### Other improvements

  * **Fix TypeScript "excessively deep" error** — A depth counter on `CanSerialize` and `IsSerializableParam` types bails out to `true` after 10 levels of recursion, preventing the "Type instantiation is excessively deep" error with deeply nested types like AI SDK `CoreMessage[]`.
  * **POST SSE keepalive** — The POST SSE handler now sends `event: ping` every 30 seconds to keep the connection alive, matching the existing GET SSE handler behavior. This prevents POST response streams from being silently dropped by proxies during long-running tool calls.
  * **Widened peer dependency ranges** — Peer dependency ranges across packages have been widened to prevent cascading major bumps during 0.x minor releases. `@cloudflare/ai-chat` and `@cloudflare/codemode` are now marked as optional peer dependencies.  
#### Upgrade  
To update to the latest version:  
```sh  
npm i agents@latest @cloudflare/ai-chat@latest  
```

Feb 13, 2026
1. ### [Introducing GLM-4.7-Flash on Workers AI, @cloudflare/tanstack-ai, and workers-ai-provider v3.1.1](https://edgetunnel-b2h.pages.dev/changelog/post/2026-02-13-glm-47-flash-workers-ai/)  
[ Workers ](https://edgetunnel-b2h.pages.dev/workers/)[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)[ Workers AI ](https://edgetunnel-b2h.pages.dev/workers-ai/)  
We're excited to announce **GLM-4.7-Flash** on Workers AI, a fast and efficient text generation model optimized for multilingual dialogue and instruction-following tasks, along with the brand-new [**@cloudflare/tanstack-ai** ↗](https://www.npmjs.com/package/@cloudflare/tanstack-ai) package and [**workers-ai-provider v3.1.1** ↗](https://www.npmjs.com/package/workers-ai-provider).  
You can now run AI agents entirely on Cloudflare. With GLM-4.7-Flash's multi-turn tool calling support, plus full compatibility with TanStack AI and the Vercel AI SDK, you have everything you need to build agentic applications that run completely at the edge.  
#### GLM-4.7-Flash — Multilingual Text Generation Model  
[@cf/zai-org/glm-4.7-flash](https://edgetunnel-b2h.pages.dev/workers-ai/models/glm-4.7-flash/) is a multilingual model with a 131,072 token context window, making it ideal for long-form content generation, complex reasoning tasks, and multilingual applications.

**Key Features and Use Cases:**

  * **Multi-turn Tool Calling for Agents**: Build AI agents that can call functions and tools across multiple conversation turns
  * **Multilingual Support**: Built to handle content generation in multiple languages effectively
  * **Large Context Window**: 131,072 tokens for long-form writing, complex reasoning, and processing long documents
  * **Fast Inference**: Optimized for low-latency responses in chatbots and virtual assistants
  * **Instruction Following**: Excellent at following complex instructions for code generation and structured tasks  
Use GLM-4.7-Flash through the [Workers AI binding](https://edgetunnel-b2h.pages.dev/workers-ai/configuration/bindings/) (`env.AI.run()`), the REST API at `/run` or `/v1/chat/completions`, [AI Gateway](https://edgetunnel-b2h.pages.dev/ai-gateway/), or via [workers-ai-provider](https://edgetunnel-b2h.pages.dev/workers-ai/configuration/ai-sdk/) for the Vercel AI SDK.  
Pricing is available on the [model page](https://edgetunnel-b2h.pages.dev/workers-ai/models/glm-4.7-flash/) or [pricing page](https://edgetunnel-b2h.pages.dev/workers-ai/platform/pricing/).  
#### @cloudflare/tanstack-ai v0.1.1 — TanStack AI adapters for Workers AI and AI Gateway  
We've released `@cloudflare/tanstack-ai`, a new package that brings Workers AI and AI Gateway support to [TanStack AI ↗](https://tanstack.com/ai). This provides a framework-agnostic alternative for developers who prefer TanStack's approach to building AI applications.

**Workers AI adapters** support four configuration modes — plain binding (`env.AI`), plain REST, AI Gateway binding (`env.AI.gateway(id)`), and AI Gateway REST — across all capabilities:

  * **Chat** (`createWorkersAiChat`) — Streaming chat completions with tool calling, structured output, and reasoning text streaming.
  * **Image generation** (`createWorkersAiImage`) — Text-to-image models.
  * **Transcription** (`createWorkersAiTranscription`) — Speech-to-text.
  * **Text-to-speech** (`createWorkersAiTts`) — Audio generation.
  * **Summarization** (`createWorkersAiSummarize`) — Text summarization.

**AI Gateway adapters** route requests from third-party providers — OpenAI, Anthropic, Gemini, Grok, and OpenRouter — through Cloudflare AI Gateway for caching, rate limiting, and unified billing.  
To get started:  
```sh  
npm install @cloudflare/tanstack-ai @tanstack/ai  
```  
#### workers-ai-provider v3.1.1 — transcription, speech, reranking, and reliability  
The Workers AI provider for the [Vercel AI SDK ↗](https://ai-sdk.dev) now supports three new capabilities beyond chat and image generation:

  * **Transcription** (`provider.transcription(model)`) — Speech-to-text with automatic handling of model-specific input formats across binding and REST paths.
  * **Text-to-speech** (`provider.speech(model)`) — Audio generation with support for voice and speed options.
  * **Reranking** (`provider.reranking(model)`) — Document reranking for RAG pipelines and search result ordering.

**TypeScript**  
```typescript  
import { createWorkersAI } from "workers-ai-provider";  
import {  
  experimental_transcribe,  
  experimental_generateSpeech,  
  rerank,  
} from "ai";  
const workersai = createWorkersAI({ binding: env.AI });  
const transcript = await experimental_transcribe({  
  model: workersai.transcription("@cf/openai/whisper-large-v3-turbo"),  
  audio: audioData,  
  mediaType: "audio/wav",  
});  
const speech = await experimental_generateSpeech({  
  model: workersai.speech("@cf/deepgram/aura-1"),  
  text: "Hello world",  
  voice: "asteria",  
});  
const ranked = await rerank({  
  model: workersai.reranking("@cf/baai/bge-reranker-base"),  
  query: "What is machine learning?",  
  documents: ["ML is a branch of AI.", "The weather is sunny."],  
});  
```  
This release also includes a comprehensive reliability overhaul (v3.0.5):

  * **Fixed streaming** — Responses now stream token-by-token instead of buffering all chunks, using a proper `TransformStream` pipeline with backpressure.
  * **Fixed tool calling** — Resolved issues with tool call ID sanitization, conversation history preservation, and a heuristic that silently fell back to non-streaming mode when tools were defined.
  * **Premature stream termination detection** — Streams that end unexpectedly now report `finishReason: "error"` instead of silently reporting `"stop"`.
  * **AI Search support** — Added `createAISearch` as the canonical export (renamed from AutoRAG). `createAutoRAG` still works with a deprecation warning.  
To upgrade:  
```sh  
npm install workers-ai-provider@latest ai  
```  
#### Resources

  * [@cloudflare/tanstack-ai on npm ↗](https://www.npmjs.com/package/@cloudflare/tanstack-ai)
  * [workers-ai-provider on npm ↗](https://www.npmjs.com/package/workers-ai-provider)
  * [GitHub repository ↗](https://github.com/cloudflare/ai)

Feb 09, 2026
1. ### [Agents SDK v0.4.0: Readonly connections, MCP security improvements, x402 v2 migration, and custom MCP OAuth providers](https://edgetunnel-b2h.pages.dev/changelog/post/2026-02-09-agents-sdk-v040/)  
[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)[ Workers ](https://edgetunnel-b2h.pages.dev/workers/)  
The latest release of the [Agents SDK ↗](https://github.com/cloudflare/agents) brings readonly connections, MCP protocol and security improvements, x402 payment protocol v2 migration, and the ability to customize OAuth for MCP server connections.  
#### Readonly connections  
Agents can now restrict WebSocket clients to read-only access, preventing them from modifying agent state. This is useful for dashboards, spectator views, or any scenario where clients should observe but not mutate.  
New hooks: `shouldConnectionBeReadonly`, `setConnectionReadonly`, `isConnectionReadonly`. Readonly connections block both client-side `setState()` and mutating `@callable()` methods, and the readonly flag survives hibernation.

  * [  JavaScript ](#tab-panel-3221)
  * [  TypeScript ](#tab-panel-3222)

**JavaScript**  
```js  
class MyAgent extends Agent {  
  shouldConnectionBeReadonly(connection) {  
    // Make spectators readonly  
    return connection.url.includes("spectator");  
  }  
}  
```

**TypeScript**  
```ts  
class MyAgent extends Agent {  
  shouldConnectionBeReadonly(connection) {  
    // Make spectators readonly  
    return connection.url.includes("spectator");  
  }  
}  
```  
#### Custom MCP OAuth providers  
The new `createMcpOAuthProvider` method on the `Agent` class allows subclasses to override the default OAuth provider used when connecting to MCP servers. This enables custom authentication strategies such as pre-registered client credentials or mTLS, beyond the built-in dynamic client registration.

  * [  JavaScript ](#tab-panel-3225)
  * [  TypeScript ](#tab-panel-3226)

**JavaScript**  
```js  
class MyAgent extends Agent {  
  createMcpOAuthProvider(callbackUrl) {  
    return new MyCustomOAuthProvider(this.ctx.storage, this.name, callbackUrl);  
  }  
}  
```

**TypeScript**  
```ts  
class MyAgent extends Agent {  
  createMcpOAuthProvider(callbackUrl: string): AgentMcpOAuthProvider {  
    return new MyCustomOAuthProvider(this.ctx.storage, this.name, callbackUrl);  
  }  
}  
```  
#### MCP SDK upgrade to 1.26.0  
Upgraded the MCP SDK to 1.26.0 to prevent cross-client response leakage. Stateless MCP Servers should now create a new `McpServer` instance per request instead of sharing a single instance. A guard is added in this version of the MCP SDK which will prevent connection to a Server instance that has already been connected to a transport. Developers will need to modify their code if they declare their `McpServer` instance as a global variable.  
#### MCP OAuth callback URL security fix  
Added `callbackPath` option to `addMcpServer` to prevent instance name leakage in MCP OAuth callback URLs. When `sendIdentityOnConnect` is `false`, `callbackPath` is now required — the default callback URL would expose the instance name, undermining the security intent. Also fixes callback request detection to match via the `state` parameter instead of a loose `/callback` URL substring check, enabling custom callback paths.  
#### Deprecate `onStateUpdate` in favor of `onStateChanged`  
`onStateChanged` is a drop-in rename of `onStateUpdate` (same signature, same behavior). `onStateUpdate` still works but emits a one-time console warning per class. `validateStateChange` rejections now propagate a `CF_AGENT_STATE_ERROR` message back to the client.  
#### x402 v2 migration  
Migrated the x402 MCP payment integration from the legacy `x402` package to `@x402/core` and `@x402/evm` v2.

**Breaking changes for x402 users:**

  * Peer dependencies changed: replace `x402` with `@x402/core` and `@x402/evm`
  * `PaymentRequirements` type now uses v2 fields (e.g. `amount` instead of `maxAmountRequired`)
  * `X402ClientConfig.account` type changed from `viem.Account` to `ClientEvmSigner` (structurally compatible with `privateKeyToAccount()`)  
```bash  
npm uninstall x402  
npm install @x402/core @x402/evm  
```  
Network identifiers now accept both legacy names and CAIP-2 format:

**TypeScript**  
```ts  
// Legacy name (auto-converted)  
{  
  network: "base-sepolia",  
}  
// CAIP-2 format (preferred)  
{  
  network: "eip155:84532",  
}  
```

**Other x402 changes:**

  * `X402ClientConfig.network` is now optional — the client auto-selects from available payment requirements
  * Server-side lazy initialization: facilitator connection is deferred until the first paid tool invocation
  * Payment tokens support both v2 (`PAYMENT-SIGNATURE`) and v1 (`X-PAYMENT`) HTTP headers
  * Added `normalizeNetwork` export for converting legacy network names to CAIP-2 format
  * Re-exports `PaymentRequirements`, `PaymentRequired`, `Network`, `FacilitatorConfig`, and `ClientEvmSigner` from `agents/x402`  
#### Other improvements

  * Fix `useAgent` and `AgentClient` crashing when using `basePath` routing
  * CORS handling delegated to partyserver's native support (simpler, more reliable)
  * Client-side `onStateUpdateError` callback for handling rejected state updates  
#### Upgrade  
To update to the latest version:  
```sh  
npm i agents@latest  
```

Feb 09, 2026
1. ### [Interactive browser terminals in Sandboxes](https://edgetunnel-b2h.pages.dev/changelog/post/2026-02-09-pty-terminal-support/)  
[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)  
The [Sandbox SDK ↗](https://github.com/cloudflare/sandbox-sdk) now supports PTY (pseudo-terminal) passthrough, enabling browser-based terminal UIs to connect to sandbox shells via WebSocket.  
#### `sandbox.terminal(request)`  
The new `terminal()` method proxies a WebSocket upgrade to the container's PTY endpoint, with output buffering for replay on reconnect.

  * [  JavaScript ](#tab-panel-3223)
  * [  TypeScript ](#tab-panel-3224)

**JavaScript**  
```js  
// Worker: proxy WebSocket to container terminal  
return sandbox.terminal(request, { cols: 80, rows: 24 });  
```

**TypeScript**  
```ts  
// Worker: proxy WebSocket to container terminal  
return sandbox.terminal(request, { cols: 80, rows: 24 });  
```  
#### Multiple terminals per sandbox  
Each session can have its own terminal with an isolated working directory and environment, so users can run separate shells side-by-side in the same container.

  * [  JavaScript ](#tab-panel-3227)
  * [  TypeScript ](#tab-panel-3228)

**JavaScript**  
```js  
// Multiple isolated terminals in the same sandbox  
const dev = await sandbox.getSession("dev");  
return dev.terminal(request);  
```

**TypeScript**  
```ts  
// Multiple isolated terminals in the same sandbox  
const dev = await sandbox.getSession("dev");  
return dev.terminal(request);  
```  
#### xterm.js addon  
The new `@cloudflare/sandbox/xterm` export provides a `SandboxAddon` for [xterm.js ↗](https://xtermjs.org/) with automatic reconnection (exponential backoff + jitter), buffered output replay, and resize forwarding.

  * [  JavaScript ](#tab-panel-3231)
  * [  TypeScript ](#tab-panel-3232)

**JavaScript**  
```js  
import { SandboxAddon } from "@cloudflare/sandbox/xterm";  
const addon = new SandboxAddon({  
  getWebSocketUrl: ({ sandboxId, origin }) =>  
    `${origin}/ws/terminal?id=${sandboxId}`,  
  onStateChange: (state, error) => updateUI(state),  
});  
terminal.loadAddon(addon);  
addon.connect({ sandboxId: "my-sandbox" });  
```

**TypeScript**  
```ts  
import { SandboxAddon } from "@cloudflare/sandbox/xterm";  
const addon = new SandboxAddon({  
  getWebSocketUrl: ({ sandboxId, origin }) =>  
    `${origin}/ws/terminal?id=${sandboxId}`,  
  onStateChange: (state, error) => updateUI(state),  
});  
terminal.loadAddon(addon);  
addon.connect({ sandboxId: "my-sandbox" });  
```  
#### Upgrade  
To update to the latest version:  
```sh  
npm i @cloudflare/sandbox@latest  
```

Feb 03, 2026
1. ### [Agents SDK v0.3.7: Workflows integration, synchronous state, and scheduleEvery()](https://edgetunnel-b2h.pages.dev/changelog/post/2026-02-03-agents-workflows-integration/)  
[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)[ Workflows ](https://edgetunnel-b2h.pages.dev/workflows/)  
The latest release of the [Agents SDK ↗](https://github.com/cloudflare/agents) brings first-class support for [Cloudflare Workflows](https://edgetunnel-b2h.pages.dev/workflows/), synchronous state management, and new scheduling capabilities.  
#### Cloudflare Workflows integration  
Agents excel at real-time communication and state management. Workflows excel at durable execution. Together, they enable powerful patterns where Agents handle WebSocket connections while Workflows handle long-running tasks, retries, and human-in-the-loop flows.  
Use the new `AgentWorkflow` class to define workflows with typed access to your Agent:

  * [  JavaScript ](#tab-panel-3241)
  * [  TypeScript ](#tab-panel-3242)

**JavaScript**  
```js  
import { AgentWorkflow } from "agents/workflows";  
export class ProcessingWorkflow extends AgentWorkflow {  
  async run(event, step) {  
    // Call Agent methods via RPC  
    await this.agent.updateStatus(event.payload.taskId, "processing");  
    // Non-durable: progress reporting to clients  
    await this.reportProgress({ step: "process", percent: 0.5 });  
    this.broadcastToClients({ type: "update", taskId: event.payload.taskId });  
    // Durable via step: idempotent, won't repeat on retry  
    await step.mergeAgentState({ taskProgress: 0.5 });  
    const result = await step.do("process", async () => {  
      return processData(event.payload.data);  
    });  
    await step.reportComplete(result);  
    return result;  
  }  
}  
```

**TypeScript**  
```ts  
import { AgentWorkflow } from "agents/workflows";  
import type { AgentWorkflowEvent, AgentWorkflowStep } from "agents/workflows";  
export class ProcessingWorkflow extends AgentWorkflow<MyAgent, TaskParams> {  
  async run(event: AgentWorkflowEvent<TaskParams>, step: AgentWorkflowStep) {  
    // Call Agent methods via RPC  
    await this.agent.updateStatus(event.payload.taskId, "processing");  
    // Non-durable: progress reporting to clients  
    await this.reportProgress({ step: "process", percent: 0.5 });  
    this.broadcastToClients({ type: "update", taskId: event.payload.taskId });  
    // Durable via step: idempotent, won't repeat on retry  
    await step.mergeAgentState({ taskProgress: 0.5 });  
    const result = await step.do("process", async () => {  
      return processData(event.payload.data);  
    });  
    await step.reportComplete(result);  
    return result;  
  }  
}  
```  
Start workflows from your Agent with `runWorkflow()` and handle lifecycle events:

  * [  JavaScript ](#tab-panel-3243)
  * [  TypeScript ](#tab-panel-3244)

**JavaScript**  
```js  
export class MyAgent extends Agent {  
  async startTask(taskId, data) {  
    const instanceId = await this.runWorkflow("PROCESSING_WORKFLOW", {  
      taskId,  
      data,  
    });  
    return { instanceId };  
  }  
  async onWorkflowProgress(workflowName, instanceId, progress) {  
    this.broadcast(JSON.stringify({ type: "progress", progress }));  
  }  
  async onWorkflowComplete(workflowName, instanceId, result) {  
    console.log(`Workflow ${instanceId} completed`);  
  }  
  async onWorkflowError(workflowName, instanceId, error) {  
    console.error(`Workflow ${instanceId} failed:`, error);  
  }  
}  
```

**TypeScript**  
```ts  
export class MyAgent extends Agent {  
  async startTask(taskId: string, data: string) {  
    const instanceId = await this.runWorkflow("PROCESSING_WORKFLOW", {  
      taskId,  
      data,  
    });  
    return { instanceId };  
  }  
  async onWorkflowProgress(  
    workflowName: string,  
    instanceId: string,  
    progress: unknown,  
  ) {  
    this.broadcast(JSON.stringify({ type: "progress", progress }));  
  }  
  async onWorkflowComplete(  
    workflowName: string,  
    instanceId: string,  
    result?: unknown,  
  ) {  
    console.log(`Workflow ${instanceId} completed`);  
  }  
  async onWorkflowError(  
    workflowName: string,  
    instanceId: string,  
    error: unknown,  
  ) {  
    console.error(`Workflow ${instanceId} failed:`, error);  
  }  
}  
```  
Key workflow methods on your Agent:

  * `runWorkflow(workflowName, params, options?)` — Start a workflow with optional metadata
  * `getWorkflow(workflowId)` / `getWorkflows(criteria?)` — Query workflows with cursor-based pagination
  * `approveWorkflow(workflowId)` / `rejectWorkflow(workflowId)` — Human-in-the-loop approval flows
  * `pauseWorkflow()`, `resumeWorkflow()`, `terminateWorkflow()` — Workflow control  
#### Synchronous setState()  
State updates are now synchronous with a new `validateStateChange()` validation hook:

  * [  JavaScript ](#tab-panel-3235)
  * [  TypeScript ](#tab-panel-3236)

**JavaScript**  
```js  
export class MyAgent extends Agent {  
  validateStateChange(oldState, newState) {  
    // Return false to reject the change  
    if (newState.count < 0) return false;  
    // Return modified state to transform  
    return { ...newState, lastUpdated: Date.now() };  
  }  
}  
```

**TypeScript**  
```ts  
export class MyAgent extends Agent<Env, State> {  
  validateStateChange(oldState: State, newState: State): State | false {  
    // Return false to reject the change  
    if (newState.count < 0) return false;  
    // Return modified state to transform  
    return { ...newState, lastUpdated: Date.now() };  
  }  
}  
```  
#### scheduleEvery() for recurring tasks  
The new `scheduleEvery()` method enables fixed-interval recurring tasks with built-in overlap prevention:

  * [  JavaScript ](#tab-panel-3233)
  * [  TypeScript ](#tab-panel-3234)

**JavaScript**  
```js  
// Run every 5 minutes  
await this.scheduleEvery("syncData", 5 * 60 * 1000, { source: "api" });  
```

**TypeScript**  
```ts  
// Run every 5 minutes  
await this.scheduleEvery("syncData", 5 * 60 * 1000, { source: "api" });  
```  
#### Callable system improvements

  * **Client-side RPC timeout** — Set timeouts on callable method invocations
  * **`StreamingResponse.error(message)`** — Graceful stream error signaling
  * **`getCallableMethods()`** — Introspection API for discovering callable methods
  * **Connection close handling** — Pending calls are automatically rejected on disconnect

  * [  JavaScript ](#tab-panel-3237)
  * [  TypeScript ](#tab-panel-3238)

**JavaScript**  
```js  
await agent.call("method", [args], {  
  timeout: 5000,  
  stream: { onChunk, onDone, onError },  
});  
```

**TypeScript**  
```ts  
await agent.call("method", [args], {  
  timeout: 5000,  
  stream: { onChunk, onDone, onError },  
});  
```  
#### Email and routing enhancements

**Secure email reply routing** — Email replies are now secured with HMAC-SHA256 signed headers, preventing unauthorized routing of emails to agent instances.

**Routing improvements:**

  * `basePath` option to bypass default URL construction for custom routing
  * Server-sent identity — Agents send `name` and `agent` type on connect
  * New `onIdentity` and `onIdentityChange` callbacks on the client

  * [  JavaScript ](#tab-panel-3239)
  * [  TypeScript ](#tab-panel-3240)

**JavaScript**  
```js  
const agent = useAgent({  
  basePath: "user",  
  onIdentity: (name, agentType) => console.log(`Connected to ${name}`),  
});  
```

**TypeScript**  
```ts  
const agent = useAgent({  
  basePath: "user",  
  onIdentity: (name, agentType) => console.log(`Connected to ${name}`),  
});  
```  
#### Upgrade  
To update to the latest version:  
```sh  
npm i agents@latest  
```  
For the complete Workflows API reference and patterns, see [Run Workflows](https://edgetunnel-b2h.pages.dev/agents/runtime/execution/run-workflows/).

Dec 22, 2025
1. ### [Agents SDK v0.3.0, workers-ai-provider v3.0.0, and ai-gateway-provider v3.0.0 with AI SDK v6 support](https://edgetunnel-b2h.pages.dev/changelog/post/2025-12-22-agents-sdk-ai-sdk-v6/)  
[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)[ Workers ](https://edgetunnel-b2h.pages.dev/workers/)  
We've shipped a new release for the [Agents SDK ↗](https://github.com/cloudflare/agents) v0.3.0 bringing full compatibility with [AI SDK v6 ↗](https://ai-sdk.dev/docs/introduction) and introducing the unified tool pattern, dynamic tool approval, and enhanced React hooks with improved tool handling.  
This release includes improved streaming and tool support, dynamic tool approval (for "human in the loop" systems), enhanced React hooks with `onToolCall` callback, improved error handling for streaming responses, and seamless migration from v5 patterns.  
This makes it ideal for building production AI chat interfaces with Cloudflare Workers AI models, agent workflows, human-in-the-loop systems, or any application requiring reliable tool execution and approval workflows.  
Additionally, we've updated **workers-ai-provider v3.0.0**, the official provider for Cloudflare Workers AI models, and **ai-gateway-provider v3.0.0**, the provider for Cloudflare AI Gateway, to be compatible with AI SDK v6.  
#### Agents SDK v0.3.0  
#### Unified Tool Pattern  
AI SDK v6 introduces a unified tool pattern where all tools are defined on the server using the `tool()` function. This replaces the previous client-side `AITool` pattern.  
#### Server-Side Tool Definition

**TypeScript**  
```ts  
import { tool } from "ai";  
import { z } from "zod";  
// Server: Define ALL tools on the server  
const tools = {  
  // Server-executed tool  
  getWeather: tool({  
    description: "Get weather for a city",  
    inputSchema: z.object({ city: z.string() }),  
    execute: async ({ city }) => fetchWeather(city)  
  }),  
  // Client-executed tool (no execute = client handles via onToolCall)  
  getLocation: tool({  
    description: "Get user location from browser",  
    inputSchema: z.object({})  
    // No execute function  
  }),  
  // Tool requiring approval (dynamic based on input)  
  processPayment: tool({  
    description: "Process a payment",  
    inputSchema: z.object({ amount: z.number() }),  
    needsApproval: async ({ amount }) => amount > 100,  
    execute: async ({ amount }) => charge(amount)  
  })  
};  
```  
#### Client-Side Tool Handling

**TypeScript**  
```ts  
// Client: Handle client-side tools via onToolCall callback  
import { useAgentChat } from "agents/ai-react";  
const { messages, sendMessage, addToolOutput } = useAgentChat({  
  agent,  
  onToolCall: async ({ toolCall, addToolOutput }) => {  
    if (toolCall.toolName === "getLocation") {  
      const position = await new Promise((resolve, reject) => {  
        navigator.geolocation.getCurrentPosition(resolve, reject);  
      });  
      addToolOutput({  
        toolCallId: toolCall.toolCallId,  
        output: {  
          lat: position.coords.latitude,  
          lng: position.coords.longitude  
        }  
      });  
    }  
  }  
});  
```

**Key benefits of the unified tool pattern:**

  * **Server-defined tools**: All tools are defined in one place on the server
  * **Dynamic approval**: Use `needsApproval` to conditionally require user confirmation
  * **Cleaner client code**: Use `onToolCall` callback instead of managing tool configs
  * **Type safety**: Full TypeScript support with proper tool typing  
#### useAgentChat(options)  
Creates a new chat interface with enhanced v6 capabilities.

**TypeScript**  
```ts  
// Basic chat setup with onToolCall  
const { messages, sendMessage, addToolOutput } = useAgentChat({  
  agent,  
  onToolCall: async ({ toolCall, addToolOutput }) => {  
    // Handle client-side tool execution  
    await addToolOutput({  
      toolCallId: toolCall.toolCallId,  
      output: { result: "success" }  
    });  
  }  
});  
```  
#### Dynamic Tool Approval  
Use `needsApproval` on server tools to conditionally require user confirmation:

**TypeScript**  
```ts  
const paymentTool = tool({  
  description: "Process a payment",  
  inputSchema: z.object({  
    amount: z.number(),  
    recipient: z.string()  
  }),  
  needsApproval: async ({ amount }) => amount > 1000,  
  execute: async ({ amount, recipient }) => {  
    return await processPayment(amount, recipient);  
  }  
});  
```  
#### Tool Confirmation Detection  
The `isToolUIPart` and `getToolName` functions now check both static and dynamic tool parts:

**TypeScript**  
```ts  
import { isToolUIPart, getToolName } from "ai";  
const pendingToolCallConfirmation = messages.some((m) =>  
  m.parts?.some(  
    (part) => isToolUIPart(part) && part.state === "input-available",  
  ),  
);  
// Handle tool confirmation  
if (pendingToolCallConfirmation) {  
  await addToolOutput({  
    toolCallId: part.toolCallId,  
    output: "User approved the action"  
  });  
}  
```  
If you need the v5 behavior (static-only checks), use the new functions:

**TypeScript**  
```ts  
import { isStaticToolUIPart, getStaticToolName } from "ai";  
```  
#### convertToModelMessages() is now async  
The `convertToModelMessages()` function is now asynchronous. Update all calls to await the result:

**TypeScript**  
```ts  
import { convertToModelMessages } from "ai";  
const result = streamText({  
  messages: await convertToModelMessages(this.messages),  
  model: openai("gpt-4o")  
});  
```  
#### ModelMessage type  
The `CoreMessage` type has been removed. Use `ModelMessage` instead:

**TypeScript**  
```ts  
import { convertToModelMessages, type ModelMessage } from "ai";  
const modelMessages: ModelMessage[] = await convertToModelMessages(messages);  
```  
#### generateObject mode option removed  
The `mode` option for `generateObject` has been removed:

**TypeScript**  
```ts  
// Before (v5)  
const result = await generateObject({  
  mode: "json",  
  model,  
  schema,  
  prompt  
});  
// After (v6)  
const result = await generateObject({  
  model,  
  schema,  
  prompt  
});  
```  
#### Structured Output with generateText  
While `generateObject` and `streamObject` are still functional, the recommended approach is to use `generateText`/`streamText` with the `Output.object()` helper:

**TypeScript**  
```ts  
import { generateText, Output, stepCountIs } from "ai";  
const { output } = await generateText({  
  model: openai("gpt-4"),  
  output: Output.object({  
    schema: z.object({ name: z.string() })  
  }),  
  stopWhen: stepCountIs(2),  
  prompt: "Generate a name"  
});  
```  
> **Note**: When using structured output with `generateText`, you must configure multiple steps with `stopWhen` because generating the structured output is itself a step.  
#### workers-ai-provider v3.0.0  
Seamless integration with Cloudflare Workers AI models through the updated workers-ai-provider v3.0.0 with AI SDK v6 support.  
#### Model Setup with Workers AI  
Use Cloudflare Workers AI models directly in your agent workflows:

**TypeScript**  
```ts  
import { createWorkersAI } from "workers-ai-provider";  
import { useAgentChat } from "agents/ai-react";  
// Create Workers AI model (v3.0.0 - enhanced v6 internals)  
const model = createWorkersAI({  
  binding: env.AI,  
})("@cf/meta/llama-3.2-3b-instruct");  
```  
#### Enhanced File and Image Support  
Workers AI models now support v6 file handling with automatic conversion:

**TypeScript**  
```ts  
// Send images and files to Workers AI models  
sendMessage({  
  role: "user",  
  parts: [  
    { type: "text", text: "Analyze this image:" },  
    {  
      type: "file",  
      data: imageBuffer,  
      mediaType: "image/jpeg",  
    },  
  ],  
});  
// Workers AI provider automatically converts to proper format  
```  
#### Streaming with Workers AI  
Enhanced streaming support with automatic warning detection:

**TypeScript**  
```ts  
// Streaming with Workers AI models  
const result = await streamText({  
  model: createWorkersAI({ binding: env.AI })("@cf/meta/llama-3.2-3b-instruct"),  
  messages: await convertToModelMessages(messages),  
  onChunk: (chunk) => {  
    // Enhanced streaming with warning handling  
    console.log(chunk);  
  },  
});  
```  
#### ai-gateway-provider v3.0.0  
The ai-gateway-provider v3.0.0 now supports AI SDK v6, enabling you to use Cloudflare AI Gateway with multiple AI providers including Anthropic, Azure, AWS Bedrock, Google Vertex, and Perplexity.  
#### AI Gateway Setup  
Use Cloudflare AI Gateway to add analytics, caching, and rate limiting to your AI applications:

**TypeScript**  
```ts  
import { createAIGateway } from "ai-gateway-provider";  
// Create AI Gateway provider (v3.0.0 - enhanced v6 internals)  
const model = createAIGateway({  
  gatewayUrl: "https://gateway.ai.cloudflare.com/v1/your-account-id/gateway",  
  headers: {  
    "Authorization": `Bearer ${env.AI_GATEWAY_TOKEN}`  
  }  
})({  
  provider: "openai",  
  model: "gpt-4o"  
});  
```  
#### Migration from v5  
#### Deprecated APIs  
The following APIs are deprecated in favor of the unified tool pattern:

| Deprecated                            | Replacement                                      |
| ------------------------------------- | ------------------------------------------------ |
| AITool type                           | Use AI SDK's tool() function on server           |
| extractClientToolSchemas()            | Define tools on server, no client schemas needed |
| createToolsFromClientSchemas()        | Define tools on server with tool()               |
| toolsRequiringConfirmation option     | Use needsApproval on server tools                |
| experimental\_automaticToolResolution | Use onToolCall callback                          |
| tools option in useAgentChat          | Use onToolCall for client-side execution         |
| addToolResult()                       | Use addToolOutput()                              |  
#### Breaking Changes Summary

  1. **Unified Tool Pattern**: All tools must be defined on the server using `tool()`
  2. **`convertToModelMessages()` is async**: Add `await` to all calls
  3. **`CoreMessage` removed**: Use `ModelMessage` instead
  4. **`generateObject` mode removed**: Remove `mode` option
  5. **`isToolUIPart` behavior changed**: Now checks both static and dynamic tool parts  
#### Installation  
Update your dependencies to use the latest versions:  
```bash  
npm install agents@^0.3.0 workers-ai-provider@^3.0.0 ai-gateway-provider@^3.0.0 ai@^6.0.0 @ai-sdk/react@^3.0.0 @ai-sdk/openai@^3.0.0  
```  
#### Resources

  * [Migration Guide ↗](https://github.com/cloudflare/agents/blob/main/docs/migration-to-ai-sdk-v6.md) \- Comprehensive migration documentation from v5 to v6
  * [AI SDK v6 Documentation ↗](https://ai-sdk.dev/docs/migration-guides/migration-guide-6-0) \- Official AI SDK migration guide
  * [AI SDK v6 Announcement ↗](https://vercel.com/blog/ai-sdk-6) \- Learn about new features in v6
  * [AI SDK Documentation ↗](https://sdk.vercel.ai/docs) \- Complete AI SDK reference
  * [GitHub Issues ↗](https://github.com/cloudflare/agents/issues) \- Report bugs or request features  
#### Feedback Welcome  
We'd love your feedback! We're particularly interested in feedback on:

  * **Migration experience** \- How smooth was the upgrade from v5 to v6?
  * **Unified tool pattern** \- How does the new server-defined tool pattern work for you?
  * **Dynamic tool approval** \- Does the `needsApproval` feature meet your needs?
  * **AI Gateway integration** \- How well does the new provider work with your setup?

Nov 26, 2025
1. ### [Agents SDK v0.2.24 with resumable streaming, MCP improvements, and schedule fixes](https://edgetunnel-b2h.pages.dev/changelog/post/2025-11-26-agents-resumable-streaming/)  
[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)[ Workers ](https://edgetunnel-b2h.pages.dev/workers/)  
The latest release of [@cloudflare/agents ↗](https://github.com/cloudflare/agents) brings resumable streaming, significant MCP client improvements, and critical fixes for schedules and Durable Object lifecycle management.  
#### Resumable streaming  
`AIChatAgent` now supports resumable streaming, allowing clients to reconnect and continue receiving streamed responses without losing data. This is useful for:

  * Long-running AI responses
  * Users on unreliable networks
  * Users switching between devices mid-conversation
  * Background tasks where users navigate away and return
  * Real-time collaboration where multiple clients need to stay in sync  
Streams are maintained across page refreshes, broken connections, and syncing across open tabs and devices.  
#### Other improvements

  * Default JSON schema validator added to MCP client
  * [Schedules ↗](https://edgetunnel-b2h.pages.dev/agents/runtime/execution/schedule-tasks/) can now safely destroy the agent  
#### MCP client API improvements  
The `MCPClientManager` API has been redesigned for better clarity and control:

  * **New `registerServer()` method**: Register MCP servers without immediately connecting
  * **New `connectToServer()` method**: Establish connections to registered servers
  * **Improved reconnect logic**: `restoreConnectionsFromStorage()` now properly handles failed connections

**TypeScript**  
```ts  
// Register a server to Agent  
const { id } = await this.mcp.registerServer({  
  name: "my-server",  
  url: "https://my-mcp-server.example.com",  
});  
// Connect when ready  
await this.mcp.connectToServer(id);  
// Discover tools, prompts and resources  
await this.mcp.discoverIfConnected(id);  
```  
The SDK now includes a formalized `MCPConnectionState` enum with states: `idle`, `connecting`, `authenticating`, `connected`, `discovering`, and `ready`.  
#### Enhanced MCP discovery  
MCP discovery fetches the available tools, prompts, and resources from an MCP server so your agent knows what capabilities are available. The `MCPClientConnection` class now includes a dedicated `discover()` method with improved reliability:

  * Supports cancellation via AbortController
  * Configurable timeout (default 15s)
  * Discovery failures now throw errors immediately instead of silently continuing  
#### Bug fixes

  * Fixed a bug where [schedules ↗](https://edgetunnel-b2h.pages.dev/agents/runtime/execution/schedule-tasks/) meant to fire immediately with this.schedule(0, ...) or `this.schedule(new Date(), ...)` would not fire
  * Fixed an issue where schedules that took longer than 30 seconds would occasionally time out
  * Fixed SSE transport now properly forwards session IDs and request headers
  * Fixed AI SDK stream events conversion to UIMessageStreamPart  
#### Upgrade  
To update to the latest version:  
```sh  
npm i agents@latest  
```

Sep 10, 2025
1. ### [Agents SDK v0.1.0 and workers-ai-provider v2.0.0 with AI SDK v5 support](https://edgetunnel-b2h.pages.dev/changelog/post/2025-09-03-agents-sdk-beta-v5/)  
[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)[ Workers ](https://edgetunnel-b2h.pages.dev/workers/)  
We've shipped a new release for the [Agents SDK ↗](https://github.com/cloudflare/agents) bringing full compatibility with [AI SDK v5 ↗](https://ai-sdk.dev/docs/introduction) and introducing automatic message migration that handles all legacy formats transparently.  
This release includes improved streaming and tool support, tool confirmation detection (for "human in the loop" systems), enhanced React hooks with automatic tool resolution, improved error handling for streaming responses, and seamless migration utilities that work behind the scenes.  
This makes it ideal for building production AI chat interfaces with Cloudflare Workers AI models, agent workflows, human-in-the-loop systems, or any application requiring reliable message handling across SDK versions — all while maintaining backward compatibility.  
Additionally, we've updated workers-ai-provider v2.0.0, the official provider for Cloudflare Workers AI models, to be compatible with AI SDK v5.  
#### useAgentChat(options)  
Creates a new chat interface with enhanced v5 capabilities.

**TypeScript**  
```ts  
// Basic chat setup  
const { messages, sendMessage, addToolResult } = useAgentChat({  
  agent,  
  experimental_automaticToolResolution: true,  
  tools,  
});  
// With custom tool confirmation  
const chat = useAgentChat({  
  agent,  
  experimental_automaticToolResolution: true,  
  toolsRequiringConfirmation: ["dangerousOperation"],  
});  
```  
#### Automatic Tool Resolution  
Tools are automatically categorized based on their configuration:

**TypeScript**  
```ts  
const tools = {  
  // Auto-executes (has execute function)  
  getLocalTime: {  
    description: "Get current local time",  
    inputSchema: z.object({}),  
    execute: async () => new Date().toLocaleString(),  
  },  
  // Requires confirmation (no execute function)  
  deleteFile: {  
    description: "Delete a file from the system",  
    inputSchema: z.object({  
      filename: z.string(),  
    }),  
  },  
  // Server-executed (no client confirmation)  
  analyzeData: {  
    description: "Analyze dataset on server",  
    inputSchema: z.object({ data: z.array(z.number()) }),  
    serverExecuted: true,  
  },  
} satisfies Record<string, AITool>;  
```  
#### Message Handling  
Send messages using the new v5 format with parts array:

**TypeScript**  
```ts  
// Text message  
sendMessage({  
  role: "user",  
  parts: [{ type: "text", text: "Hello, assistant!" }],  
});  
// Multi-part message with file  
sendMessage({  
  role: "user",  
  parts: [  
    { type: "text", text: "Analyze this image:" },  
    { type: "image", image: imageData },  
  ],  
});  
```  
#### Tool Confirmation Detection  
Simplified logic for detecting pending tool confirmations:

**TypeScript**  
```ts  
const pendingToolCallConfirmation = messages.some((m) =>  
  m.parts?.some(  
    (part) => isToolUIPart(part) && part.state === "input-available",  
  ),  
);  
// Handle tool confirmation  
if (pendingToolCallConfirmation) {  
  await addToolResult({  
    toolCallId: part.toolCallId,  
    tool: getToolName(part),  
    output: "User approved the action",  
  });  
}  
```  
#### Automatic Message Migration  
Seamlessly handle legacy message formats without code changes.

**TypeScript**  
```ts  
// All these formats are automatically converted:  
// Legacy v4 string content  
const legacyMessage = {  
  role: "user",  
  content: "Hello world",  
};  
// Legacy v4 with tool calls  
const legacyWithTools = {  
  role: "assistant",  
  content: "",  
  toolInvocations: [  
    {  
      toolCallId: "123",  
      toolName: "weather",  
      args: { city: "SF" },  
      state: "result",  
      result: "Sunny, 72°F",  
    },  
  ],  
};  
// Automatically becomes v5 format:  
// {  
//   role: "assistant",  
//   parts: [{  
//     type: "tool-call",  
//     toolCallId: "123",  
//     toolName: "weather",  
//     args: { city: "SF" },  
//     state: "result",  
//     result: "Sunny, 72°F"  
//   }]  
// }  
```  
#### Tool Definition Updates  
Migrate tool definitions to use the new `inputSchema` property.

**TypeScript**  
```ts  
// Before (AI SDK v4)  
const tools = {  
  weather: {  
    description: "Get weather information",  
    parameters: z.object({  
      city: z.string(),  
    }),  
    execute: async (args) => {  
      return await getWeather(args.city);  
    },  
  },  
};  
// After (AI SDK v5)  
const tools = {  
  weather: {  
    description: "Get weather information",  
    inputSchema: z.object({  
      city: z.string(),  
    }),  
    execute: async (args) => {  
      return await getWeather(args.city);  
    },  
  },  
};  
```  
#### Cloudflare Workers AI Integration  
Seamless integration with Cloudflare Workers AI models through the updated workers-ai-provider v2.0.0.  
#### Model Setup with Workers AI  
Use Cloudflare Workers AI models directly in your agent workflows:

**TypeScript**  
```ts  
import { createWorkersAI } from "workers-ai-provider";  
import { useAgentChat } from "agents/ai-react";  
// Create Workers AI model (v2.0.0 - same API, enhanced v5 internals)  
const model = createWorkersAI({  
  binding: env.AI,  
})("@cf/meta/llama-3.2-3b-instruct");  
```  
#### Enhanced File and Image Support  
Workers AI models now support v5 file handling with automatic conversion:

**TypeScript**  
```ts  
// Send images and files to Workers AI models  
sendMessage({  
  role: "user",  
  parts: [  
    { type: "text", text: "Analyze this image:" },  
    {  
      type: "file",  
      data: imageBuffer,  
      mediaType: "image/jpeg",  
    },  
  ],  
});  
// Workers AI provider automatically converts to proper format  
```  
#### Streaming with Workers AI  
Enhanced streaming support with automatic warning detection:

**TypeScript**  
```ts  
// Streaming with Workers AI models  
const result = await streamText({  
  model: createWorkersAI({ binding: env.AI })("@cf/meta/llama-3.2-3b-instruct"),  
  messages,  
  onChunk: (chunk) => {  
    // Enhanced streaming with warning handling  
    console.log(chunk);  
  },  
});  
```  
#### Import Updates  
Update your imports to use the new v5 types:

**TypeScript**  
```ts  
// Before (AI SDK v4)  
import type { Message } from "ai";  
import { useChat } from "ai/react";  
// After (AI SDK v5)  
import type { UIMessage } from "ai";  
// or alias for compatibility  
import type { UIMessage as Message } from "ai";  
import { useChat } from "@ai-sdk/react";  
```  
#### Resources

  * [Migration Guide ↗](https://github.com/cloudflare/agents/blob/main/docs/migration-to-ai-sdk-v5.md) \- Comprehensive migration documentation
  * [AI SDK v5 Documentation ↗](https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0) \- Official AI SDK migration guide
  * [An Example PR showing the migration from AI SDK v4 to v5 ↗](https://github.com/cloudflare/agents-starter/pull/105)
  * [GitHub Issues ↗](https://github.com/cloudflare/agents/issues) \- Report bugs or request features  
#### Feedback Welcome  
We'd love your feedback! We're particularly interested in feedback on:

  * **Migration experience** \- How smooth was the upgrade process?
  * **Tool confirmation workflow** \- Does the new automatic detection work as expected?
  * **Message format handling** \- Any edge cases with legacy message conversion?

Aug 05, 2025
1. ### [Agents SDK adds MCP Elicitation support, http-streamable support, task queues, email integration and more](https://edgetunnel-b2h.pages.dev/changelog/post/2025-08-05-agents-mcp-update/)  
[ Agents ](https://edgetunnel-b2h.pages.dev/agents/)[ Workers ](https://edgetunnel-b2h.pages.dev/workers/)  
The latest releases of [@cloudflare/agents ↗](https://github.com/cloudflare/agents) brings major improvements to MCP transport protocols support and agents connectivity. Key updates include:  
#### MCP elicitation support  
MCP servers can now request user input during tool execution, enabling interactive workflows like confirmations, forms, and multi-step processes. This feature uses durable storage to preserve elicitation state even during agent hibernation, ensuring seamless user interactions across agent lifecycle events.

**TypeScript**  
```ts  
// Request user confirmation via elicitation  
const confirmation = await this.elicitInput({  
  message: `Are you sure you want to increment the counter by ${amount}?`,  
  requestedSchema: {  
    type: "object",  
    properties: {  
      confirmed: {  
        type: "boolean",  
        title: "Confirm increment",  
        description: "Check to confirm the increment",  
      },  
    },  
    required: ["confirmed"],  
  },  
});  
```  
Check out our [demo ↗](https://github.com/whoiskatrin/agents/tree/main/examples/mcp-elicitation-demo) to see elicitation in action.  
#### HTTP streamable transport for MCP  
MCP now supports HTTP streamable transport which is recommended over SSE. This transport type offers:

  * **Better performance**: More efficient data streaming and reduced overhead
  * **Improved reliability**: Enhanced connection stability and error recover- **Automatic fallback**: If streamable transport is not available, it gracefully falls back to SSE

**TypeScript**  
```ts  
export default MyMCP.serve("/mcp", {  
  binding: "MyMCP",  
});  
```  
The SDK automatically selects the best available transport method, gracefully falling back from streamable-http to SSE when needed.  
#### Enhanced MCP connectivity  
Significant improvements to MCP server connections and transport reliability:

  * **Auto transport selection**: Automatically determines the best transport method, falling back from streamable-http to SSE as needed
  * **Improved error handling**: Better connection state management and error reporting for MCP servers
  * **Reliable prop updates**: Centralized agent property updates ensure consistency across different contexts  
#### Lightweight .queue for fast task deferral  
You can use `.queue()` to enqueue background work — ideal for tasks like processing user messages, sending notifications etc.

**TypeScript**  
```ts  
class MyAgent extends Agent {  
  doSomethingExpensive(payload) {  
    // a long running process that you want to run in the background  
  }  
  queueSomething() {  
    await this.queue("doSomethingExpensive", somePayload); // this will NOT block further execution, and runs in the background  
    await this.queue("doSomethingExpensive", someOtherPayload); // the callback will NOT run until the previous callback is complete  
    // ... call as many times as you want  
  }  
}  
```  
Want to try it yourself? Just define a method like processMessage in your agent, and you’re ready to scale.  
#### New email adapter  
Want to build an AI agent that can receive and respond to emails automatically? With the new email adapter and onEmail lifecycle method, now you can.

**TypeScript**  
```ts  
export class EmailAgent extends Agent {  
  async onEmail(email: AgentEmail) {  
    const raw = await email.getRaw();  
    const parsed = await PostalMime.parse(raw);  
    // create a response based on the email contents  
    // and then send a reply  
    await this.replyToEmail(email, {  
      fromName: "Email Agent",  
      body: `Thanks for your email! You've sent us "${parsed.subject}". We'll process it shortly.`,  
    });  
  }  
}  
```  
You route incoming mail like this:

**TypeScript**  
```ts  
export default {  
  async email(email, env) {  
    await routeAgentEmail(email, env, {  
      resolver: createAddressBasedEmailResolver("EmailAgent"),  
    });  
  },  
};  
```  
You can find a full example [here ↗](https://github.com/cloudflare/agents/tree/main/examples/email-agent).  
#### Automatic context wrapping for custom methods  
Custom methods are now automatically wrapped with the agent's context, so calling `getCurrentAgent()` should work regardless of where in an agent's lifecycle it's called. Previously this would not work on RPC calls, but now just works out of the box.

**TypeScript**  
```ts  
export class MyAgent extends Agent {  
  async suggestReply(message) {  
    // getCurrentAgent() now correctly works, even when called inside an RPC method  
    const { agent } = getCurrentAgent()!;  
    return generateText({  
      prompt: `Suggest a reply to: "${message}" from "${agent.name}"`,  
      tools: [replyWithEmoji],  
    });  
  }  
}  
```  
Try it out and tell us what you build!

```json
{"@context":"https://schema.org","@type":"BlogPosting","@id":"https://edgetunnel-b2h.pages.dev/changelog/product/agents/#page","headline":"Agents Changelog | Cloudflare Docs","url":"https://edgetunnel-b2h.pages.dev/changelog/product/agents/","inLanguage":"en","image":"https://edgetunnel-b2h.pages.dev/cf-twitter-card.png","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://edgetunnel-b2h.pages.dev/#website","name":"Cloudflare Docs","url":"https://edgetunnel-b2h.pages.dev/"}}
```
