Skip to content

Latest commit

 

History

History
2954 lines (1780 loc) · 173 KB

File metadata and controls

2954 lines (1780 loc) · 173 KB

@cloudflare/agents

0.8.7

Patch Changes

  • #1207 b1da19c Thanks @threepointone! - Add transport: "auto" option for McpAgent.serve() that serves both Streamable HTTP and legacy SSE on the same endpoint. Capable clients use Streamable HTTP automatically, while older SSE-only clients continue to work transparently.

  • #1217 6801966 Thanks @threepointone! - update partyserver

0.8.6

Patch Changes

  • #1201 fc6d214 Thanks @threepointone! - Bump @modelcontextprotocol/sdk from 1.26.0 to 1.28.0 and populate url on MCP RequestInfo so tool handlers can access the request URL and query parameters

0.8.5

Patch Changes

  • #1198 dde826e Thanks @threepointone! - Derive callbackHost from connection.uri in addMcpServer when called from a @callable method over WebSocket. Previously, callbackHost had to be passed explicitly (or read from an env var) because the WebSocket onMessage context has no HTTP request to derive the host from. Now the host is automatically extracted from the WebSocket connection's original upgrade URL, so addMcpServer("name", url) works without any extra options in callables. Also adds vite/client to the shared agents/tsconfig types for TS6 compatibility with CSS side-effect imports.

0.8.4

Patch Changes

  • #1190 b39dbff Thanks @threepointone! - Export shared agents/tsconfig and agents/vite so examples and internal projects are self-contained. The agents/vite plugin handles TC39 decorator transforms for @callable() until Oxc lands native support.

0.8.3

Patch Changes

  • #1182 c03e87b Thanks @dmmulroy! - Fix elicitInput() hanging on RPC transport by intercepting elicitation responses in handleMcpMessage() and adding awaitPendingResponse() to RPCServerTransport

0.8.2

Patch Changes

  • #1181 e9bace9 Thanks @threepointone! - Fix alarm handler resilience: move JSON.parse(row.payload) inside try/catch and guard warning emission so a single failure cannot break processing of remaining schedule rows.

0.8.1

Patch Changes

  • #1176 750446b Thanks @threepointone! - Remove local development workarounds for workflow instance methods now that pause(), resume(), restart(), and terminate() are supported in wrangler dev

0.8.0

Minor Changes

  • #1152 16cc622 Thanks @threepointone! - feat: expose readable state property on useAgent and AgentClient

    Both useAgent (React) and AgentClient (vanilla JS) now expose a state property that tracks the current agent state. Previously, state was write-only via setState() — reading state required manually tracking it through the onStateUpdate callback.

    React (useAgent)

    const agent = useAgent<GameAgent, GameState>({
      agent: "game-agent",
      name: "room-123",
    });
    
    // Read state directly — no need for separate useState + onStateUpdate
    return <div>Score: {agent.state?.score}</div>;
    
    // Spread for partial updates — works correctly now
    agent.setState({ ...agent.state, score: agent.state.score + 10 });

    agent.state is reactive — the component re-renders when state changes from either the server or client-side setState().

    Vanilla JS (AgentClient)

    const client = new AgentClient<GameAgent>({
      agent: "game-agent",
      name: "room-123",
      host: "your-worker.workers.dev",
    });
    
    // State updates synchronously on setState and server broadcasts
    client.setState({ score: 100 });
    console.log(client.state); // { score: 100 }

    Backward compatible

    The onStateUpdate callback continues to work exactly as before. The new state property is additive — it provides a simpler alternative to manual state tracking for the common case.

    Type: State | undefined

    State starts as undefined and is populated when the server sends state on connect (from initialState) or when setState() is called. Use optional chaining (agent.state?.field) for safe access.

  • #1154 74a018a Thanks @threepointone! - feat: idempotent schedule() to prevent row accumulation across DO restarts

    schedule() now supports an idempotent option that deduplicates by (type, callback, payload), preventing duplicate rows from accumulating when called repeatedly (e.g., in onStart()).

    Cron schedules are idempotent by default. Calling schedule("0 * * * *", "tick") multiple times with the same callback, cron expression, and payload returns the existing schedule instead of creating a duplicate. Set { idempotent: false } to override.

    Delayed and scheduled (Date) types support opt-in idempotency:

    async onStart() {
      // Safe across restarts — only one row exists at a time
      await this.schedule(60, "maintenance", undefined, { idempotent: true });
    }

    New warnings for common foot-guns:

    • schedule() called inside onStart() without { idempotent: true } now emits a console.warn with actionable guidance (once per callback, skipped for cron and when idempotent is explicitly set)
    • alarm() processing ≥10 stale one-shot rows for the same callback emits a console.warn and a schedule:duplicate_warning diagnostics channel event
  • #1146 b74e108 Thanks @threepointone! - feat: strongly-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 that useAgent already provides.

    New: typed call and stub

    When an agent type is provided, call() infers method names, argument types, and return types from the agent's methods. A new stub property provides a direct RPC-style proxy — call agent methods as if they were local functions:

    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:

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

    Backward compatible

    Existing untyped usage continues to work without changes:

    const client = new AgentClient({ agent: "my-agent", host: "..." });
    client.call("anyMethod", [args]); // still works
    client.call<number>("add", [1, 2]); // explicit return type still works
    client.stub.anyMethod("arg1", 123); // untyped stub also available

    The previous AgentClient<State> pattern is preserved — new AgentClient<{ count: number }>({...}) still correctly types onStateUpdate and leaves call/stub untyped.

    Breaking: call is now an instance property instead of a prototype method

    AgentClient.prototype.call no longer exists. The call function is assigned per-instance in the constructor (via .bind()). This is required for the conditional type system to switch between typed and untyped signatures. Normal usage (client.call(...)) is unaffected, but code that reflects on the prototype or subclasses that override call as a method may need adjustment.

    Shared type utilities

    The RPC type utilities (AgentMethods, AgentStub, RPCMethods, etc.) are now exported from agents/client so they can be shared between AgentClient and useAgent, and are available to consumers who need them for advanced typing scenarios.

  • #1138 36e2020 Thanks @threepointone! - Drop Zod v3 from peer dependency range — now requires zod ^4.0.0. Replace dynamic import("ai") with z.fromJSONSchema() from Zod 4 for MCP tool schema conversion, removing the ai runtime dependency from the agents core. Remove ensureJsonSchema().

Patch Changes

  • #1147 1f85b06 Thanks @threepointone! - Replace schedule-based keepAlive with lightweight ref-counted alarms

    • keepAlive() no longer creates schedule rows or emits schedule:create/schedule:execute/schedule:cancel observability events — it uses an in-memory ref count and feeds directly into _scheduleNextAlarm()
    • multiple concurrent keepAlive() callers now share a single alarm cycle instead of each creating their own interval schedule row
    • add _onAlarmHousekeeping() hook (called on every alarm cycle) for extensions like the fiber mixin to run housekeeping without coupling to the scheduling system
    • bump internal schema to v2 with a migration that cleans up orphaned _cf_keepAliveHeartbeat schedule rows from the previous implementation
    • remove @experimental from keepAlive() and keepAliveWhile()

0.7.9

Patch Changes

  • #1128 01cfb52 Thanks @threepointone! - Add sessionAffinity getter to Agent base class for Workers AI prefix-cache optimization. Returns the Durable Object ID, which is globally unique and stable per agent instance. Pass it as the sessionAffinity option when creating a Workers AI model to route requests from the same agent to the same backend replica.

0.7.8

Patch Changes

  • #1122 a16e74d Thanks @threepointone! - Remove agents/experimental/workspace export. Workspace now lives in @cloudflare/shell — import it from there instead.

0.7.7

Patch Changes

  • #1120 6a6108c Thanks @whoiskatrin! - Restore lost Durable Object alarms when scheduleEvery() reuses an existing interval schedule after restart.

0.7.6

Patch Changes

  • #1090 a91b598 Thanks @threepointone! - Allow MCP clients to connect to localhost and loopback URLs again for local development while continuing to block private, link-local, and metadata endpoints.

  • #1088 16e2833 Thanks @threepointone! - Embed sub-agent (facet) API into the Agent base class. Adds subAgent(), abortSubAgent(), and deleteSubAgent() methods directly on Agent, replacing the experimental withSubAgents mixin. Uses composite facet keys for class-aware naming, guards scheduling and keepAlive in facets, and persists the facet flag to storage so it survives hibernation.

  • #1085 0b73a74 Thanks @threepointone! - Remove unnecessary storage operations in McpAgent:

    • Fix redundant props read in onStart: skip storage.get("props") when props are passed directly (only read from storage on hibernation recovery)
    • Replace elicitation storage polling with in-memory Promise/resolver: eliminates repeated storage.get/put/delete calls (up to 6 per elicitation) in favor of zero-storage in-memory signaling
  • #1086 e8195e7 Thanks @threepointone! - Simplify Agent storage: schema version gating and single-row state

    • Skip redundant DDL migrations on established DOs by tracking schema version in cf_agents_state
    • Eliminate STATE_WAS_CHANGED row — state persistence now uses a single row with row-existence check, correctly handling falsy values (null, 0, false, "")
    • Clean up legacy STATE_WAS_CHANGED rows during migration
    • Add schema DDL snapshot test that breaks if table definitions change without bumping CURRENT_SCHEMA_VERSION
    • Fix corrupted state test helper that was using incorrect row IDs
  • #1081 933b00f Thanks @threepointone! - Add default console.error logging to onWorkflowError() so unhandled workflow errors are visible in logs

  • #1089 a1eab1d Thanks @threepointone! - Add Workspace class — durable file storage for any Agent with hybrid SQLite+R2 backend and optional just-bash shell execution. Usage: new Workspace(this, { r2, idPrefix }). Import from agents/workspace.

0.7.5

Patch Changes

  • #1071 6312684 Thanks @threepointone! - Fix missing await on _workflow_updateState RPC calls in AgentWorkflow._wrapStep() for updateAgentState, mergeAgentState, and resetAgentState, which could cause state updates to be silently lost.

  • #1069 b5238de Thanks @threepointone! - Add Workspace class — durable file storage for any Agent with hybrid SQLite+R2 backend and optional just-bash shell execution. Includes BashSession for multi-step shell workflows with persistent cwd and env across exec calls, and cwd option on bash(). Usage: new Workspace(this, { r2, r2Prefix }). Import from agents/experimental/workspace.

0.7.4

Patch Changes

  • #1063 4ace1d4 Thanks @threepointone! - Fix CHECK constraint migration for cf_agents_schedules table to include 'interval' type, allowing scheduleEvery() and keepAlive() to work on DOs created with older SDK versions.

