Platforms can now create temporary preview accounts through the Cloudflare REST API. This lets your platform deploy a live Worker before the user signs in to Cloudflare.
With the Temporary Accounts API, coding agents, AI app builders, and other platforms can build a similar flow for generated Workers and supported resources.
Your platform can keep users in its onboarding flow while they generate, deploy, and test an application. Users do not need an existing Cloudflare account, and your platform does not need write access to one.

The API returns a claim URL that lets the user make the temporary account and its resources permanent.
Cloudflare Drop ↗ demonstrates this preview-and-claim pattern for static sites. Someone can upload a site, test and share it for one hour, then sign in or create an account only when they want to keep it.
This API expands the flow first introduced with
wrangler deploy --temporary. Your backend now controls the provisioning and deployment experience directly:- Show Cloudflare's Terms of Service and Privacy Policy in your product, and require the user to accept them.
- Request and solve a proof-of-work challenge.
- Create a temporary preview account.
- Deploy with the returned temporary account ID and API token.
- Show the deployed Worker URL and claim URL to the user.
Terminal window curl "https://api.cloudflare.com/client/v4/provisioning/previews/challenge" \-X POST \-H "Content-Type: application/json" \--data '{}'curl "https://api.cloudflare.com/client/v4/provisioning/previews" \-X POST \-H "Content-Type: application/json" \--data '{"termsOfService": "https://www.cloudflare.com/terms/","privacyPolicy": "https://www.cloudflare.com/privacypolicy/","acceptTermsOfService": "yes","challengeToken": "<CHALLENGE_TOKEN>","solution": {"checkpoints": "<BASE64_CHECKPOINTS>"}}'For the complete API flow, proof-of-work requirements, supported products, and limits, refer to Claim deployments (temporary accounts). For the background and design goals behind this flow, refer to Temporary Cloudflare Accounts for AI agents ↗.
Agents connected to Model Context Protocol (MCP) servers with
addMcpServercan now handle 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 notificationRegister a handler for each mode your Agent supports in
onStart():JavaScript 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 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. The
mcp-client↗ andmcp-elicitation↗ examples implement both sides.To update to this release:
npm i agents@latestyarn add agents@latestpnpm add agents@latestbun add agents@latest
If your account does not already have a key-value (KV) backed Durable Object namespace, you can no longer create new ones. New Durable Object namespaces must use the SQLite storage backend, which has been recommended for all new Durable Objects since it became generally available ↗ in 2024.
Create a new class with a
new_sqlite_classesmigration:JSONC {"$schema": "./node_modules/wrangler/config-schema.json","migrations": [{"tag": "v1","new_sqlite_classes": ["MyDurableObject"]}]}TOML [[migrations]]tag = "v1"new_sqlite_classes = ["MyDurableObject"]SQLite-backed Durable Objects have feature parity with the key-value backend — including the key-value storage API — and additionally support relational SQL queries and point-in-time recovery to restore an object's storage to any point in the past 30 days.
If you attempt to create a new key-value backed namespace (a
new_classesmigration) on an affected account, the deployment fails with the following error:Creating new key-value backed Durable Object namespaces is no longer supported on this account. Please create a namespace using a `new_sqlite_classes` migration instead.This change only affects accounts that are not already using the key-value storage backend. Accounts with at least one existing key-value backed namespace can still create new ones for now, and the Workers Free plan has only ever supported SQLite-backed Durable Objects. It is part of a broader move toward SQLite as the single storage backend for Durable Objects, ahead of a future migration path for existing key-value backed objects.
For more information, refer to Durable Objects migrations.
Wrangler now collects npm package dependency information from your project's
package.jsonduringwrangler deployandwrangler versions upload, and includes it in the upload metadata sent to the Cloudflare API. This data, each dependency's name, declared version range, and exact installed version, enables dependency analytics and future supply chain security features such as vulnerability alerting.To opt out, set
dependencies_instrumentation.enabledtofalsein your Wrangler configuration file:JSONC {"dependencies_instrumentation": {"enabled": false}}TOML [dependencies_instrumentation]enabled = falseFor more details, refer to Wrangler configuration.
Cloudflare Drop ↗ lets you deploy a static site to Cloudflare without requiring a Cloudflare account to get started.

