github cloudflare/agents agents@0.27.0

5 hours ago

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) and OpenCodeHarness, all with the same shape as PiHarness.
  • New agents/websearch: a web_search tool 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 old agents/channels entry point has been removed. The new API includes ChannelGateway, the Web Channel and its client, an AI SDK chat transport, and a new npx agents tui terminal client.
  • Fixes: Streams, Tasks, useAgentChat reconnects, WebChannelClient, MCP credential cleanup on removeServer(), and x402 with MCP SDK v2.

Minor Changes

  • #2434 0ebeae5 Thanks @cjol! - Add AiSdkHarness from the new agents/harness/ai-sdk entry point, which runs each message with streamText and keeps sessions and transcripts in the Durable Object, so Channels.forHarness can serve an AI SDK model. The same entry point exports the AI SDK message conversions it is built on and createSendMessageTool, an AI SDK tool that sends a message to a surface through the gateway.

  • #2431 6393265 Thanks @cjol! - Add Channels to agents/experimental/channels: a Lifecycle capability that serves an agent harness's sessions as conversations to every surface that joins them, with durable, resumable responses on Streams. Connect a harness with Channels.forHarness(harness, { channels }). The entry point also exports the turn protocol types and a draft harness interface (AgentHarness), which may change.

  • #2429 df4cbd6 Thanks @cjol! - Breaking: remove the first pass of agents/channels and move Channels to agents/experimental/channels.

    The agents/channels entry points are gone, along with ChannelHost, the fallback and fanout composites, and the AI SDK, TanStack AI and Voice helpers from the first pass. Slack, Telegram and Email move to agents/experimental/channels/slack, agents/experimental/channels/telegram and agents/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 752cdc3 Thanks @cjol! - Add ChannelGateway to agents/experimental/channels: the Worker entry point that verifies channel webhooks, takes Web Channel upgrades through web() from agents/experimental/channels/web, routes each to the agent object that holds its conversation, and delivers outbound messages. Every Channel that takes ingress needs a participant callback, 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 a route callback can share one between participants or refuse them.

  • #2436 062d091 Thanks @cjol! - Add WebChannelChatTransport (agents/experimental/channels/web/ai-sdk), an AI SDK ChatTransport over the Web Channel, so AI SDK UIs can join a Channels conversation.

  • #2433 6e6c58a Thanks @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. WebSockets gains use for protocol handlers that run before the configured ones.

  • #2502 66ae955 Thanks @ben-reitz! - Add agents/websearch, a web_search tool over Cloudflare's Web Search API for the pi harness, the AI SDK, and TanStack AI. See Search the Web.

Patch Changes

  • #2480 1923625 Thanks @cjol! - AiSdkHarness: session.wait(operationId, signal) now rejects straight away when signal is already aborted, instead of staying pending until the operation settles.

  • #2463 1e97e11 Thanks @cjol! - AiSdkHarness no longer fails to start when it has stored sessions. onStart listed sessions while listing each one's pending work, and Durable Object storage allows one open kv.list() at a time, so a restarted agent stopped acknowledging messages.

  • #2502 66ae955 Thanks @ben-reitz! - AiSdkHarness passes its tools to convertToModelMessages, so a tool's toModelOutput also shapes its results on later turns instead of the model getting the raw output as JSON.

  • #2488 98ec934 Thanks @ben-reitz! - browserTool now has its own tool description instead of codemode's generic one. It covers the cdp API, common CDP mistakes, and the run time limit, so the model no longer runs codemode.search first. It also explains how to take a smaller screenshot when one is over the 1 MB result limit. See Persistent browser.

  • #2437 5a7643e Thanks @cjol! - Add npx 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. --header adds headers to the WebSocket upgrade, and CF_ACCESS_CLIENT_ID / CF_ACCESS_CLIENT_SECRET are sent as an Access service token. Its dependencies are bundled into dist/cli.js, so installing agents adds none.

  • #2514 1203bdd Thanks @mattzcarey! - Add experimental agents/harness/container, agents/harness/container/daemon, agents/harness/container/runtime and agents/harness/store. Everything in them may change before it stabilizes.

    ContainerHarness runs 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 as PiHarness (prompt, submit, wait, abort, messages, pending, session(id), sessions). Typed presets (claudeCode(), codex(), containerAgent()) say what runs: the harness builds the container from cloudflare/debian-trixie at runtime and snapshots it, so there is no image to build, and ContainerEgress adds 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 as container_lost (or reruns it with onContainerLost: "retry"), and an object evicted mid-run reattaches and replays what it missed. agents/harness/container/runtime is the daemon's Node runtime with cliAdapter, for images that bring their own CLI; agents/harness/container/daemon is its runtime-agnostic core; and agents/harness/store is a session store on the object's SQLite that any harness can use.

  • #2475 7a4c6c0 Thanks @mattzcarey! - MCPClientManager.removeServer() (and Agent.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 get invalidateCredentials("all").

  • #2513 f2f745b Thanks @mattzcarey! - Add experimental agents/harness/opencode and agents/models/opencode. OpenCodeHarness hosts OpenCode v2 (@opencode/sdk/workerd) in a Durable Object behind the same interface as PiHarness: OpenCode's tables are kept under an opencode_ 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 }) from agents/models/opencode returns an OpenCode plugin that serves Workers AI models through the AI binding. Both APIs are experimental and will change.

  • #2458 6bf0378 Thanks @cjol! - Streams: a live read/readBatches no 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 bda70b5 Thanks @mattzcarey! - Streams rechecks that the v1 chunk table still exists before using it. Two Streams instances on one object, such as a host's own and the one ThinkHarness keeps, each remembered the table separately, so after one folded the last v1 rows and dropped the table the other failed its next append with no such table.

  • #2476 9f92f6f Thanks @mattzcarey! - A routed Task wake whose owner facet is no longer in the root's registry now ends quietly instead of throwing Invalid job outcome on the alarm.

  • #2512 bda70b5 Thanks @mattzcarey! - Add ThinkHarness from the new experimental agents/harness/think entry point. It runs Think's agent loop as a Lifecycle capability with the same shape as PiHarness (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() and compact() 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 carries recovery: "rerun". ThinkChat serves a session over Think's useAgentChat WebSocket protocol. See Think harness.

  • #2462 2f3176b Thanks @cjol! - agents tui now handles Cloudflare Access: for a URL behind Access, it gets a token from cloudflared (logging in through the browser the first time) and sends it on the WebSocket upgrade. CF_ACCESS_CLIENT_ID / CF_ACCESS_CLIENT_SECRET and explicit --header credentials still take precedence.

  • #2481 fcc23f4 Thanks @cjol! - agents tui drops a #fragment from the URL it is given, since a WebSocket URL cannot carry one. Previously such a URL made the client exit before connecting.

  • #2496 cf7c9e3 Thanks @ben-reitz! - useAgentChat replaces 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 f3d13e9 Thanks @cjol! - WebChannelClient: send() and listConversations() now reject once the client is closed instead of waiting forever. follow() and close() 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 04922ec Thanks @ben-reitz! - withX402(...).paidTool now reads the payment from MCP SDK v2 servers, and matches the PAYMENT-SIGNATURE / X-PAYMENT headers case-insensitively.

Don't miss a new agents release

NewReleases is sending notifications on new releases.