0.7.3

Patch Changes

  • #1057 c804c73 Thanks @threepointone! - Updated dependencies.

  • #1057 c804c73 Thanks @threepointone! - Fix workflow RPC callbacks bypassing Agent initialization. The _workflow_handleCallback, _workflow_broadcast, and _workflow_updateState methods now call __unsafe_ensureInitialized() before executing, ensuring this.name is hydrated and onStart() has been called even when the Durable Object wakes via native RPC.

0.7.2

Patch Changes

  • #1050 6157741 Thanks @ask-bonk! - Fix Agent alarm() bypassing PartyServer's initialization

    The Agent class defined alarm as a public readonly arrow function property, which completely shadowed PartyServer's alarm() prototype method. This meant #ensureInitialized() was never called when a Durable Object woke via alarm (e.g. from scheduleEvery), causing this.name to throw and onStart to never run.

    Converted alarm from an arrow function property to a regular async method that calls super.alarm() before processing scheduled tasks. Also added an onAlarm() no-op override to suppress PartyServer's default warning log.

  • #1052 f1e2bfa Thanks @ask-bonk! - Make scheduleEvery() idempotent

    scheduleEvery() now deduplicates by the combination of callback name, interval, and payload: calling it multiple times with the same arguments returns the existing schedule instead of creating a duplicate. A different interval or payload creates a separate, independent schedule.

    This fixes the common pattern of calling scheduleEvery() inside onStart(), which runs on every Durable Object wake. Previously each wake created a new interval schedule, leading to a thundering herd of duplicate executions.

0.7.1

Patch Changes

  • #1046 2cde136 Thanks @threepointone! - Add agent and name fields to observability events, identifying which agent class and instance emitted each event.

    New events: disconnect (WebSocket close), email:receive, email:reply, queue:create, and a new agents:email channel.

    Make _emit protected so subclasses can use it. Update AIChatAgent to use _emit so message/tool events carry agent identity.

0.7.0

Minor Changes

  • #1024 e9ae070 Thanks @threepointone! - Overhaul observability: diagnostics_channel, leaner events, error tracking.

    Breaking changes to agents/observability types

    • BaseEvent: Removed id and displayMessage fields. Events now contain only type, payload, and timestamp. The payload type is now strict — accessing undeclared fields is a type error. Narrow on event.type before accessing payload properties.
    • Observability.emit(): Removed the optional ctx second parameter.
    • AgentObservabilityEvent: Split combined union types so each event has its own discriminant (enables proper Extract-based type narrowing). Added new error event types.

    If you have a custom Observability implementation, update your emit signature to emit(event: ObservabilityEvent): void.

    diagnostics_channel replaces console.log

    The default genericObservability implementation no longer logs every event to the console. Instead, events are published to named diagnostics channels using the Node.js diagnostics_channel API. Publishing to a channel with no subscribers is a no-op, eliminating logspam.

    Seven named channels, one per event domain:

    • agents:state — state sync events
    • agents:rpc — RPC method calls and errors
    • agents:message — message request/response/clear/cancel/error + tool result/approval
    • agents:schedule — schedule and queue create/execute/cancel/retry/error events
    • agents:lifecycle — connection and destroy events
    • agents:workflow — workflow start/event/approve/reject/terminate/pause/resume/restart
    • agents:mcp — MCP client connect/authorize/discover events

    New error events

    Error events are now emitted at failure sites instead of (or alongside) console.error:

    • rpc:error — RPC method failures (includes method name and error message)
    • schedule:error — schedule callback failures after all retries exhausted
    • queue:error — queue callback failures after all retries exhausted

    Reduced boilerplate

    All 20+ inline emit blocks in the Agent class have been replaced with a private _emit() helper that auto-generates timestamps, reducing each call site from ~10 lines to 1.

    Typed subscribe helper

    A new subscribe() function is exported from agents/observability with full type narrowing per channel:

    import { subscribe } from "agents/observability";
    
    const unsub = subscribe("rpc", (event) => {
      // event is fully typed as rpc | rpc:error
      console.log(event.payload.method);
    });

    Tail Worker integration

    In production, all diagnostics channel messages are automatically forwarded to Tail Workers via event.diagnosticsChannelEvents — no subscription needed in the agent itself.

    TracingChannel potential

    The diagnostics_channel API also provides TracingChannel for start/end/error spans with AsyncLocalStorage integration, opening the door to end-to-end tracing of RPC calls, workflow steps, and schedule executions.

  • #1029 c898308 Thanks @threepointone! - Add experimental keepAlive() and keepAliveWhile() methods to the Agent class. Keeps the Durable Object alive via alarm heartbeats (every 30 seconds), preventing idle eviction during long-running work. keepAlive() returns a disposer function; keepAliveWhile(fn) runs an async function and automatically cleans up the heartbeat when it completes.

    AIChatAgent now automatically calls keepAliveWhile() during _reply() streaming, preventing idle eviction during long LLM generations.

Patch Changes

  • #1020 70ebb05 Thanks @threepointone! - udpate dependencies

  • #1035 24cf279 Thanks @threepointone! - MCP protocol handling improvements:

    • JSON-RPC error responses: RPCServerTransport.handle() now returns a proper JSON-RPC -32600 Invalid Request error response for malformed messages instead of throwing an unhandled exception. This aligns with the JSON-RPC 2.0 spec requirement that servers respond with error objects.
    • McpAgent protocol message suppression: McpAgent now overrides shouldSendProtocolMessages() to suppress CF_AGENT_IDENTITY, CF_AGENT_STATE, and CF_AGENT_MCP_SERVERS frames on MCP transport connections (detected via the cf-mcp-method header). Regular WebSocket connections to a hybrid McpAgent are unaffected.
    • CORS warning removed: Removed the one-time warning about Authorization in Access-Control-Allow-Headers with wildcard origin. The warning was noisy and unhelpful — the combination is valid for non-credentialed requests and does not pose a real security risk.
  • #996 baf6751 Thanks @threepointone! - Fix race condition where MCP tools are intermittently unavailable in onChatMessage after hibernation.

    agents: Added MCPClientManager.waitForConnections(options?) which awaits all in-flight connection and discovery operations. Accepts an optional { timeout } in milliseconds. Background restore promises from restoreConnectionsFromStorage() are now tracked so callers can wait for them to settle.

    @cloudflare/ai-chat: Added waitForMcpConnections opt-in config on AIChatAgent. Set to true to wait indefinitely, or { timeout: 10_000 } to cap the wait. Default is false (non-blocking, preserving existing behavior). For lower-level control, call this.mcp.waitForConnections() directly in your onChatMessage.

  • #1035 24cf279 Thanks @threepointone! - Fix this.sql to throw SqlError directly instead of routing through onError

    Previously, SQL errors from this.sql were passed to this.onError(), which by default logged the error and re-threw it. This caused confusing double error logs and made it impossible to catch SQL errors with a simple try/catch around this.sql calls if onError was overridden to swallow errors.

    Now, this.sql wraps failures in SqlError (which includes the query string for debugging) and throws directly. The onError lifecycle hook is reserved for WebSocket connection errors and unhandled server errors, not SQL errors.

  • #1022 c2bfd3c Thanks @threepointone! - Remove redundant unawaited updateProps calls in MCP transport handlers that caused sporadic "Failed to pop isolated storage stack frame" errors in test environments. Props are already delivered through getAgentByNameonStart, making the extra calls unnecessary. Also removes the RPC experimental warning from addMcpServer.

  • #1003 d24936c Thanks @threepointone! - Fix: throw new Error() in AgentWorkflow now triggers onWorkflowError on the Agent

    Previously, throwing an error inside a workflow's run() method would halt the workflow but never notify the Agent via onWorkflowError. Only explicit step.reportError() calls triggered the callback, but those did not halt the workflow.

    Now, unhandled errors in run() are automatically caught and reported to the Agent before re-throwing. A double-notification guard (_errorReported flag) ensures that if step.reportError() was already called before the throw, the auto-report is skipped.

  • #1040 766f20b Thanks @threepointone! - Changed addMcpServer dedup logic to match on both server name AND URL for HTTP transport. Previously, calling addMcpServer with the same name but a different URL would silently return the stale connection. Now each unique (name, URL) pair is treated as a separate connection. RPC transport continues to dedup by name only.

  • #997 a570ea5 Thanks @threepointone! - Security hardening for Agent and MCP subsystems:

    • SSRF protection: MCP client now validates URLs before connecting, blocking private/internal IP addresses (RFC 1918, loopback, link-local, cloud metadata endpoints, IPv6 unique local and link-local ranges)
    • OAuth log redaction: Removed OAuth state parameter value from consumeState warning logs to prevent sensitive data leakage
    • Error sanitization: MCP server error strings are now sanitized (control characters stripped, truncated to 500 chars) before broadcasting to clients to mitigate XSS risk
    • sendIdentityOnConnect warning: When using custom routing (where the instance name is not visible in the URL), a one-time console warning now informs developers that the instance name is being sent to clients. Set static options = { sendIdentityOnConnect: false } to opt out, or true to silence the warning.
  • #992 4fcf179 Thanks @Muhammad-Bin-Ali! - Fix email routing to handle lowercased agent names from email infrastructure

    Email servers normalize addresses to lowercase, so SomeAgent+id@domain.com arrives as someagent+id@domain.com. The router now registers a lowercase key in addition to the original binding name and kebab-case version, so all three forms resolve correctly.

0.6.0

Minor Changes

  • #565 0e9a607 Thanks @mattzcarey! - Add RPC transport for MCP: connect an Agent to an McpAgent via Durable Object bindings

    New feature: addMcpServer with DO binding

    Agents can now connect to McpAgent instances in the same Worker using RPC transport — no HTTP, no network overhead. Pass the Durable Object namespace directly:

    // In your Agent
    await this.addMcpServer("counter", env.MY_MCP);
    
    // With props
    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 proper TypeScript overloads, so HTTP and RPC paths are type-safe and cannot be mixed.

    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. No need to manually re-establish connections in onStart().

    Deduplication

    Calling addMcpServer with the same server name multiple times (e.g., across hibernation cycles) now returns the existing connection instead of creating duplicates. This applies to both RPC and HTTP connections. Connection IDs are stable across hibernation restore.

    Other changes

    • Rewrote RPCClientTransport to accept a DurableObjectNamespace and create the stub internally via getServerByName from partyserver, instead of requiring a pre-constructed stub
    • Rewrote RPCServerTransport to drop session management (unnecessary for DO-scoped RPC) and use JSONRPCMessageSchema from the MCP SDK for validation instead of 170 lines of hand-written validation
    • Removed _resolveRpcBinding, _buildRpcTransportOptions, _buildHttpTransportOptions, and _connectToMcpServerInternal from the Agent base class — RPC transport logic no longer leaks into index.ts
    • Added AddRpcMcpServerOptions type (discriminated from AddMcpServerOptions) so props is only available when passing a binding
    • Added RPC_DO_PREFIX constant used consistently across all RPC naming
    • Fixed MCPClientManager.callTool passing serverId through to conn.client.callTool (it should be stripped before the call)
    • Added getRpcServersFromStorage() and saveRpcServerToStorage() to MCPClientManager for hibernation persistence
    • restoreConnectionsFromStorage now skips RPC servers (restored separately by the Agent class which has access to env)
    • Reduced rpc.ts from 609 lines to 245 lines
    • Reduced types.ts from 108 lines to 26 lines
    • Updated mcp-rpc-transport example to use Workers AI (no API keys needed), Kumo/agents-ui components, and Tailwind CSS
    • Updated MCP transports documentation