Upload a folder or zip file of static assets (static HTML, CSS, JavaScript, images, and fonts) and get a temporary live preview that stays live for 1 hour. During that window, you can test the site, share the preview URL, or claim the deployment to keep it.

When you are ready to make the deployment permanent, click Claim to sign in or create a Cloudflare account. You can claim the site into an existing Cloudflare account or create a new account for the deployment.

After claiming the site, you can:
- Add a domain: Connect an existing domain or purchase a new one for your site.
- Enable observability: Monitor your site's performance and usage.
- Enable Markdown for Agents: Allow AI agents to access your site's content in Markdown.
- Control access: Make your site private and choose who can view it.

A new declarative
exportsfield in your Wrangler configuration file replaces the imperativemigrationsarray for managing Durable Object class lifecycle. Instead of writing an ordered list of migration steps with unique tags, you declare each Durable Object class your Worker exports and Cloudflare compares that against what's already deployed to determine what Durable Object state needs to be created, renamed, or deleted.With legacy migrations, renaming
ChatRoomtoRoomrequires retaining both tagged steps:Before — legacy migrations {"migrations": [{ "tag": "v1", "new_sqlite_classes": ["ChatRoom"] },{"tag": "v2","renamed_classes": [{ "from": "ChatRoom", "to": "Room" }],},],}With
exports, you instead declareRoomas the current class and markChatRoomas renamed:After — declarative exports {"exports": {"ChatRoom": {"type": "durable-object","state": "renamed","renamed_to": "Room",},"Room": { "type": "durable-object", "storage": "sqlite" },},}Each entry is keyed by class name. The
statefield carries the lifecycle (createdby default — a live class — plus tombstone statesdeleted,renamed, andtransferred, and theexpecting-transferreceiving state for cross-Worker transfers).Key improvements over the legacy
migrationsarray:- No migration tags. The current
exportsmap is the source of truth — there is no historical chain ofv1,v2,v3entries to maintain. - Structured deployment output. Wrangler reports when it creates, updates, deletes, renames, or transfers Durable Object classes. It also identifies stale configuration entries that are safe to remove. Deployments with no changes or notices do not print this output.
- Zero-downtime rename and transfer patterns are first-class. Tombstones may coexist with the source class still in code, enabling a three-deploy rename and a four-deploy cross-Worker transfer without runtime errors during the rollout window.
- Cross-Worker safety. When you delete or rename a class, Cloudflare lists every other Worker in your account whose bindings still reference the namespace, so you can redeploy them before the change goes live.
Existing Workers using the legacy
migrationsarray continue to work unchanged. To move toexports, refer to the migration guide.exportsandmigrationsare mutually exclusive within a single Worker.For the full reference, refer to Durable Object class exports.
- No migration tags. The current
We have released version 5 of
@cloudflare/workers-types↗. This release simplifies the package to expose only the latest runtime types.We still recommend that you generate types for your Worker using
wrangler types, but if you want to use the package directly, you can install it with your package manager of choice:npm i -D @cloudflare/workers-types@latestyarn add -D @cloudflare/workers-types@latestpnpm add -D @cloudflare/workers-types@latestbun add -d @cloudflare/workers-types@latestThe package now exposes two entrypoints:
@cloudflare/workers-typesreflects the latest compatibility date, using the latest stable compatibility flags.@cloudflare/workers-types/experimentalreflects APIs behind experimental compatibility flags.
The dated entrypoints, such as
@cloudflare/workers-types/2022-11-30and@cloudflare/workers-types/2023-03-01, are removed. With runtime type generation in Wrangler v4, you can generate these with thewrangler typescommand to create types locked to your Worker's compatibility date.For more information, refer to TypeScript language support.
Wrangler CLI now supports auth profiles: named logins that you scope to specific Cloudflare accounts and switch between automatically, based on the directory you are working in.
A profile is a named OAuth login bound to a directory. Commands run in that directory, and its subdirectories, use the matching account — so you can move between accounts without re-running
wrangler login.Use profiles to keep a separate login for each client when working at an agency, or to separate staging and production into different accounts. Pair a profile with an
account_idin your Wrangler configuration file so a command cannot reach the wrong account.Terminal window # Create a profile for each account, choosing which accounts it can reachwrangler auth create client-awrangler auth activate client-a ~/clients/client-awrangler auth create client-bwrangler auth activate client-b ~/clients/client-bUse the
--profileflag to run a single command with a specific profile:Terminal window wrangler deploy --profile personalIn CI and other automated environments,
CLOUDFLARE_API_TOKENstill takes precedence over all profiles.For setup, the resolution order, and the full command reference, refer to Authentication profiles.
You can now monitor how much memory your Workers and Durable Objects consume across invocations with the new Memory Usage chart in the Workers Metrics tab, broken down by P50, P90, P99, and P999 percentiles.

