ReleaseCloudflare (Workers AI)Cloudflare (Workers AI)published Jun 12, 2026seen 8h

cloudflare/agents agents@0.16.0

cloudflare/agents

Open original ↗

Captured source

source ↗
published Jun 12, 2026seen 8hcaptured 8hhttp 200method plain

agents@0.16.0

Repository: cloudflare/agents

Tag: agents@0.16.0

Published: 2026-06-12T16:46:18Z

Prerelease: no

Release notes:

Minor Changes

  • #1656 `4c2d1a7` Thanks @cjol! - Rebuild agents/browser on the codemode connector runtime (experimental).

The browser tool surface is now a single durable tool, `browser_execute`: the model writes sandboxed code against a cdp connector (cdp.send, cdp.attachToTarget, cdp.spec, cdp.getDebugLog, …) instead of picking from several flat tools. Executions are recorded on a CodemodeRuntime Durable Object facet with abort-and-replay, so a run can pause for approval and resume with its browser session, tabs, and cookies intact.

  • `BrowserConnector` — a CodemodeConnector (name cdp) that owns CDP sockets keyed by execution id. Sockets are released at the end of every execution pass (onPassEnd); browser sessions are torn down on terminal status (disposeExecution) — never on pause.
  • Session modesone-shot (default, fresh session per execution), reuse (named shared session), and dynamic (starts one-shot; the model can promote with cdp.startSession() after e.g. logging in). Shared sessions are tracked in durable storage and survive hibernation; connector.sweep() reclaims expired ones from a scheduled task.
  • Safe sweeping — per-execution entries are touched on use and only swept after maxExecIdleMs (default 24h, matching the runtime's paused TTL), so a run awaiting approval keeps its browser. A swept entry leaves a tombstone so a later resume fails with a clear "expired or was swept" error instead of silently continuing in a fresh browser. Concurrent CDP calls share one in-flight socket connect instead of leaking the loser's WebSocket. Session-store locks wrap storage operations only — liveness probes and session create/delete happen outside the lock (with a commit re-check; a racing create's redundant session is deleted), so a hung Browser Rendering call can't serialize other session operations.
  • Stable attach handlescdp.attachToTarget returns { sessionId } where the id is a stable handle bound to the target (not a raw CDP session id), so handles recorded before a pause still work after the resume reconnects. The object shape mirrors the real Target.attachToTarget response, which is what models expect.
  • Model-actionable CDP errors — a "method wasn't found" failure on a send without a sessionId explains that page-scoped commands need cdp.attachToTarget first, and a missing targetId explains how to list/create targets.
  • `createBrowserTools({ ctx, browser, loader, session? })` (AI SDK and TanStack AI variants) now requires the hosting Durable Object's ctx and returns { browser_execute }; createBrowserRuntime additionally exposes the runtime handle and connector for host-side wiring (approvals, sessionInfo/closeSession/sweep). The previous browser_search/flat-tool surface and createBrowserProvider are removed.
  • Worker entries must export the facet class: export { CodemodeRuntime } from "agents/browser".

agents/chat gains pausedExecutionUpdate, a tool-part update that replaces a paused execution's output in the transcript with its resolved outcome (completed / rejected / paused again) — the transcript-side half of human-in-the-loop approvals for durable executions.

useAgent's RPC layer now survives socket replacement. usePartySocket creates a brand-new socket whenever connection options change (async query refresh, enabled toggle, path change) — previously, a call issued against a stale agent reference was buffered inside the permanently-closed old socket and its promise never settled, and a call transmitted just before replacement lost its response with no rejection either.

  • agent.call() (and agent.stub / agent.setState) now route through the live socket, so stale references captured by mount-time effects keep working.
  • RPC requests are only handed to a socket once it's open. Until then they're queued by the hook and flushed on the next open — including on a replacement socket. This is safe: queued requests were never transmitted, so they can't double-execute.
  • Calls whose request was already transmitted are rejected with Connection closed when their socket closes or is replaced (the response is connection-bound and can never arrive). Calls in flight on a newer socket are no longer spuriously rejected by a stale close event from an old socket.
  • Queued calls only follow the connection to the _same_ agent instance. If the hook is re-pointed at a different address (the agent, name, basePath, or path props change) before a queued call could be transmitted, the call is rejected instead of executing against an instance it wasn't composed for.
  • AgentClient similarly keeps buffered (untransmitted) calls pending across transient disconnects — PartySocket re-sends them on reconnect — and only rejects calls the server actually received.
  • Non-streaming calls now have a default 30s timeout as a backstop so lost responses reject instead of hanging. Configure per client via defaultCallTimeout (0 disables) on useAgent / AgentClient, or per call via the existing timeout option (timeout: 0 opts out). Streaming calls are exempt.
  • RPC responses that arrive with no matching pending call (e.g. after a timeout) now log a console.warn instead of being silently discarded.

Patch Changes

The server intentionally sends CF_AGENT_STREAM_RESUMING for the same request from both onConnect and its CF_AGENT_STREAM_RESUME_REQUEST handler. When both offers reached the useAgentChat...

Excerpt shown — open the source for the full document.

Notability

notability 3.0/10

Routine minor version release of agents library.