Patch Changes

  • #973 969fbff Thanks @threepointone! - Update dependencies

  • #960 179b8cb Thanks @mattzcarey! - Harden JSON Schema to TypeScript converter for production use

    • Add depth and circular reference guards to prevent stack overflows on recursive or deeply nested schemas
    • Add $ref resolution for internal JSON Pointers (#/definitions/..., #/$defs/..., #)
    • Add tuple support (prefixItems for JSON Schema 2020-12, array items for draft-07)
    • Add OpenAPI 3.0 nullable: true support across all schema branches
    • Fix string escaping in enum/const values, property names (control chars, U+2028/U+2029), and JSDoc comments (*/)
    • Add per-tool error isolation in generateTypes() so one malformed schema cannot crash the pipeline
    • Guard missing inputSchema in getAITools() with a fallback to { type: "object" }
    • Add per-tool error isolation in getAITools() so one bad MCP tool does not break the entire tool set
  • #963 b848008 Thanks @threepointone! - Make callbackHost optional in addMcpServer for non-OAuth servers

    Previously, addMcpServer() always required a callbackHost (either explicitly or derived from the request context) and eagerly created an OAuth auth provider, even when connecting to MCP servers that do not use OAuth. This made simple non-OAuth connections unnecessarily difficult, especially from WebSocket callable methods where the request context origin is unreliable.

    Now, callbackHost and the OAuth auth provider are only required when the MCP server actually needs OAuth (returns a 401/AUTHENTICATING state). For non-OAuth servers, addMcpServer("name", url) works with no additional options. If an OAuth server is encountered without a callbackHost, a clear error is thrown: "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.

  • 97c6702 Thanks @threepointone! - Add one-time console warning when using RPC transport (DO binding) with addMcpServer, noting the API is experimental and linking to the feedback issue.

0.5.1

Patch Changes

  • #954 943c407 Thanks @threepointone! - update dependencies

  • #944 e729b5d Thanks @threepointone! - Export DurableObjectOAuthClientProvider from top-level agents package and fix restoreConnectionsFromStorage() to use the Agent's createMcpOAuthProvider() override instead of hardcoding the default provider

  • #850 2cb12df Thanks @Muhammad-Bin-Ali! - Fix: MCP OAuth callback errors are now returned as structured results instead of throwing unhandled exceptions. Errors with an active connection properly transition to "failed" state and are surfaced to clients via WebSocket broadcast.

0.5.0

This release adds per-connection protocol message control and a built-in retry system. Agents can now suppress JSON protocol frames for binary-only clients (MQTT, IoT devices) while keeping RPC and regular messaging working — useful for Durable Objects that serve mixed connection types. The new this.retry() method and per-task retry options bring exponential backoff with jitter to scheduling, queues, and MCP connections without external dependencies. This release also improves scheduling ergonomics with synchronous getter methods, a cleaner discriminated union schema, and fixes for hibernation, deep type recursion, and SSE keepalives.

Minor Changes

  • #920 4dea3bd Thanks @threepointone! - Add shouldSendProtocolMessages hook and isConnectionProtocolEnabled predicate for per-connection control of protocol text frames

    Adds the ability to suppress protocol messages (CF_AGENT_IDENTITY, CF_AGENT_STATE, CF_AGENT_MCP_SERVERS) on a per-connection basis. This is useful for binary-only clients (e.g. MQTT devices) that cannot handle JSON text frames.

    Override shouldSendProtocolMessages(connection, ctx) to return false for connections that should not receive protocol messages. These connections still fully participate in RPC and regular messaging — only the automatic protocol text frames are suppressed, both on connect and during broadcasts.

    Use isConnectionProtocolEnabled(connection) to check a connection's protocol status at any time.

    Also fixes isConnectionReadonly to correctly survive Durable Object hibernation by re-wrapping the connection when the in-memory accessor cache has been cleared.

  • #874 a6ec9b0 Thanks @threepointone! - Add retry utilities: this.retry(), per-task retry options, and RetryOptions type

    • this.retry(fn, options?) — retry any async operation with exponential backoff and jitter. Accepts optional shouldRetry predicate to bail early on non-retryable errors.
    • queue(), schedule(), scheduleEvery() accept { retry?: RetryOptions } for per-task retry configuration, persisted in SQLite alongside the task.
    • addMcpServer() accepts { retry?: RetryOptions } for configurable MCP connection retries.
    • RetryOptions type is exported for TypeScript consumers.
    • Retry options are validated eagerly at enqueue/schedule time — invalid values throw immediately.
    • Class-level retry defaults via static options = { retry: { ... } } — override defaults for an entire agent class.
    • Internal retries added for workflow operations (terminateWorkflow, pauseWorkflow, etc.) with Durable Object-aware error detection.

Patch Changes

  • #899 04c6411 Thanks @threepointone! - Fix React hooks exhaustive-deps warning in useAgent by referencing cacheInvalidatedAt inside useMemo body.

  • #904 d611b94 Thanks @ask-bonk! - Fix TypeScript "excessively deep" error with deeply nested state types

    Add a depth counter to CanSerialize and IsSerializableParam types that bails out to true after 10 levels of recursion. This prevents the "Type instantiation is excessively deep and possibly infinite" error when using deeply nested types like AI SDK CoreMessage[] as agent state.

  • #911 67b1601 Thanks @threepointone! - Update all dependencies and fix breaking changes.

    Update all dependencies, add required aria-label props to Kumo Button components with shape (now required for accessibility), and fix state test for constructor-time validation of conflicting onStateChanged/onStateUpdate hooks.

  • #889 9100e65 Thanks @deathbyknowledge! - Fix scheduling schema compatibility with zod v3 and improve schema structure.

    • Change zod/v3 import to zod so the package works for users on zod v3 (who don't have the zod/v3 subpath).
    • Replace flat object with optional fields with a z.discriminatedUnion on when.type. Each scheduling variant now only contains the fields it needs, making the schema cleaner and easier for LLMs to follow.
    • Replace z.coerce.date() with z.string(). Zod v4's toJSONSchema() cannot represent Date, and the AI SDK routes zod v4 schemas through it directly. Dates are now returned as ISO 8601 strings.
    • Type change: Schedule["when"] is now a discriminated union instead of a flat object with optional fields. when.date is string instead of Date.
  • #916 24e16e0 Thanks @threepointone! - Widen peer dependency ranges across packages to prevent cascading major bumps during 0.x minor releases. Mark @cloudflare/ai-chat and @cloudflare/codemode as optional peer dependencies of agents to fix unmet peer dependency warnings during installation.

  • #898 cd2d34f Thanks @jvg123! - Add keepalive ping to POST SSE response streams in WorkerTransport

    The GET SSE handler already sends event: ping every 30 seconds to keep the connection alive, but the POST SSE handler did not. This caused POST response streams to be silently dropped by proxies and infrastructure during long-running tool calls (e.g., MCP tools/call), resulting in clients never receiving the response.

  • #874 a6ec9b0 Thanks @threepointone! - Make queue and schedule getter methods synchronous

    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.

0.4.1

Patch Changes

  • #890 22dbd2c Thanks @ask-bonk! - Fix _flushQueue() permanently blocking when a queued callback throws

    A throwing callback in _flushQueue() previously caused the failing row to never be dequeued, creating an infinite retry loop that blocked all subsequent queued tasks. Additionally, _flushingQueue was never reset to false on error, permanently locking the queue for the lifetime of the Durable Object instance.

    The fix wraps each callback invocation in try-catch-finally so that failing items are always dequeued and subsequent items continue processing. The _flushingQueue flag is now reset in a top-level finally block. Missing callbacks are also dequeued instead of being skipped indefinitely.

    Note for existing stuck Durable Objects: This fix is self-healing for poison rows — they will be properly dequeued on the next _flushQueue() call. However, _flushQueue() is only triggered by a new queue() call, not on DO initialization. If you have DOs stuck in production, you can either trigger a new queue() call on affected DOs, or call dequeueAll()/dequeueAllByCallback() to clear the poison rows manually. A future improvement may add a _flushQueue() call to onStart() so stuck DOs self-heal on wake.

  • #891 0723b99 Thanks @ask-bonk! - Fix getCurrentAgent() returning undefined connection when used with @cloudflare/ai-chat and Vite SSR

    Re-export agentContext as __DO_NOT_USE_WILL_BREAK__agentContext from the main agents entry point and update @cloudflare/ai-chat to import it from agents instead of the agents/internal_context subpath export. This prevents Vite SSR pre-bundling from creating two separate AsyncLocalStorage instances, which caused getCurrentAgent().connection to be undefined inside onChatMessage and tool execute functions.

    The agents/internal_context subpath export has been removed from package.json and the deprecated agentContext alias has been removed from internal_context.ts. This was never a public API.

  • Updated dependencies [584cebe, 0723b99, 4292f6b]:

    • @cloudflare/ai-chat@0.0.8

0.4.0

Minor Changes

  • #848 a167344 Thanks @mattzcarey! - Upgrade MCP SDK to 1.26.0 to prevent cross-client response leakage. Updated examples for stateless MCP Servers create 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.

  • #298 27f4e3e Thanks @jaredhanson! - Add createMcpOAuthProvider method to the Agent class, allowing 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.

  • #610 f59f305 Thanks @threepointone! - Deprecate onStateUpdate server-side hook 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
    • Throws if a class overrides both hooks simultaneously
    • validateStateChange rejections now propagate a CF_AGENT_STATE_ERROR message back to the client
  • #871 27f8f75 Thanks @threepointone! - Migrate x402 MCP integration from 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())

    Migration guide:

    1. Update dependencies:

      npm uninstall x402
      npm install @x402/core @x402/evm
    2. Update network identifiers — both legacy names and CAIP-2 format are accepted:

      // Before
      {
        network: "base-sepolia";
      }
      // After (either works)
      {
        network: "base-sepolia";
      } // legacy name, auto-converted
      {
        network: "eip155:84532";
      } // CAIP-2 format (preferred)
    3. If you access PaymentRequirements fields in callbacks, update to v2 field names (see @x402/core docs).

    4. The version field on X402Config and X402ClientConfig is now deprecated and ignored — the protocol version is determined automatically.

    Other 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

Patch Changes

  • #610 f59f305 Thanks @threepointone! - Add readonly connections: restrict WebSocket clients from modifying agent state

    • New hooks: shouldConnectionBeReadonly, setConnectionReadonly, isConnectionReadonly
    • Blocks both client-side setState() and mutating @callable() methods for readonly connections
    • Readonly flag stored in a namespaced connection attachment (_cf_readonly), surviving hibernation without extra SQL
    • Connection state wrapping hides the internal flag from user code and preserves it across connection.setState() calls
    • Client-side onStateUpdateError callback for handling rejected state updates
  • #855 271a3cf Thanks @threepointone! - Fix useAgent and AgentClient crashing when using basePath routing.

  • #868 b3e2dc1 Thanks @threepointone! - Fix MCP OAuth callback URL leaking instance name

    Add 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.

  • #872 de71f9e Thanks @threepointone! - update dependencies

  • 8893fbe Thanks @threepointone! - partykit releases

    partyserver

    0.1.3 (Feb 8, 2026)

    • #319 — Add configurable: true to the state, setState, serializeAttachment, and deserializeAttachment property descriptors on connection objects. This allows downstream consumers (like the Cloudflare Agents SDK) to redefine these properties with Object.defineProperty for namespacing or wrapping internal state storage. Default behavior is unchanged.

    0.1.4 (Feb 9, 2026)

    • #320Add CORS support to routePartykitRequest. Pass cors: true for permissive defaults or cors: { ...headers } for custom CORS headers. Preflight (OPTIONS) requests are handled automatically for matched routes, and CORS headers are appended to all non-WebSocket responses — including responses returned by onBeforeRequest.
    • #260 — Remove redundant initialize code as setName takes care of it, along with the nested blockConcurrencyWhile call.

    partysocket

    1.1.12 (Feb 8, 2026)

    • #317 — Fix PartySocket.reconnect() crashing when using basePath without room. The reconnect guard now accepts either room or basePath as sufficient context to construct a connection URL.
    • #319 — Throw a clear error when constructing a PartySocket without room or basePath (and without startClosed: true), instead of silently connecting to a malformed URL containing "undefined" as the room name.

    1.1.13 (Feb 9, 2026)

    • #322 — Fix reconnect() not working after maxRetries has been exhausted. The _connectLock was not released when the max retries early return was hit in _connect(), preventing any subsequent reconnect() call from initiating a new connection.
  • #869 fc17506 Thanks @threepointone! - Remove room/party workaround for basePath routing now that partysocket handles reconnect without requiring room to be set.

  • #873 d0579fa Thanks @threepointone! - Remove CORS wrapping from routeAgentRequest and delegate to partyserver's native CORS support. The cors option is now passed directly through to routePartykitRequest, which handles preflight and response headers automatically since partyserver 0.1.4.

  • #865 c3211d0 Thanks @threepointone! - update dependencies

  • Updated dependencies [21a7977, 3de98a3, c3211d0]:

    • @cloudflare/codemode@0.0.7
    • @cloudflare/ai-chat@0.0.7

0.3.10

Patch Changes

  • #839 68916bf Thanks @whoiskatrin! - Invalidate query cache on disconnect to fix stale auth tokens

  • #841 3f490d0 Thanks @mattzcarey! - Escape authError to prevent XSS attacks and store it in the connection state to avoid needing script tags to display error.

  • Updated dependencies [83f137f]:

    • @cloudflare/ai-chat@0.0.6

0.3.9

Patch Changes

  • #837 b11b9dd Thanks @threepointone! - Fix AgentWorkflow run() method not being called in production

    The run() method wrapper was being set as an instance property in the constructor, but Cloudflare's RPC system invokes methods from the prototype chain. This caused the initialization wrapper to be bypassed in production, resulting in _initAgent never being called.

    Changed to wrap the subclass prototype's run method directly with proper safeguards:

    • Uses Object.hasOwn() to only wrap prototypes that define their own run method (prevents double-wrapping inherited methods)
    • Uses a WeakSet to track wrapped prototypes (prevents re-wrapping on subsequent instantiations)
    • Uses an instance-level __agentInitCalled flag to prevent double initialization if super.run() is called from a subclass

0.3.8

Patch Changes

  • #833 6c80022 Thanks @tarushnagpal! - On invalid OAuth state, clear auth_url in storage and set the MCP connection state to FAILED ready for reconnection.

  • #834 2b4aecd Thanks @threepointone! - Fix AgentClient.close() to immediately reject pending RPC calls instead of waiting for WebSocket close handshake timeout.

    Previously, calling client.close() would not reject pending RPC calls until the WebSocket close handshake completed (which could take 15+ seconds in some environments). Now pending calls are rejected immediately when close() is called, providing faster feedback on intentional disconnects.

0.3.7

agents@0.3.7 Release Notes

This release introduces Cloudflare Workflows integration for durable multi-step processing, secure email reply routing with HMAC-SHA256 signatures, 15+ new documentation files, and significant improvements to state management, the callable RPC system, and scheduling.

Highlights

  • Workflows Integration - Seamless integration between Cloudflare Agents and Cloudflare Workflows for durable, multi-step background processing
  • Secure Email Routing - HMAC-SHA256 signed email headers prevent unauthorized routing of emails to agent instances
  • Comprehensive Documentation - 15+ new docs covering getting started, state, routing, HTTP/WebSocket lifecycle, callable methods, MCP, and scheduling
  • Synchronous setState() - State updates are now synchronous with a new validateStateChange() validation hook
  • scheduleEvery() Method - Fixed-interval recurring tasks with overlap prevention
  • Callable System Improvements - Client-side RPC timeouts, streaming error signaling, introspection API
  • 100+ New Tests - Comprehensive test coverage across state, routing, callable, and email utilities

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.

AgentWorkflow Base Class

Extend AgentWorkflow instead of WorkflowEntrypoint to get typed access to the originating Agent:

import { AgentWorkflow } 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(params.taskId, "processing");

    // Non-durable: progress reporting
    await this.reportProgress({ step: "process", percent: 0.5 });
    this.broadcastToClients({ type: "update", taskId: params.taskId });

    // Durable via step: idempotent, won't repeat on retry
    await step.mergeAgentState({ taskProgress: 0.5 });
    await step.reportComplete(result);

    return result;
  }
}