Memory usage measures the V8 isolate memory at the time of each invocation, subject to the 128 MB per-isolate limit — a single isolate can handle many concurrent requests and shares memory across them.
Use the Memory Usage chart to:
- Track memory trends — Spot gradual increases that may indicate a memory leak before they cause
Exceeded Memoryerrors. - Correlate with deployments — Deployment markers on the chart help you identify whether a new version introduced a memory regression.
- Right-size your Worker — Understand your baseline memory footprint and how much headroom you have before hitting the 128 MB limit.
For Durable Objects, memory usage reflects the in-memory state an object holds (class properties, caches, active WebSocket connections), which persists across invocations until the object is hibernated or evicted. This state is not preserved across eviction, hibernation, or a crash, so persist anything important to storage.
To view memory usage, open the Metrics tab for your Worker ↗ or Durable Object namespace ↗. For Durable Objects, you can filter by DO ID or name to drill down into memory usage for a specific object. You can also query memory usage programmatically via the GraphQL Analytics API using the
workersInvocationsAdaptivedataset — thequantiles.memoryUsageBytesP50throughquantiles.memoryUsageBytesP999fields return percentile values in bytes.For local memory debugging, you can also profile memory with DevTools to take heap snapshots and identify specific objects causing high memory usage.
- Track memory trends — Spot gradual increases that may indicate a memory leak before they cause
Workers
fetch()requests now support thecf.varyrequest option. Usecf.varyto control how Cloudflare caches origin responses with aVaryheader for a single subrequest.src/index.js export default {async fetch(request) {return fetch(request, {cf: {vary: {default: { action: "bypass" },headers: {accept: {action: "normalize",media_types: ["text/html", "application/json"],},"accept-language": {action: "normalize",languages: ["en", "fr", "de"],},},},},});},};src/index.ts export default {async fetch(request): Promise<Response> {return fetch(request, {cf: {vary: {default: { action: "bypass" },headers: {accept: {action: "normalize",media_types: ["text/html", "application/json"],},"accept-language": {action: "normalize",languages: ["en", "fr", "de"],},},},},});},} satisfies ExportedHandler;For more information, refer to
cf.vary.
The latest release of the Agents SDK ↗ 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
runTurnturn-admission entry point, and a large round of recovery and reliability fixes that continue converging@cloudflare/thinkand@cloudflare/ai-chatonto one model.runAgentToolcan 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 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 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
maxBudgetMsceiling (default 24h) andcancelAgentTool(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-wiredonFinishneeded.
Sub-agents can also report mid-run progress that rides their own turn stream back to the parent's connected clients:
JavaScript // Inside the child sub-agent:await this.reportProgress({fraction: 0.6,phase: "deploying",message: "Generating menu page…",});TypeScript // Inside the child sub-agent:await this.reportProgress({fraction: 0.6,phase: "deploying",message: "Generating menu page…",});Progress surfaces on
AgentToolRunState.progressviauseAgentToolEvents, so a background-runs tray can render a live bar without drilling in, and the latest snapshot is persisted for inspection after eviction. Naming amilestonepromotes a signal to a durable, replayable row, anddetached: { onMilestones }can surface a milestone as a synthetic chat message ("narrate"for a cheap status line, or"react"to drive a model turn).@cloudflare/thinkadds a publicrunTurn(options)facade that unifies turn admission behind a singlemode:JavaScript await this.runTurn({ mode: "wait", messages }); // saveMessages / continueLastTurnawait this.runTurn({ mode: "submit", messages }); // durable submitMessagesawait this.runTurn({ mode: "stream", messages }); // chat()TypeScript await this.runTurn({ mode: "wait", messages }); // saveMessages / continueLastTurnawait this.runTurn({ mode: "submit", messages }); // durable submitMessagesawait this.runTurn({ mode: "stream", messages }); // chat()streammode accepts array and function inputs to matchwaitmode, 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.A large part of this release continues hardening recovery and converging
@cloudflare/thinkand@cloudflare/ai-chatonto one model:- Stream stall watchdog.
AIChatAgentcan detect and recover from a hung model/transport stream via the opt-inchatStreamStallTimeoutMswatchdog. WithchatRecoveryenabled 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.
AIChatAgentnow repairs a transcript with a dead server-tool call before re-entering inference (parity with@cloudflare/think), so a recovered turn no longer fails withAI_MissingToolResultsError. An overridablerepairInterruptedToolPart(part)hook lets apps customize the repaired shape. - Stuck status after reconnect. Fixed AI SDK
statusgetting 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 onready. - Live "recovering…" on connect.
AIChatAgentnow replays the recovering status to a client that connects mid-recovery, souseAgentChat'sisRecoveringreflects 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/onConnectionErroronAgentClient,useAgent, anduseAgentChat. - Agent-tool child recovery. A healthy long-running sub-agent run is no longer abandoned as
interruptedafter a deploy (both@cloudflare/thinkandAIChatAgent). - 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.
- Shared chat React core. A new
agents/chat/reactentry exposesuseAgentChat, transport helpers, and shared wire types, withsyncMessagesToServerfor server-authoritative transcript storage.@cloudflare/think/reactand@cloudflare/ai-chat/reactare now thin wrappers over it. - Optional
aipeer. The rootagentsand@cloudflare/codemoderuntimes no longer reference AI SDK types, so they bundle withoutai/zodinstalled; AI-specific entry points still require the peer when imported.just-bashlikewise moves to an optional peer used only by the skills bash runner. - Code Mode. The default
DynamicWorkerExecutortimeout 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 toopenApiMcpServerrequest callbacks. - Voice. Voice turns now support AI SDK
fullStreamresponses (and warn whentextStreamis used). - MCP.
McpAgentserver-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.
To update to the latest version:
npm i agents@latest @cloudflare/think@latest @cloudflare/ai-chat@latest @cloudflare/codemode@latest @cloudflare/voice@latestyarn add agents@latest @cloudflare/think@latest @cloudflare/ai-chat@latest @cloudflare/codemode@latest @cloudflare/voice@latestpnpm add agents@latest @cloudflare/think@latest @cloudflare/ai-chat@latest @cloudflare/codemode@latest @cloudflare/voice@latestbun add agents@latest @cloudflare/think@latest @cloudflare/ai-chat@latest @cloudflare/codemode@latest @cloudflare/voice@latestRefer to the Think documentation, Code Mode documentation, and Agents documentation for more information.
Durable Objects now supports a
usjurisdiction, letting you create Durable Objects that only run and store data within the United States. Use theusjurisdiction when you need to keep a Durable Object's compute and storage inside the United States to meet data residency requirements.Create a namespace restricted to the
usjurisdiction the same way as any other jurisdiction:JavaScript // Workerexport default {async fetch(request, env) {const usSubnamespace = env.MY_DURABLE_OBJECT.jurisdiction("us");const stub = usSubnamespace.getByName("general");return stub.fetch(request);},};Workers may still access Durable Objects constrained to the
usjurisdiction from anywhere in the world. The jurisdiction constraint only controls where the Durable Object itself runs and persists data.For the full list of supported jurisdictions, refer to Data location — Restrict Durable Objects to a jurisdiction.
The
@cloudflare/vitest-pool-workerspackage now includesevictDurableObjectandevictAllDurableObjectstest helpers, exported fromcloudflare:test.These helpers let you test how a Durable Object behaves across evictions, simulating the production lifecycle where an idle Durable Object can be evicted from memory.
For more context, refer to Lifecycle of a Durable Object.
TypeScript import { evictDurableObject, evictAllDurableObjects } from "cloudflare:test";import { env } from "cloudflare:workers";const id = env.COUNTER.idFromName("my-counter");const stub = env.COUNTER.get(id);// Evict the Durable Object instance pointed to by a specific stubawait evictDurableObject(stub);// Close WebSockets instead of hibernating themawait evictDurableObject(stub, { webSockets: "close" });// Evict all currently-running Durable Objects in evictable namespacesawait evictAllDurableObjects();These helpers are available in
@cloudflare/vitest-pool-workers@0.16.20and later.Learn more in the Test APIs reference and the Testing Durable Objects guide.
Durable Objects now supports two new location hints for Asia-Pacific:
apac-ne(Northeast Asia-Pacific) andapac-se(Southeast Asia-Pacific). Useapac-neorapac-sewhen you want finer-grained placement within Asia-Pacific rather than the broaderapachint.Use the new hints the same way as any other
locationHint:JavaScript // Northeast Asia-Pacific (Japan, Korea, etc.)const stubNE = env.MY_DURABLE_OBJECT.get(id, { locationHint: "apac-ne" });// Southeast Asia-Pacific (Singapore, Indonesia, etc.)const stubSE = env.MY_DURABLE_OBJECT.get(id, { locationHint: "apac-se" });If your users are spread across all of Asia-Pacific, the existing
apachint remains the right choice. Only reach forapac-neorapac-sewhen your traffic is clearly concentrated in one sub-region and you want to minimize round-trip time to that audience. The default behavior and what we generally recommended is not adding a location hint unless absolutely needed, this will create the Durable Object as close to the initializing request as possible to reduce latency.As with all location hints, these are best-effort suggestions. Cloudflare will place the Durable Object in a nearby data center, not necessarily the exact hinted location.
For the full list of supported hints, refer to Data location — Provide a location hint.
AI agents can now deploy Workers to Cloudflare without first requiring a user to sign up, open a browser-based OAuth flow, click through the dashboard, or create an API token. When an agent tries to deploy without Cloudflare credentials, Wrangler can tell it to rerun with
--temporary, then deploy the Worker to a temporary preview account.To try this with your agent, update to Wrangler 4.102.0 or later, make sure you are logged out (
wrangler logout), and then ask your agent to build something and deploy it to Cloudflare. The agent should follow Wrangler's output and deploy using the--temporaryflag.
Terminal window wrangler deploy --temporaryThe temporary deployment stays live for 60 minutes. During that window, the agent can verify the Worker, redeploy changes, and return both the live Worker URL and claim URL. Opening the claim URL lets you sign in to or create a Cloudflare account and make the temporary account permanent.
Temporary preview accounts currently support a limited set of products, including Workers, Workers Static Assets, Workers KV, D1, Durable Objects, Hyperdrive, Queues, and SSL/TLS certificates. For supported products, limits, and claim behavior, refer to Claim deployments (temporary accounts).
For more context, refer to Temporary Cloudflare Accounts for Agents ↗.
You can create PlanetScale Postgres and MySQL databases from Cloudflare and bill PlanetScale database usage through your Cloudflare account as a pay-as-you-go customer. Cloudflare contract customers will be able to add PlanetScale usage to their contract in July so reach out to your Cloudflare account team if interested.
Create a PlanetScale database from the Cloudflare dashboard to check out globally distributed Workers optimized for regional data access.
Go to Create a PlanetScale databasePlanetScale databases created from Cloudflare work with Workers through Hyperdrive. Hyperdrive manages database connection pools and query caching, so you can use PlanetScale as a centralized relational database for Workers applications without changing your database drivers, object-relational mapping (ORM) libraries, or SQL tooling.
PlanetScale usage appears on your Cloudflare invoice each billing period as a dollar total at PlanetScale's standard pricing ↗. You can introspect per-database billing usage via PlanetScale's dashboard ↗.
When you create a PlanetScale database from the Cloudflare dashboard, you receive the same PlanetScale developer experience, including development branches, query insights, and Model Context Protocol (MCP) server support for agents.
To get started, refer to PlanetScale Postgres and MySQL with Hyperdrive.
The latest release of the Agents SDK ↗ 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.
Agents can now use Browser Run through a single durable
browser_executetool. 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 const browserTools = createBrowserTools({ctx: this.ctx,browser: this.env.BROWSER,loader: this.env.LOADER,session: { mode: "dynamic" },});TypeScript 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, andbrowser_scrapefor one-shot browsing tasks.Code Mode now uses
createCodemodeRuntime, connectors, and a durable execution log. This lets you give a model onecodemodetool 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 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 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.
Think sub-agents can now use client-defined tools over the RPC
chat()path. A parent agent can pass tool schemas withclientToolsand resolve tool calls throughonClientToolCall. This lets delegated agents use caller-provided capabilities without requiring a browser WebSocket.JavaScript 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 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 alongsidestate.*andtools.*when Browser Run is bound.Voice clients can route assistant audio to a specific output device. Use
outputDeviceIdwithuseVoiceAgent, or callclient.setOutputDevice()from the framework-agnostic client.JavaScript const voice = useVoiceAgent({agent: "MyVoiceAgent",outputDeviceId: selectedSpeakerId,});TypeScript 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.This release includes several fixes for production agents:
useAgentandAgentClienthandle 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.
To update to the latest version:
npm i agents@latest @cloudflare/think@latest @cloudflare/codemode@latest @cloudflare/ai-chat@latest @cloudflare/voice@latestyarn add agents@latest @cloudflare/think@latest @cloudflare/codemode@latest @cloudflare/ai-chat@latest @cloudflare/voice@latestpnpm add agents@latest @cloudflare/think@latest @cloudflare/codemode@latest @cloudflare/ai-chat@latest @cloudflare/voice@latestbun add agents@latest @cloudflare/think@latest @cloudflare/codemode@latest @cloudflare/ai-chat@latest @cloudflare/voice@latestRefer to the Code Mode documentation, Browser tools documentation, Think tools documentation, and Voice documentation for more information.
We are excited to announce GLM-5.2 on Workers AI, Z.ai's flagship agentic coding model.
@cf/zai-org/glm-5.2is 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 (
env.AI.run()), the REST API at/runor/v1/chat/completions, or AI Gateway.Pricing is available on the model page or pricing page.
You can now create custom trace spans in your Workers code using
tracing.enterSpan(). Custom spans appear alongside the automatic platform instrumentation (fetch calls, KV reads, D1 queries, and other platform operations) in your traces and OpenTelemetry exports, with correct parent-child nesting.The API is available via
import { tracing } from "cloudflare:workers"or through the handler context asctx.tracing:TypeScript import { tracing } from "cloudflare:workers";export default {async fetch(request, env, ctx) {return tracing.enterSpan("handleRequest", async (span) => {span.setAttribute("url.path", new URL(request.url).pathname);const data = await env.MY_KV.get("key");return new Response(data);});},};Spans nest automatically based on the JavaScript async context, and are auto-ended when the callback returns or its returned promise settles. The
Spanobject providessetAttribute(key, value)for attaching metadata and anisTracedproperty to check whether the current request is being sampled.
Tracing must be enabled in your Wrangler configuration for spans to be recorded.
For full API details and examples, refer to Custom spans.
You can now filter the Metrics tab for a Durable Objects namespace by an individual Durable Object's ID or name in the Cloudflare dashboard. Previously, metrics charts only showed aggregate, namespace-level data, making it difficult to isolate the behavior of a specific object.
Go to Durable Objects
Start typing an ID or name into the filter and select a match from the autocomplete dropdown. The autocomplete only shows objects with invocations during the selected time range, so an object that does not appear has not been invoked in that window. This does not necessarily mean the object has been deleted. Every chart on the page updates to reflect only the selected object. This makes it easier to identify and investigate a single Durable Object when debugging a high-traffic object, an error spike, or unexpected storage usage. Clear the filter to return to namespace-level metrics.
Metrics are powered by the GraphQL Analytics API, so standard analytics behavior such as ingestion delay and sampling applies.
For more information, refer to Metrics and analytics.

Customers can now view the number of Dynamic Workers invoked during their billing period from the Workers overview page in the Cloudflare dashboard.
This count reflects the number of Dynamic Workers that Cloudflare would bill for during the selected billing period. Dynamic Workers usage data only goes back to June 1, 2026.
You can also query this count through the GraphQL Analytics API by using
workersInvocationsByOwnerAndScriptGroupsand selectingdistinctDynamicWorkerCount:query getDynamicWorkersCount($accountTag: string!$filter: AccountWorkersInvocationsByOwnerAndScriptGroupsFilter_InputObject) {viewer {accounts(filter: { accountTag: $accountTag }) {workersInvocationsByOwnerAndScriptGroups(limit: 10000, filter: $filter) {uniq {distinctDynamicWorkerCount}}}}}Use variables to set the account and billing-period date range:
{"accountTag": "<ACCOUNT_ID>","filter": {"date_geq": "2026-06-01","date_leq": "2026-06-30"}}For more information, refer to Dynamic Workers pricing.
Pay-as-you-go customers can now view billable usage and create budget alerts directly from the product overview pages for Workers & Pages, D1, R2, Workers KV, Queues, Vectorize, Durable Objects, and Containers. A new sidebar widget shows current-period spend and the billing cycle date range, alongside a button to create a budget alert.
The widget pulls from the same data as the Billable Usage dashboard and aligns to your billing cycle (or the current day on Free plans), so the numbers match your invoice. Enterprise contract accounts are not yet supported.

Selecting Create budget alert opens the budget alert flow inline so you can set a dollar threshold in the same place you are reviewing usage. Budget alerts apply to your total account-level spend across all products, not just the product page you create them from.
For more information, refer to the Usage-based billing documentation.
The
pipelinefield inside thepipelinesbinding configuration in your Wrangler configuration file has been renamed tostream. The old field is deprecated but still accepted.Update your configuration to use
streamto avoid the deprecation warning.Before (deprecated):
JSONC {"$schema": "./node_modules/wrangler/config-schema.json","pipelines": [{"binding": "MY_PIPELINE","pipeline": "<STREAM_ID>"}]}TOML [[pipelines]]binding = "MY_PIPELINE"pipeline = "<STREAM_ID>"After:
JSONC {"$schema": "./node_modules/wrangler/config-schema.json","pipelines": [{"binding": "MY_PIPELINE","stream": "<STREAM_ID>"}]}TOML [[pipelines]]binding = "MY_PIPELINE"stream = "<STREAM_ID>"No other changes are required. The binding name, TypeScript types, and runtime API (
env.MY_PIPELINE.send(...)) remain the same.For more information on configuring pipeline bindings, refer to Writing to streams.
You can now create, update, or delete multiple secrets for your Worker in a single request using the bulk secrets endpoint.
- Include a secret with a value to create or update.
- Set a secret to
nullto delete. - Secrets not included in the request are left unchanged.
The following example creates
API_KEY, updates the already existingDB_PASSWORD, and deletesOLD_SECRET:{"secrets": {"API_KEY": { "type": "secret_text", "name": "API_KEY", "text": "my-api-key" },"DB_PASSWORD": { "type": "secret_text", "name": "DB_PASSWORD", "text": "my-db-password" },"OLD_SECRET": null}}You can do the same from the command line using
wrangler secret bulk:Terminal window npx wrangler secret bulk < secrets.jsonTo delete a key, set its value to
nullin the JSON file. Deletion is not supported with.envfiles.Each request supports up to 100 total operations (creates, updates, and deletes combined).
Wrangler can now store the OAuth credentials returned by
wrangler loginin an AES-256-GCM ↗-encrypted file, with the encryption key held in your operating system keychain. The default behavior is unchanged — credentials still live in a plaintext TOML file unless you opt in.To opt in, run:
Terminal window npx wrangler login --use-keyringThe choice is persisted across Wrangler invocations. Opt back out with
npx wrangler login --no-use-keyring, or override the preference for a single command with theCLOUDFLARE_AUTH_USE_KEYRINGenvironment variable.wrangler whoaminow reports where credentials are stored:🔐 Credentials are stored in: Encrypted file (~/.config/.wrangler/config/default.enc) with key in macOS Keychain (service=wrangler, account=default)Per-platform backends:
- macOS uses the built-in Keychain via
/usr/bin/security. - Linux uses libsecret ↗ via the
secret-toolCLI from thelibsecret-toolspackage. - Windows uses Credential Manager via
@napi-rs/keyring↗, installed on-demand the first time you opt in.
Refer to Storing OAuth credentials in the OS keychain for the full details, including the migration behavior on opt-in/opt-out and the
CLOUDFLARE_AUTH_USE_KEYRINGenvironment variable.- macOS uses the built-in Keychain via