This release adds four new experimental harnesses and a web_search tool, and reorganizes the experimental Channels API.
- New experimental harnesses:
AiSdkHarness,ThinkHarness,ContainerHarness(which runs Claude Code or Codex in a Container) andOpenCodeHarness, all with the same shape asPiHarness. - New
agents/websearch: aweb_searchtool over Cloudflare's Web Search API for pi, the AI SDK and TanStack AI. - Channels moved to
agents/experimental/channels: Channels is being rebuilt around conversations and turns, and the oldagents/channelsentry point has been removed. The new API includesChannelGateway, the Web Channel and its client, an AI SDK chat transport, and a newnpx agents tuiterminal client. - Fixes: Streams, Tasks,
useAgentChatreconnects,WebChannelClient, MCP credential cleanup onremoveServer(), and x402 with MCP SDK v2.
Minor Changes
-
#2434
0ebeae5Thanks @cjol! - AddAiSdkHarnessfrom the newagents/harness/ai-sdkentry point, which runs each message withstreamTextand keeps sessions and transcripts in the Durable Object, soChannels.forHarnesscan serve an AI SDK model. The same entry point exports the AI SDK message conversions it is built on andcreateSendMessageTool, an AI SDK tool that sends a message to a surface through the gateway. -
#2431
6393265Thanks @cjol! - AddChannelstoagents/experimental/channels: a Lifecycle capability that serves an agent harness's sessions as conversations to every surface that joins them, with durable, resumable responses onStreams. Connect a harness withChannels.forHarness(harness, { channels }). The entry point also exports the turn protocol types and a draft harness interface (AgentHarness), which may change. -
#2429
df4cbd6Thanks @cjol! - Breaking: remove the first pass ofagents/channelsand move Channels toagents/experimental/channels.The
agents/channelsentry points are gone, along withChannelHost, thefallbackandfanoutcomposites, and the AI SDK, TanStack AI and Voice helpers from the first pass. Slack, Telegram and Email move toagents/experimental/channels/slack,agents/experimental/channels/telegramandagents/experimental/channels/email, and keep verified ingress and normalization. Channels is being rebuilt around conversations and turns; the new API is experimental and may change between releases. -
#2432
752cdc3Thanks @cjol! - AddChannelGatewaytoagents/experimental/channels: the Worker entry point that verifies channel webhooks, takes Web Channel upgrades throughweb()fromagents/experimental/channels/web, routes each to the agent object that holds its conversation, and delivers outbound messages. Every Channel that takes ingress needs aparticipantcallback, where the application says who the sender is; Channels never picks an identity. The agent object is the authorization boundary: by default each participant gets one of their own, and aroutecallback can share one between participants or refuse them. -
#2436
062d091Thanks @cjol! - AddWebChannelChatTransport(agents/experimental/channels/web/ai-sdk), an AI SDKChatTransportover the Web Channel, so AI SDK UIs can join a Channels conversation. -
#2433
6e6c58aThanks @cjol! - Add the Web Channel (agents/experimental/channels/web) and its client (agents/experimental/channels/web/client), which connect browsers and terminals to a conversation over the agent's WebSockets: live transcript and turns, approvals, client tool results, and creating, forking, resetting, listing and following conversations.WebSocketsgainsusefor protocol handlers that run before the configured ones. -
#2502
66ae955Thanks @ben-reitz! - Addagents/websearch, aweb_searchtool over Cloudflare's Web Search API for the pi harness, the AI SDK, and TanStack AI. See Search the Web.
Patch Changes
-
#2480
1923625Thanks @cjol! -AiSdkHarness:session.wait(operationId, signal)now rejects straight away whensignalis already aborted, instead of staying pending until the operation settles. -
#2463
1e97e11Thanks @cjol! -AiSdkHarnessno longer fails to start when it has stored sessions.onStartlisted sessions while listing each one's pending work, and Durable Object storage allows one openkv.list()at a time, so a restarted agent stopped acknowledging messages. -
#2502
66ae955Thanks @ben-reitz! -AiSdkHarnesspasses its tools toconvertToModelMessages, so a tool'stoModelOutputalso shapes its results on later turns instead of the model getting the raw output as JSON. -
#2488
98ec934Thanks @ben-reitz! -browserToolnow has its own tool description instead of codemode's generic one. It covers thecdpAPI, common CDP mistakes, and the run time limit, so the model no longer runscodemode.searchfirst. It also explains how to take a smaller screenshot when one is over the 1 MB result limit. See Persistent browser. -
#2437
5a7643eThanks @cjol! - Addnpx agents tui <url>, a terminal client for a conversation's Web Channel.It streams the transcript with highlighted Markdown, collapsible reasoning and tool cards, and shows messages and turns from every surface live. It answers approvals inline, lets a person type a client tool's result, and cancels the running turn with Esc.
--headeradds headers to the WebSocket upgrade, andCF_ACCESS_CLIENT_ID/CF_ACCESS_CLIENT_SECRETare sent as an Access service token. Its dependencies are bundled intodist/cli.js, so installingagentsadds none. -
#2514
1203bddThanks @mattzcarey! - Add experimentalagents/harness/container,agents/harness/container/daemon,agents/harness/container/runtimeandagents/harness/store. Everything in them may change before it stabilizes.ContainerHarnessruns an agent CLI such as Claude Code or Codex in a Cloudflare Container (ctx.container) and drives it from a Durable Object, with the same interface asPiHarness(prompt,submit,wait,abort,messages,pending,session(id),sessions). Typed presets (claudeCode(),codex(),containerAgent()) say what runs: the harness builds the container fromcloudflare/debian-trixieat runtime and snapshots it, so there is no image to build, andContainerEgressadds credentials (apiKey,baseUrl,headers) outside the container, which only ever sees a placeholder key. The container stops after an idle timeout (five minutes by default) and the next prompt starts a new one in which the CLI resumes its own session; a container lost mid-run settles that run ascontainer_lost(or reruns it withonContainerLost: "retry"), and an object evicted mid-run reattaches and replays what it missed.agents/harness/container/runtimeis the daemon's Node runtime withcliAdapter, for images that bring their own CLI;agents/harness/container/daemonis its runtime-agnostic core; andagents/harness/storeis a session store on the object's SQLite that any harness can use. -
#2475
7a4c6c0Thanks @mattzcarey! -MCPClientManager.removeServer()(andAgent.removeMcpServer()) now clears the server's saved OAuth state from Durable Object storage: tokens and client information (including those under earlier client IDs), verifiers, pending states and discovery state. This also works when the server's connection was already closed. Other servers' credentials are untouched, and tokens are not revoked at the OAuth provider. Custom auth providers getinvalidateCredentials("all"). -
#2513
f2f745bThanks @mattzcarey! - Add experimentalagents/harness/opencodeandagents/models/opencode.OpenCodeHarnesshosts OpenCode v2 (@opencode/sdk/workerd) in a Durable Object behind the same interface asPiHarness: OpenCode's tables are kept under anopencode_prefix, a Lifecycle wake job per session brings runs back after eviction, and the harness closes OpenCode when it is idle so the object can hibernate.createAI({ binding })fromagents/models/opencodereturns an OpenCode plugin that serves Workers AI models through theAIbinding. Both APIs are experimental and will change. -
#2458
6bf0378Thanks @cjol! - Streams: a liveread/readBatchesno longer misses chunks appended while the consumer is still handling the previous batch. It re-polls before waiting for the next append, so a reader no longer stalls until a later append or the stream's end. -
#2512
bda70b5Thanks @mattzcarey! -Streamsrechecks that the v1 chunk table still exists before using it. Two Streams instances on one object, such as a host's own and the oneThinkHarnesskeeps, each remembered the table separately, so after one folded the last v1 rows and dropped the table the other failed its next append withno such table. -
#2476
9f92f6fThanks @mattzcarey! - A routed Task wake whose owner facet is no longer in the root's registry now ends quietly instead of throwingInvalid job outcomeon the alarm. -
#2512
bda70b5Thanks @mattzcarey! - AddThinkHarnessfrom the new experimentalagents/harness/thinkentry point. It runs Think's agent loop as a Lifecycle capability with the same shape asPiHarness(prompt,submit,wait,abort,sessions,session(id)), and implements the shared harness interface Channels serves.The harness owns its storage: transcripts in the Sessions tables, with
branches(),search()andcompact()on each session, and in-flight model output in the Streams tables, so a host installs only the harness. Each session has one Lifecycle wake job, so a turn interrupted by an eviction picks up from its last durable write: a cut-short model call is rebuilt from its stream and continued in the same message, and a cut-short tool call is reported to the model, or rerun when the tool carriesrecovery: "rerun".ThinkChatserves a session over Think'suseAgentChatWebSocket protocol. See Think harness. -
#2462
2f3176bThanks @cjol! -agents tuinow handles Cloudflare Access: for a URL behind Access, it gets a token fromcloudflared(logging in through the browser the first time) and sends it on the WebSocket upgrade.CF_ACCESS_CLIENT_ID/CF_ACCESS_CLIENT_SECRETand explicit--headercredentials still take precedence. -
#2481
fcc23f4Thanks @cjol! -agents tuidrops a#fragmentfrom the URL it is given, since a WebSocket URL cannot carry one. Previously such a URL made the client exit before connecting. -
#2496
cf7c9e3Thanks @ben-reitz! -useAgentChatreplaces a cut-off assistant reply with the server's copy after the WebSocket drops mid-stream. Think sends that copy on reconnect; AIChatAgent doesn't yet. -
#2479
f3d13e9Thanks @cjol! -WebChannelClient:send()andlistConversations()now reject once the client is closed instead of waiting forever.follow()andclose()also cancel a reconnect still waiting after a dropped connection, so it no longer opens a second socket that replaces the followed one or reconnects a closed client. -
#2494
04922ecThanks @ben-reitz! -withX402(...).paidToolnow reads the payment from MCP SDK v2 servers, and matches thePAYMENT-SIGNATURE/X-PAYMENTheaders case-insensitively.