Agent Methods for Workflows

  • runWorkflow(workflowName, params, options?) - Start workflow with optional metadata
  • sendWorkflowEvent(workflowName, workflowId, event) - Send events to waiting workflows
  • getWorkflow(workflowId) / getWorkflows(criteria?) - Query workflows with cursor-based pagination
  • deleteWorkflow(workflowId) / deleteWorkflows(criteria?) - Delete workflows by ID or criteria
  • approveWorkflow(workflowId) / rejectWorkflow(workflowId) - Human-in-the-loop approval flows
  • terminateWorkflow(), pauseWorkflow(), resumeWorkflow(), restartWorkflow() - Workflow control

Lifecycle Callbacks

async onWorkflowProgress(workflowName, workflowId, progress) {}
async onWorkflowComplete(workflowName, workflowId, result?) {}
async onWorkflowError(workflowName, workflowId, error) {}
async onWorkflowEvent(workflowName, workflowId, event) {}

See docs/workflows.md for full documentation.


Secure Email Reply Routing

Prevents unauthorized routing of emails to arbitrary agent instances using HMAC-SHA256 signed headers.

New Resolver

import { createSecureReplyEmailResolver } from "agents/email";

const resolver = createSecureReplyEmailResolver(env.EMAIL_SECRET, {
  maxAge: 7 * 24 * 60 * 60, // Optional: 7 days (default: 30 days)
  onInvalidSignature: (email, reason) => {
    console.warn(`Invalid signature from ${email.from}: ${reason}`);
  },
});

Automatic Signing on Reply

await this.replyToEmail(email, {
  fromName: "My Agent",
  body: "Thanks!",
  secret: this.env.EMAIL_SECRET, // Signs headers for secure reply routing
});

Breaking Changes

  • Email utilities moved to agents/email subpath
  • createHeaderBasedEmailResolver removed (security vulnerability)
  • New onNoRoute callback for handling unmatched emails

New Documentation

Document Description
getting-started.md Quick start guide: installation, first agent, state basics, deployment
adding-to-existing-project.md Integrating agents into existing Workers, React apps, Hono
state.md State management, validateStateChange(), persistence, client sync
routing.md URL routing patterns, basePath, server-sent identity
http-websockets.md HTTP/WebSocket lifecycle hooks, connection management, hibernation
callable-methods.md @callable decorator, RPC over WebSocket, streaming responses
mcp-client.md Connecting to MCP servers, OAuth flows, transport options
scheduling.md One-time, recurring (scheduleEvery), and cron-based scheduling
workflows.md Complete Workflows integration guide

State Management Improvements

Synchronous setState()

setState() is now synchronous. Existing await this.setState(...) code continues to work.

// Preferred (new)
this.setState({ count: 1 });

// Still works (backward compatible)
await this.setState({ count: 1 });

validateStateChange() Hook

New synchronous validation hook that runs before state is persisted:

validateStateChange(nextState: State, source: Connection | "server") {
  if (nextState.count < 0) {
    throw new Error("Count cannot be negative");
  }
}

Execution Order

  1. validateStateChange(nextState, source) - validation (sync, gating)
  2. State persisted to SQLite
  3. State broadcast to connected clients
  4. onStateUpdate(nextState, source) - notifications (async via ctx.waitUntil, non-gating)

Scheduling: scheduleEvery()

Fixed-interval recurring tasks with overlap prevention and error resilience:

await this.scheduleEvery(60, "cleanup");
await this.scheduleEvery(300, "syncData", { source: "api" });
  • Validates interval doesn't exceed 30 days (DO alarm limit)
  • Overlap prevention with hung callback detection (configurable via hungScheduleTimeoutSeconds)

Callable System Improvements

Client-side RPC Timeout

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

New Features

  • StreamingResponse.error(message) - Graceful stream error signaling
  • getCallableMethods() - Introspection API for callable methods
  • Connection close handling - Pending calls rejected on disconnect
  • crypto.randomUUID() for more robust RPC IDs
  • Streaming observability events and error logging

MCP Server API

Options-based addMcpServer() overload for cleaner configuration:

await this.addMcpServer("server", url, {
  callbackHost: "https://my-worker.workers.dev",
  transport: { headers: { Authorization: "Bearer ..." } },
});

Routing & Identity Enhancements

  • basePath - Bypass default URL construction for custom routing
  • Server-sent identity - Agents send name and agent type on connect
  • onIdentity / onIdentityChange callbacks on the client
  • static options = { sendIdentityOnConnect } for server-side control
const agent = useAgent({
  basePath: "user",
  onIdentity: (name, agentType) => console.log(`Connected to ${name}`),
});

Email Utilities

  • isAutoReplyEmail(headers) - Detect auto-reply emails using standard RFC headers

Bug Fixes

  • Fixed tool error content type in getAITools (#781)
  • Fixed React useRef type error
  • Memory leak prevention with WeakMap for callable metadata
  • Connection cleanup - pending RPC calls rejected on WebSocket close
  • JSON parse error handling - graceful fallback to initialState on corrupted state
  • Fixed resumable streaming to avoid delivering live chunks before resume ACK (#795)

Migration Notes

Email Imports

// Before
import { createAddressBasedEmailResolver, signAgentHeaders } from "agents";

// After
import {
  createAddressBasedEmailResolver,
  signAgentHeaders,
} from "agents/email";

Workflow Imports

import { AgentWorkflow } from "agents/workflows";
import type { AgentWorkflowStep, WorkflowInfo } from "agents/workflows";

OpenAI Provider Options

When using scheduleSchema with OpenAI models via the AI SDK, pass providerOptions:

await generateObject({
  // ... other options
  providerOptions: { openai: { strictJsonSchema: false } },
});

Patch Changes

  • #825 0c3c9bb Thanks @threepointone! - Add cursor-based pagination to getWorkflows(). Returns a WorkflowPage with workflows, total count, and cursor for next page. Default limit is 50 (max 100).

  • #825 0c3c9bb Thanks @threepointone! - Add workflow control methods: terminateWorkflow(), pauseWorkflow(), resumeWorkflow(), and restartWorkflow().

  • #799 d1a0c2b Thanks @threepointone! - feat: Add Cloudflare Workflows integration for Agents

    Adds seamless integration between Cloudflare Agents and Cloudflare Workflows for durable, multi-step background processing.

    Why use Workflows with Agents?

    Agents excel at real-time communication and state management, while Workflows excel at durable execution. Together:

    • Agents handle WebSocket connections and quick operations
    • Workflows handle long-running tasks, retries, and human-in-the-loop flows

    AgentWorkflow Base Class

    Extend AgentWorkflow instead of WorkflowEntrypoint to get typed access to the originating Agent:

    export class ProcessingWorkflow extends AgentWorkflow<MyAgent, TaskParams> {
      async run(event: AgentWorkflowEvent<TaskParams>, step: AgentWorkflowStep) {
        const params = event.payload;
    
        // Call Agent methods via RPC
        await this.agent.updateStatus(params.taskId, "processing");
    
        // Non-durable: progress reporting (lightweight, for frequent updates)
        await this.reportProgress({
          step: "process",
          percent: 0.5,
          message: "Halfway done",
        });
        this.broadcastToClients({ type: "update", taskId: params.taskId });
    
        // Durable via step: idempotent, won't repeat on retry
        await step.mergeAgentState({ taskProgress: 0.5 });
        await step.reportComplete(result);
    
        return result;
      }
    }

    Agent Methods

    • runWorkflow(workflowName, params, options?) - Start workflow with optional metadata for querying
    • sendWorkflowEvent(workflowName, workflowId, event) - Send events to waiting workflows
    • getWorkflow(workflowId) - Get tracked workflow by ID
    • getWorkflows(criteria?) - Query by status, workflowName, or metadata with pagination
    • deleteWorkflow(workflowId) - Delete a workflow tracking record
    • deleteWorkflows(criteria?) - Delete workflows by criteria (status, workflowName, metadata, createdBefore)
    • approveWorkflow(workflowId, data?) - Approve a waiting workflow
    • rejectWorkflow(workflowId, data?) - Reject a waiting workflow

    AgentWorkflow Methods

    On this (non-durable, lightweight):

    • reportProgress(progress) - Report typed progress object to Agent
    • broadcastToClients(message) - Broadcast to WebSocket clients
    • waitForApproval(step, opts?) - Wait for approval (throws on rejection)

    On step (durable, idempotent):

    • step.reportComplete(result?) - Report successful completion
    • step.reportError(error) - Report an error
    • step.sendEvent(event) - Send custom event to Agent
    • step.updateAgentState(state) - Replace Agent state (broadcasts to clients)
    • step.mergeAgentState(partial) - Merge into Agent state (broadcasts to clients)
    • step.resetAgentState() - Reset Agent state to initialState (broadcasts to clients)

    Lifecycle Callbacks

    Override these methods to handle workflow events (workflowName is first for easy differentiation):

    async onWorkflowProgress(workflowName, workflowId, progress) {} // progress is typed object
    async onWorkflowComplete(workflowName, workflowId, result?) {}
    async onWorkflowError(workflowName, workflowId, error) {}
    async onWorkflowEvent(workflowName, workflowId, event) {}

    Workflow Tracking

    Workflows are automatically tracked in cf_agents_workflows SQLite table:

    • Status, timestamps, errors
    • Optional metadata field for queryable key-value data
    • Params/output NOT stored by default (could be large)

    See docs/workflows.md for full documentation.

  • #812 6218541 Thanks @threepointone! - # Bug Fixes

    This release includes three bug fixes:

    1. Hung Callback Detection in scheduleEvery()

    Fixed a deadlock where if an interval callback hung indefinitely, all future interval executions would be skipped forever.

    Fix: Track execution start time and force reset after 30 seconds of inactivity. If a previous execution appears hung (started more than 30s ago), it is force-reset and re-executed.

    // Now safe - hung callbacks won't block future executions
    await this.scheduleEvery(60, "myCallback");

    2. Corrupted State Recovery

    Fixed a crash when the database contains malformed JSON state.

    Fix: Wrapped JSON.parse in try-catch with fallback to initialState. If parsing fails, the agent logs an error and recovers gracefully.

    // Agent now survives corrupted state
    class MyAgent extends Agent {
      initialState = { count: 0 }; // Used as fallback if DB state is corrupted
    }

    3. getCallableMethods() Prototype Chain Traversal

    Fixed getCallableMethods() to find @callable methods from parent classes, not just the immediate class.

    Fix: Walk the full prototype chain using Object.getPrototypeOf() loop.

    class BaseAgent extends Agent {
      @callable()
      parentMethod() {
        return "parent";
      }
    }
    
    class ChildAgent extends BaseAgent {
      @callable()
      childMethod() {
        return "child";
      }
    }
    
    // Now correctly returns both parentMethod and childMethod
    const methods = childAgent.getCallableMethods();
  • #812 6218541 Thanks @threepointone! - # Callable System Improvements

    This release includes several improvements to the @callable decorator and RPC system:

    New Features

    Client-side RPC Timeout

    You can now specify a timeout for RPC calls that will reject if the call doesn't complete in time:

    await agent.call("slowMethod", [], { timeout: 5000 });

    StreamingResponse.error()

    New method to gracefully signal an error during streaming and close the stream:

    @callable({ streaming: true })
    async processItems(stream: StreamingResponse, items: string[]) {
      for (const item of items) {
        try {
          const result = await this.process(item);
          stream.send(result);
        } catch (e) {
          stream.error(`Failed to process ${item}: ${e.message}`);
          return;
        }
      }
      stream.end();
    }

    getCallableMethods() API

    New method on the Agent class to introspect all callable methods and their metadata:

    const methods = agent.getCallableMethods();
    // Returns Map<string, CallableMetadata>
    
    for (const [name, meta] of methods) {
      console.log(`${name}: ${meta.description || "(no description)"}`);
    }

    Connection Close Handling

    Pending RPC calls are now automatically rejected with a "Connection closed" error when the WebSocket connection closes unexpectedly.

    Internal Improvements

    • WeakMap for metadata storage: Changed callableMetadata from Map to WeakMap to prevent memory leaks when function references are garbage collected.
    • UUID for RPC IDs: Replaced Math.random().toString(36) with crypto.randomUUID() for more robust and unique RPC call identifiers.
    • Streaming observability: Added observability events for streaming RPC calls.

    API Enhancements

    The agent.call() method now accepts a unified CallOptions object with timeout support:

    // New format (preferred, supports timeout)
    await agent.call("method", [args], {
      timeout: 5000,
      stream: { onChunk, onDone, onError },
    });
    
    // Legacy format (still fully supported for backward compatibility)
    await agent.call("method", [args], { onChunk, onDone, onError });

    Both formats work seamlessly - the client auto-detects which format you're using.

  • #812 6218541 Thanks @threepointone! - feat: Add scheduleEvery method for fixed-interval scheduling

    Adds a new scheduleEvery(intervalSeconds, callback, payload?) method to the Agent class for scheduling recurring tasks at fixed intervals.

    Features

    • Fixed interval execution: Schedule a callback to run every N seconds
    • Overlap prevention: If a callback is still running when the next interval fires, the next execution is skipped
    • Error resilience: If a callback throws, the schedule persists and continues on the next interval
    • Cancellable: Use cancelSchedule(id) to stop the recurring schedule

    Usage

    class MyAgent extends Agent {
      async onStart() {
        // Run cleanup every 60 seconds
        await this.scheduleEvery(60, "cleanup");
    
        // With payload
        await this.scheduleEvery(300, "syncData", { source: "api" });
      }
    
      cleanup() {
        // Runs every 60 seconds
      }
    
      syncData(payload: { source: string }) {
        // Runs every 300 seconds with payload
      }
    }

    Querying interval schedules

    // Get all interval schedules
    const intervals = await this.getSchedules({ type: "interval" });

    Schema changes

    Adds intervalSeconds and running columns to cf_agents_schedules table (auto-migrated for existing agents).

  • #812 6218541 Thanks @threepointone! - Add isAutoReplyEmail() utility to detect auto-reply emails

    Detects auto-reply emails based on standard RFC 3834 headers (Auto-Submitted, X-Auto-Response-Suppress, Precedence). Use this to avoid mail loops when sending automated replies.

    import { isAutoReplyEmail } from "agents/email";
    import PostalMime from "postal-mime";
    
    async onEmail(email: AgentEmail) {
      const raw = await email.getRaw();
      const parsed = await PostalMime.parse(raw);
    
      // Detect and skip auto-reply emails
      if (isAutoReplyEmail(parsed.headers)) {
        console.log("Skipping auto-reply");
        return;
      }
    
      // Process the email...
    }
  • #781 fd79481 Thanks @HueCodes! - fix: properly type tool error content in getAITools

  • #812 6218541 Thanks @threepointone! - fix: improve type inference for RPC methods returning custom interfaces

    Previously, RPCMethod used { [key: string]: SerializableValue } to check if return types were serializable. This didn't work with TypeScript interfaces that have named properties (like interface CoreState { counter: number; name: string; }), causing those methods to be incorrectly excluded from typed RPC calls.

    Now uses a recursive CanSerialize<T> type that checks if all properties of an object are serializable, properly supporting:

    • Custom interfaces with named properties
    • Nested object types
    • Arrays of objects
    • Optional and nullable properties
    • Union types

    Also expanded NonSerializable to explicitly exclude non-JSON-serializable types like Date, RegExp, Map, Set, Error, and typed arrays.

    // Before: these methods were NOT recognized as callable
    interface MyState {
      counter: number;
      items: string[];
    }
    
    class MyAgent extends Agent<Env, MyState> {
      @callable()
      getState(): MyState {
        return this.state;
      } // ❌ Not typed
    }
    
    // After: properly recognized and typed
    const agent = useAgent<MyAgent, MyState>({ agent: "my-agent" });
    agent.call("getState"); // ✅ Typed as Promise<MyState>
  • #825 0c3c9bb Thanks @threepointone! - Fix workflow tracking table not being updated by AgentWorkflow callbacks.

    Previously, when a workflow reported progress, completion, or errors via callbacks, the cf_agents_workflows tracking table was not updated. This caused getWorkflow() and getWorkflows() to return stale status (e.g., "queued" instead of "running" or "complete").

    Now, onWorkflowCallback() automatically updates the tracking table:

    • Progress callbacks set status to "running"
    • Complete callbacks set status to "complete" with completed_at timestamp
    • Error callbacks set status to "errored" with error details

    Fixes #821.

  • #812 6218541 Thanks @threepointone! - feat: Add options-based API for addMcpServer

    Adds a cleaner options-based overload for addMcpServer() that avoids passing undefined for unused positional parameters.

    Before (still works)

    // Awkward when you only need transport options
    await this.addMcpServer("server", url, undefined, undefined, {
      transport: { headers: { Authorization: "Bearer ..." } },
    });

    After (preferred)

    // Clean options object
    await this.addMcpServer("server", url, {
      transport: { headers: { Authorization: "Bearer ..." } },
    });
    
    // With callback host
    await this.addMcpServer("server", url, {
      callbackHost: "https://my-worker.workers.dev",
      transport: { type: "sse" },
    });

    Options

    type AddMcpServerOptions = {
      callbackHost?: string; // OAuth callback host (auto-derived if omitted)
      agentsPrefix?: string; // Routing prefix (default: "agents")
      client?: ClientOptions; // MCP client options
      transport?: {
        headers?: HeadersInit; // Custom headers for auth
        type?: "sse" | "streamable-http" | "auto";
      };
    };

    The legacy 5-parameter signature remains fully supported for backward compatibility.

  • #812 6218541 Thanks @threepointone! - Add custom URL routing with basePath and server-sent identity

    Custom URL Routing with basePath

    New basePath option bypasses default /agents/{agent}/{name} URL construction, enabling custom routing patterns:

    // Client connects to /user instead of /agents/user-agent/...
    const agent = useAgent({
      agent: "UserAgent",
      basePath: "user",
    });

    Server handles routing manually with getAgentByName:

    export default {
      async fetch(request: Request, env: Env) {
        const url = new URL(request.url);
    
        if (url.pathname === "/user") {
          const session = await getSession(request);
          const agent = await getAgentByName(env.UserAgent, session.userId);
          return agent.fetch(request);
        }
    
        return (
          (await routeAgentRequest(request, env)) ??
          new Response("Not found", { status: 404 })
        );
      },
    };

    Server-Sent Identity

    Agents now send their identity (name and agent class) to clients on connect:

    • onIdentity callback - called when server sends identity
    • agent.name and agent.agent are updated from server (authoritative)
    const agent = useAgent({
      agent: "UserAgent",
      basePath: "user",
      onIdentity: (name, agentType) => {
        console.log(`Connected to ${agentType} instance: ${name}`);
      },
    });

    Identity State & Ready Promise

    • identified: boolean - whether identity has been received
    • ready: Promise<void> - resolves when identity is received
    • In React, name, agent, and identified are reactive state
    // React - reactive rendering
    return agent.identified ? `Connected to: ${agent.name}` : "Connecting...";
    
    // Vanilla JS - await ready
    await agent.ready;
    console.log(agent.name);

    Identity Change Detection

    • onIdentityChange callback - fires when identity differs on reconnect
    • Warns if identity changes without handler (helps catch session issues)
    useAgent({
      basePath: "user",
      onIdentityChange: (oldName, newName, oldAgent, newAgent) => {
        console.log(`Session changed: ${oldName}${newName}`);
      },
    });

    Sub-Paths with path Option

    Append additional path segments:

    // /user/settings
    useAgent({ basePath: "user", path: "settings" });
    
    // /agents/my-agent/room/settings
    useAgent({ agent: "MyAgent", name: "room", path: "settings" });

    Server-Side Identity Control

    Disable identity sending for security-sensitive instance names:

    class SecureAgent extends Agent {
      static options = { sendIdentityOnConnect: false };
    }
  • #827 e20da53 Thanks @threepointone! - Move workflow exports to agents/workflows subpath for better separation of concerns.

    import { AgentWorkflow } from "agents/workflows";
    import type { AgentWorkflowStep, WorkflowInfo } from "agents/workflows";
  • #811 f604008 Thanks @threepointone! - ### Secure Email Reply Routing

    This release introduces secure email reply routing with HMAC-SHA256 signed headers, preventing unauthorized routing of emails to arbitrary agent instances.

    Breaking Changes

    Email utilities moved to agents/email subpath: Email-specific resolvers and utilities have been moved to a dedicated subpath for better organization.

    // Before
    import { createAddressBasedEmailResolver, signAgentHeaders } from "agents";
    
    // After
    import {
      createAddressBasedEmailResolver,
      signAgentHeaders,
    } from "agents/email";

    The following remain in root: routeAgentEmail, createHeaderBasedEmailResolver (deprecated).

    createHeaderBasedEmailResolver removed: This function now throws an error with migration guidance. It was removed because it trusted attacker-controlled email headers for routing.

    Migration:

    • For inbound mail: use createAddressBasedEmailResolver(agentName)
    • For reply flows: use createSecureReplyEmailResolver(secret) with signed headers

    See https://github.com/cloudflare/agents/blob/main/docs/email.md for details.

    EmailSendOptions type removed: This type was unused and has been removed.

    New Features

    createSecureReplyEmailResolver: A new resolver that verifies HMAC-SHA256 signatures on incoming emails before routing. Signatures include a timestamp and expire after 30 days by default.

    const resolver = createSecureReplyEmailResolver(env.EMAIL_SECRET, {
      maxAge: 7 * 24 * 60 * 60, // Optional: 7 days (default: 30 days)
      onInvalidSignature: (email, reason) => {
        // Optional: log failures for debugging
        // reason: "missing_headers" | "expired" | "invalid" | "malformed_timestamp"
        console.warn(`Invalid signature from ${email.from}: ${reason}`);
      },
    });

    signAgentHeaders: Helper function to manually sign agent routing headers for use with external email services.

    const headers = await signAgentHeaders(secret, agentName, agentId);
    // Returns: { "X-Agent-Name", "X-Agent-ID", "X-Agent-Sig", "X-Agent-Sig-Ts" }

    replyToEmail signing: The replyToEmail method now accepts a secret option to automatically sign outbound email headers.

    await this.replyToEmail(email, {
      fromName: "My Agent",
      body: "Thanks!",
      secret: this.env.EMAIL_SECRET, // Signs headers for secure reply routing
    });

    If an email was routed via createSecureReplyEmailResolver, calling replyToEmail without a secret will throw an error (pass explicit null to opt-out).

    onNoRoute callback: routeAgentEmail now accepts an onNoRoute callback for handling emails that don't match any routing rule.

    await routeAgentEmail(message, env, {
      resolver,
      onNoRoute: (email) => {
        email.setReject("Unknown recipient");
      },
    });
  • #813 7aebab3 Thanks @threepointone! - update dependencies

  • #800 a54edf5 Thanks @threepointone! - Update dependencies

  • #818 7c74336 Thanks @threepointone! - update dependencies

  • #812 6218541 Thanks @threepointone! - # Synchronous setState with validation hook

    setState() is now synchronous instead of async. This improves ergonomics and aligns with the expected mental model for state updates.

    Breaking Changes

    setState() returns void instead of Promise<void>

    // Before (still works - awaiting a non-promise is harmless)
    await this.setState({ count: 1 });
    
    // After (preferred)
    this.setState({ count: 1 });

    Existing code that uses await this.setState(...) will continue to work without changes.

    onStateUpdate() no longer gates state broadcasts

    Previously, if onStateUpdate() threw an error, the state update would be aborted. Now, onStateUpdate() runs asynchronously via ctx.waitUntil() after the state is persisted and broadcast. Errors in onStateUpdate() are routed to onError() but do not prevent the state from being saved or broadcast.

    If you were using onStateUpdate() for validation, migrate to validateStateChange().

    New Features

    validateStateChange() validation hook

    A new synchronous hook that runs before state is persisted or broadcast. Use this for validation:

    validateStateChange(nextState: State, source: Connection | "server") {
      if (nextState.count < 0) {
        throw new Error("Count cannot be negative");
      }
    }
    • Runs synchronously before persistence and broadcast
    • Throwing aborts the state update entirely
    • Ideal for validation logic

    Execution order

    1. validateStateChange(nextState, source) - validation (sync, gating)
    2. State persisted to SQLite
    3. State broadcast to connected clients
    4. onStateUpdate(nextState, source) - notifications (async via ctx.waitUntil, non-gating)
  • #815 ded8d3e Thanks @threepointone! - docs: add OpenAI provider options documentation to scheduleSchema

    When using scheduleSchema with OpenAI models via the AI SDK, users must now pass providerOptions: { openai: { strictJsonSchema: false } } to generateObject. This is documented in the JSDoc for scheduleSchema.

    This is required because @ai-sdk/openai now defaults strictJsonSchema to true, which requires all schema properties to be in the required array. The scheduleSchema uses optional fields which are not compatible with this strict mode.

  • Updated dependencies [7aebab3, 77be4f8, a54edf5, 7c74336, 99cbca0]:

    • @cloudflare/codemode@0.0.6
    • @cloudflare/ai-chat@0.0.5

0.3.6

Patch Changes

0.3.5

Patch Changes

0.3.4

Patch Changes

0.3.3

Patch Changes

  • a5d0137 Thanks @threepointone! - trigger a new release

  • Updated dependencies [a5d0137]:

    • @cloudflare/codemode@0.0.3
    • @cloudflare/ai-chat@0.0.3

0.3.2

Patch Changes

  • #756 0c4275f Thanks @threepointone! - feat: split ai-chat and codemode into separate packages

    Extract @cloudflare/ai-chat and @cloudflare/codemode into their own packages with comprehensive READMEs. Update agents README to remove chat-specific content and point to new packages. Fix documentation imports to reflect new package structure.

    Maintains backward compatibility, no breaking changes.

  • #758 f12553f Thanks @whoiskatrin! - Implement createStubProxy function to fix RPC method call handling

  • Updated dependencies [0c4275f]:

    • @cloudflare/codemode@0.0.2
    • @cloudflare/ai-chat@0.0.2

0.3.1

Patch Changes

0.3.0

Minor Changes

0.2.35

Patch Changes

  • #742 29938d4 Thanks @threepointone! - mark AgentNamespace as deprecated

    It only makes things harder, especially for autogenned types.

  • #747 17a0346 Thanks @threepointone! - fix: scheduling should work

    since we updated to zod v4, the schedule schema was broken. ai sdk's .jsonSchema function doesn't correctly work on tools created with zod v4. The fix, is to use the v3 version of zod for the schedule schema.

0.2.34

Patch Changes

  • #739 e9b6bb7 Thanks @threepointone! - update all dependencies

    • remove the changesets cli patch, as well as updating node version, so we don't need to explicitly install newest npm
    • lock mcp sdk version till we figure out how to do breaking changes correctly
    • removes stray permissions block from release.yml
  • #740 087264c Thanks @threepointone! - update zod

  • #737 b8c0595 Thanks @threepointone! - update partyserver (and some other cf packages)

    specifically updating partyserver so it gets a better default type for Env, defaulting to Cloudflare.Env

  • #732 9fbb1b6 Thanks @Scalahansolo! - Setup proper peer deps for zod v4

  • #722 57b7f2e Thanks @agcty! - fix: move AI SDK packages to peer dependencies

0.2.32

Patch Changes

0.2.31

Patch Changes

  • #720 380c597 Thanks @mattzcarey! - MCP WorkerTransport accepts any supported protocol version in request headers and only rejects truly unsupported versions. This aligns with the move by MCP community to stateless transports and fixes an isse with 'mcp-protocol-version': '2025-11-25'

0.2.30

Patch Changes

  • #716 569e184 Thanks @whoiskatrin! - Fix elicitation response handling in MCP StreamableHTTP transport by adding a message interceptor

0.2.29

Patch Changes

0.2.28

Patch Changes

  • #696 6a930ef Thanks @mattzcarey! - Enables connecting to multiple MCP servers simultaneously and hardens OAuth state handling against replay/DoS attacks.

    Note: Inflight OAuth flows that were initiated on a previous version will not complete after upgrading, as the state parameter format has changed. Users will need to restart the authentication flow.

  • #702 10d453d Thanks @mattzcarey! - broadcast auth_url as soon as its returned

0.2.27

Patch Changes

0.2.26

Patch Changes

0.2.25

Patch Changes

0.2.24

Patch Changes

  • #673 603b825 Thanks @whoiskatrin! - added resumable streaming with minimal setup

  • #665 4c0838a Thanks @threepointone! - Add default JSON schema validator to MCP client

  • #664 36d03e6 Thanks @threepointone! - Refactor MCP server table management in Agent class

    Moved creation and deletion of the cf_agents_mcp_servers table from AgentMCPClientStorage to the Agent class. Removed redundant create and destroy methods from AgentMCPClientStorage and updated MCPClientManager to reflect these changes. Added comments to clarify usage in demo and test code.

  • #653 412321b Thanks @deathbyknowledge! - Allow this.destroy inside a schedule by including a destroyed flag and yielding ctx.abort instead of calling it directly Fix issue where schedules would not be able to run for more 30 seconds due to blockConccurencyWhile. alarm() isn't manually called anymore, getting rid of the bCW. Fix an issue where immediate schedules (e.g. this.schedule(0, "foo"))) would not get immediately scheduled.

  • #652 c07b2c0 Thanks @mattzcarey! - ### New Features

    • MCPClientManager API changes:
      • New registerServer() method to register servers (replaces part of connect())
      • New connectToServer() method to establish connection (replaces part of connect())
      • connect() method deprecated (still works for backward compatibility)
    • Connection state observability: New onServerStateChanged() event for tracking all server state changes
    • Improved reconnect logic: restoreConnectionsFromStorage() handles failed connections

    Bug Fixes

    • Fixed failed connections not being recreated on restore
    • Fixed redundant storage operations during connection restoration
    • Fixed potential OAuth storage initialization issue by excluding non-serializable authProvider from stored server options
    • Added defensive checks for storage initialization in MCPClientManager and DurableObjectOAuthClientProvider
    • Fixed initialization order: MCPClientManager is now created AFTER database tables are created to prevent possible table-not-found errors during DO restart
  • #678 cccbd0f Thanks @whoiskatrin! - convert internal AI SDK stream events to UIMessageStreamPart format

  • #672 7c9f8b0 Thanks @mattzcarey! - - MCPClientConnection.init() no longer triggers discovery automatically. Discovery should be done via discover() or through MCPClientManager.discoverIfConnected()

    Features

    • New discover() method on MCPClientConnection with full lifecycle management:
      • Handles state transitions (CONNECTED → DISCOVERING → READY on success, CONNECTED on failure)
      • Supports cancellation via AbortController (cancels previous in-flight discovery)
      • Configurable timeout (default 15s)
    • New cancelDiscovery() method to abort in-flight discoveries
    • New discoverIfConnected() on MCPClientManager for simpler capability discovery per server
    • createConnection() now returns the connection object for immediate use
    • Created MCPConnectionState enum to formalize possible states: idle, connecting, authenticating, connected, discovering, ready, failed

    Fixes

    • Fixed discovery hanging on repeated requests - New discoveries now cancel previous in-flight ones via AbortController
    • Fixed Durable Object crash-looping - restoreConnectionsFromStorage() now starts connections in background (fire-and-forget) to avoid blocking onStart and causing blockConcurrencyWhile timeouts
    • Fixed OAuth callback race condition - When auth_url exists in storage during restoration, state is set to AUTHENTICATING directly instead of calling connectToServer() which was overwriting the state
    • Set discovery timeout to 15s
    • MCP Client Discovery failures now throw errors immediately instead of continuing with empty arrays
    • Added "connected" state to represent a connected server with no tools loaded yet
  • #654 a315e86 Thanks @mattzcarey! - When handling MCP server requests use relatedRequestId in TransportOptions to send the response down a POST stream if supported (streamable-http)

  • #661 93589e5 Thanks @naji247! - fix: add session ID and header support to SSE transport

    The SSE transport now properly forwards session IDs and request headers to MCP message handlers, achieving closer header parity with StreamableHTTP transport. This allows MCP servers using SSE to access request headers for session management.

  • #659 48849be Thanks @threepointone! - update dependencies

0.2.23

Patch Changes

  • #649 e135cf5 Thanks @mattzcarey! - fix auth url not being cleared on a successful oauth callback causing endless reconnection

0.2.22

Patch Changes

0.2.21

Patch Changes

  • #631 6ddabb7 Thanks @ghostwriternr! - Handle OAuth errors and validate redirect URLs

  • #626 cec3cca Thanks @mattzcarey! - Remove url field from RequestExtra in WorkerTransport. It is non standard and goes against the MCP spec types.

  • #630 636aaf9 Thanks @ghostwriternr! - Fix OAuth redirect handling in MCP clients

  • #624 3bb54bf Thanks @threepointone! - Add CLI entry point and tests for agents package

    Introduces a new CLI for the agents package using yargs with the following commands (currently stubs, not yet implemented):

    • init / create - Initialize an agents project
    • dev - Start development server
    • deploy - Deploy agents to Cloudflare
    • mcp - The agents mcp server

    Adds CLI test suite with comprehensive coverage for all commands and configurations. Updates package.json to register the CLI binary, adds test scripts for CLI testing, and includes yargs dependencies.

0.2.20

Patch Changes

  • #619 e7d0d4d Thanks @mattzcarey! - Adds request info to the extra argument in onmessage. Adds a url parm which we will try push upstream to the MCP SDK as it is useful with OpenAI Apps SDK

0.2.19

Patch Changes

  • #607 c9b76cd Thanks @threepointone! - Add jurisdiction support to MCP agent and handlers

    Introduces a jurisdiction option to MCP agent server and streaming/SSE handlers, allowing Durable Object instances to be created in specific geographic regions for compliance (e.g., GDPR). Documentation updated to explain usage and available jurisdictions.

0.2.18

Patch Changes

  • #602 aed8e18 Thanks @threepointone! - Add CORS support to MCP handler and tests

    Introduces CORS configuration to experimental_createMcpHandler, including handling OPTIONS preflight requests and adding CORS headers to responses and errors. Exports corsHeaders from utils. Adds comprehensive tests for CORS behavior in handler.test.ts.

  • #603 4da191c Thanks @mattzcarey! - Drop the experimental_ prefix on createMcpHandler

0.2.17

Patch Changes

0.2.16

Patch Changes

0.2.15

Patch Changes

0.2.14

Patch Changes

0.2.13

Patch Changes

0.2.12

Patch Changes

0.2.11

Patch Changes

0.2.10

Patch Changes

0.2.9

Patch Changes

0.2.8

Patch Changes

0.2.7

Patch Changes

0.2.6

Patch Changes

0.2.5

Patch Changes

0.2.4

Patch Changes

0.2.3

Patch Changes

  • #458 d3e7a68 Thanks @whoiskatrin! - Add unified async authentication support to useAgent hook The useAgent hook now automatically detects and handles both sync and async query patterns

  • #512 f9f03b4 Thanks @threepointone! - codemode: a tool that generates code to run your tools

  • #499 fb62d22 Thanks @deathbyknowledge! - handle all message types in the reply streaming handler

  • #509 71def6b Thanks @ghostwriternr! - Fix OAuth authentication for MCP servers and add transport configuration

    • Fix authorization codes being consumed during transport fallback
    • Add transport type option to addMcpServer() for explicit control
    • Add configurable OAuth callback handling (redirects, custom responses)
    • Fix callback URL persistence across Durable Object hibernation

0.2.2

Patch Changes

0.2.1

Patch Changes

0.2.0

Minor Changes

  • #495 ff9329f Thanks @ghostwriternr! - Fix OAuth callback handling and add HOST auto-detection
    • Fix OAuth callback "Not found" errors by removing MCPClientManager override
    • Add OAuth callback URL persistence across Durable Object hibernation
    • Fix OAuth connection reuse during reconnect to prevent state loss
    • Add OAuth transport tracking to prevent authorization code consumption during auto-fallback
    • Preserve PKCE verifier across transport attempts
    • Make callbackHost parameter optional with automatic request-based detection
    • Add URL normalization for consistent transport endpoint handling

Patch Changes

  • #465 6db2cd6 Thanks @BeiXiao! - fix(ai-react): prevent stale agent capture in aiFetch; ensure active connection is used

  • #440 9ef35e2 Thanks @axuj! - fix: pass agent._pk as id to useChat to prevent stale WebSocket instances

0.1.6

Patch Changes

0.1.5

Patch Changes

0.1.4

Patch Changes

0.1.3

Patch Changes

0.1.2

Patch Changes

0.1.1

Patch Changes

0.1.0

Minor Changes

Patch Changes

  • #445 14616d3 Thanks @deathbyknowledge! - Fix MCP client to treat client_uri as a valid URL

  • #410 25b261e Thanks @amorriscode! - docs: minor fixes

  • 2684ade Thanks @threepointone! - update deps

  • 01b919d Thanks @threepointone! - remove unstable_ prefixes with deprecation warnings

    This deprecates all unstable_ prefixes with deprecation warnings. Specifically:

    • unstable_callable -> callable
    • unstable_getAITools -> getAITools
    • unstable_getSchedulePrompt -> getSchedulePrompt
    • unstable_scheduleSchema -> scheduleSchema

    Using the unstable_ prefixed versions will now emit a deprecation warning. In the next major version, the unstable_ prefixed versions will be removed.

  • #434 f0c6dce Thanks @threepointone! - don't autowrap getters on an agent

  • #446 696d33e Thanks @Flouse! - fix: use Object.getOwnPropertyDescriptor for property check

  • 1e4188c Thanks @threepointone! - update workers-ai-provider

  • #436 8dac62c Thanks @deathbyknowledge! - Fix onConnect race condition

  • #409 352d62c Thanks @MrgSub! - Refactor message types to use enum in AIChatAgent and related files

  • #442 0dace6e Thanks @threepointone! - fix: don't wrap a method with an agent context if it's already wrapped

0.0.113

Patch Changes

0.0.112

Patch Changes

  • #404 2a6e66e Thanks @threepointone! - udpate dependencies

  • #404 2a6e66e Thanks @threepointone! - log less data

    as part of our observability impl, we were logging way too much data, making it a probable data leak, but also blowing past the max size limit on o11y messages. This reduces the amount of data logged.

0.0.111

Patch Changes

0.0.110

Patch Changes

0.0.109

Patch Changes

0.0.108

Patch Changes

0.0.107

Patch Changes

0.0.106

Patch Changes

0.0.105

Patch Changes

  • #354 f31397c Thanks @jahands! - fix: dequeue items in DB after each task is complete

    Prevents a single failure from causing all items in the queue from being retried (including previously processed items that were successful).

0.0.104

Patch Changes

0.0.103

Patch Changes

0.0.102

Patch Changes

0.0.101

Patch Changes

0.0.100

Patch Changes

  • #331 7acfd65 Thanks @geelen! - Adding a new MCP header to the CORS allowlist to follow the updated spec

0.0.99

Patch Changes

0.0.98

Patch Changes

0.0.97

Patch Changes

0.0.96

Patch Changes

0.0.95

Patch Changes

0.0.94

Patch Changes

0.0.93

Patch Changes

  • #302 b57e1d9 Thanks @cmsparks! - Fix an error where MCP servers pending connection would trigger an error

0.0.92

Patch Changes

0.0.91

Patch Changes

0.0.90

Patch Changes

0.0.89

Patch Changes

0.0.88

Patch Changes

0.0.87

Patch Changes

0.0.86

Patch Changes

0.0.85

Patch Changes

0.0.84

Patch Changes

0.0.83

Patch Changes

0.0.82

Patch Changes

0.0.81

Patch Changes

0.0.80

Patch Changes

0.0.79

Patch Changes

0.0.78

Patch Changes

0.0.77

Patch Changes

0.0.76

Patch Changes

0.0.75

Patch Changes

  • 6c24007 Thanks @threepointone! - Revert "fool typescript into thinking agent will always be defined in ge…

0.0.74

Patch Changes

  • ad0054b Thanks @threepointone! - fool typescript into thinking agent will always be defined in getCurrentAgent()

0.0.73

Patch Changes

0.0.72

Patch Changes

0.0.71

Patch Changes

0.0.70

Patch Changes

0.0.69

Patch Changes

0.0.68

Patch Changes

0.0.67

Patch Changes

0.0.66

Patch Changes

0.0.65

Patch Changes

  • #205 3f532ba Thanks @threepointone! - Let .server on McpAgent be a Server or McpServer

  • #208 85d8edd Thanks @a-type! - Fix: resolved a problem in useAgentChat where initial messages would be refetched on re-render when using React StrictMode

0.0.64

Patch Changes

0.0.63

Patch Changes

0.0.62

Patch Changes

0.0.61

Patch Changes

0.0.60

Patch Changes

  • #173 49fb428 Thanks @cmsparks! - fix: require authProvider on client connect and handle client "Method not found" initialization errors

0.0.59

Patch Changes

0.0.58

Patch Changes

0.0.57

Patch Changes

0.0.56

Patch Changes

0.0.55

Patch Changes

0.0.54

Patch Changes

  • #140 2f5cb3a Thanks @cmsparks! - Remote MCP Client with auth support

    This PR adds:

    • Support for authentication for MCP Clients (Via a DO based auth provider)
    • Some improvements to the client API per #135
    • A more in depth example of MCP Client, which allows you to add any number of remote MCP servers with or without auth

0.0.53

Patch Changes

0.0.52

Patch Changes

0.0.51

Patch Changes

0.0.50

Patch Changes

0.0.49

Patch Changes

  • #138 3bbbf81 Thanks @geelen! - Fixed internal build issue that caused incomplete package to be published

0.0.48

Patch Changes

0.0.47

Patch Changes

0.0.46

Patch Changes

0.0.45

Patch Changes

  • #122 d045755 Thanks @threepointone! - import {context} from 'agents';

    Export the current agent, request, and connection from a shared context. Particularly useful for tool calls that might not have access to the current agent in their module scope.

0.0.44

Patch Changes

0.0.43

Patch Changes

0.0.42

Patch Changes

  • #107 4f3dfc7 Thanks @threepointone! - update deps, allow sub/path/prefix, AND_BINDINGS_LIKE_THIS

    of note,

    • the partyserver update now allows for prefixes that/have/sub/paths
    • bindings THAT_LOOK_LIKE_THIS are correctly converted to kebabcase now

0.0.41

Patch Changes

0.0.40

Patch Changes

0.0.39

Patch Changes

0.0.38

Patch Changes

  • #94 fb4d0a6 Thanks @threepointone! - better error handling (based on #65 by @elithrar)
    • implement this.onError for custom error handling
    • log errors from more places
    • catch some missed async errors and log them
    • mark some methods as actually private

0.0.37

Patch Changes

0.0.36

Patch Changes

0.0.35

Patch Changes

0.0.34

Patch Changes

0.0.33

Patch Changes

  • #85 acbc34e Thanks @threepointone! - Add RPC support with unstable_callable decorator for method exposure. This feature enables:

    • Remote procedure calls from clients to agents
    • Method decoration with @unstable_callable to expose agent methods
    • Support for both regular and streaming RPC calls
    • Type-safe RPC calls with automatic response handling
    • Real-time streaming responses for long-running operations

    Note: The callable decorator has been renamed to unstable_callable to indicate its experimental status.

0.0.32

Patch Changes

0.0.31

Patch Changes

0.0.30

Patch Changes

0.0.29

Patch Changes

  • #79 1dad549 Thanks @threepointone! - clear initial message cache on unmount, add getInitialMessages

    This clears the initial messages cache whenever useAgentChat is unmounted. Additionally, it adds a getInitialMessages option to pass your own custom method for setting initial messages. Setting getInitialMessages:null disables any fetch for initial messages, so that the user can populate initialMessages by themselves if they'd like.

    I also added a chat example to the playground.

0.0.28

Patch Changes

0.0.27

Patch Changes

0.0.26

Patch Changes

0.0.25

Patch Changes

0.0.24

Patch Changes

0.0.23

Patch Changes

0.0.22

Patch Changes

0.0.21

Patch Changes

  • #37 ff0679f Thanks @threepointone! - Agent::initialState

    You can now set an initial state for an agent

    type State = {
      counter: number;
      text: string;
      color: string;
    };
    
    class MyAgent extends Agent<Env, State> {
      initialState = {
        counter: 0,
        text: "",
        color: "#3B82F6",
      };
    
      doSomething() {
        console.log(this.state); // {counter: 0, text: "", color: "#3B82F6"}, if you haven't set the state yet
      }
    }

    As before, this gets synced to useAgent, so you can do:

    const [state, setState] = useState<State>();
    const agent = useAgent<State>({
      agent: "my-agent",
      onStateUpdate: (state) => {
        setState(state);
      },
    });

0.0.20

Patch Changes

0.0.19

Patch Changes

0.0.18

Patch Changes

0.0.17

Patch Changes

0.0.16

Patch Changes

0.0.15

Patch Changes

0.0.14

Patch Changes

0.0.13

Patch Changes

0.0.12

Patch Changes

0.0.11

Patch Changes

0.0.10

Patch Changes

0.0.9

Patch Changes

0.0.8

Patch Changes

  • 619dac5 Thanks @threepointone! - new useChat, with multiplayer, syncing, persistence; updated HITL guide with useChat

0.0.7

Patch Changes

0.0.6

Patch Changes

0.0.5

Patch Changes

0.0.4

Patch Changes

0.0.3

Patch Changes

0.0.2

Patch Changes

0.0.1

Patch Changes