Skip to content

Version Packages - #995

Merged
threepointone merged 1 commit into
mainfrom
changeset-release/main
Mar 2, 2026
Merged

Version Packages#995
threepointone merged 1 commit into
mainfrom
changeset-release/main

Conversation

@github-actions

@github-actions github-actions Bot commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

agents@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.

@cloudflare/ai-chat@0.1.6

Patch Changes

  • #1040 766f20b Thanks @threepointone! - Changed waitForMcpConnections default from false to { timeout: 10_000 }. MCP connections are now waited on by default with a 10-second timeout, so getAITools() returns the full set of tools in onChatMessage without requiring explicit opt-in. Set waitForMcpConnections = false to restore the previous behavior.

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

  • #1013 11aaaff Thanks @threepointone! - Fix Gemini "missing thought_signature" error when using client-side tools with addToolOutput.

    The server-side message builder (applyChunkToParts) was dropping providerMetadata from tool-input stream chunks instead of storing it as callProviderMetadata on tool UIMessage parts. When convertToModelMessages later read the persisted messages for the continuation call, callProviderMetadata was undefined, so Gemini never received its thought_signature back and rejected the request.

    • Preserve callProviderMetadata (mapped from stream providerMetadata) on tool parts in tool-input-start, tool-input-available, and tool-input-error handlers — both create and update paths
    • Preserve providerExecuted on tool parts (used by convertToModelMessages for provider-executed tools like Gemini code execution)
    • Preserve title on tool parts (tool display name)
    • Add providerExecuted to StreamChunkData type explicitly
    • Add 13 regression tests covering all affected codepaths
  • #989 8404954 Thanks @threepointone! - Fix active streams losing UI state after reconnect and dead streams after DO hibernation.

    • Send replayComplete signal after replaying stored chunks for live streams, so the client flushes accumulated parts to React state immediately instead of waiting for the next live chunk.
    • Detect orphaned streams (restored from SQLite after hibernation with no live LLM reader) via _isLive flag on ResumableStream. On reconnect, send done: true, complete the stream, and reconstruct/persist the partial assistant message from stored chunks.
    • Client-side: flush activeStreamRef on replayComplete (keeps stream alive for subsequent live chunks) and on done during replay (finalizes orphaned streams).
  • #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.

  • #993 f706e3f Thanks @ferdousbhai! - fix(ai-chat): preserve server tool outputs when client sends approval-responded state

    _mergeIncomingWithServerState now treats approval-responded the same as
    input-available when the server already has output-available for a tool call,
    preventing stale client state from overwriting completed tool results.

  • #1038 e61cb4a Thanks @threepointone! - fix(ai-chat): preserve server-generated assistant messages when client appends new messages

    The _deleteStaleRows reconciliation in persistMessages now only deletes DB rows when the incoming message set is a subset of the server state (e.g. regenerate trims the conversation). When the client sends new message IDs not yet known to the server, stale deletion is skipped to avoid destroying assistant messages the client hasn't seen.

  • #1014 74a3815 Thanks @threepointone! - Fix regenerate() leaving stale assistant messages in SQLite

    Bug 1 — Transport drops trigger field:
    WebSocketChatTransport.sendMessages was not including the trigger field
    (e.g. "regenerate-message", "submit-message") in the body payload sent
    to the server. The AI SDK passes this field so the server can distinguish
    between a new message and a regeneration request. Fixed by adding
    trigger: options.trigger to the serialized body.

    On the server side, trigger is now destructured out of the parsed body
    alongside messages and clientTools, so it does not leak into
    options.body in onChatMessage. Users who inspect options.body will
    not see any change in behavior.

    Bug 2 — persistMessages never deletes stale rows:
    persistMessages only performed INSERT ... ON CONFLICT DO UPDATE (upsert),
    so when regenerate() removed the last assistant message from the client's
    array, the old row persisted in SQLite. On the next _loadMessagesFromDb,
    the stale assistant message reappeared in this.messages, causing:

    • Anthropic models to reject with HTTP 400 (conversation must end with a
      user message)
    • Duplicate/phantom assistant messages across reconnects

    Fixed by adding an internal _deleteStaleRows option to persistMessages.
    When the chat-request handler (CF_AGENT_USE_CHAT_REQUEST) calls
    persistMessages, it passes { _deleteStaleRows: true }, which deletes
    any DB rows whose IDs are absent from the incoming (post-merge) message set.
    This uses the post-merge IDs from _mergeIncomingWithServerState to
    correctly handle cases where client assistant IDs are remapped to server IDs.

    The _deleteStaleRows flag is internal only (@internal JSDoc) and is
    never passed by user code or other handlers (CF_AGENT_CHAT_MESSAGES,
    _reply, saveMessages). The default behavior of persistMessages
    (upsert-only, no deletes) is unchanged.

    Bug 3 — Content-based reconciliation mismatches identical messages:
    _reconcileAssistantIdsWithServerState used a single-pass cursor for both
    exact-ID and content-based matching. When an exact-ID match jumped the
    cursor forward, it skipped server messages needed for content matching
    of later identical-text assistant messages (e.g. "Sure", "I understand").

    Rewritten with a two-pass approach: Pass 1 resolves all exact-ID matches
    and claims server indices. Pass 2 does content-based matching only over
    unclaimed server indices. This prevents exact-ID matches from interfering
    with content matching, fixing duplicate rows in long conversations with
    repeated short assistant responses.

  • #999 95753da Thanks @threepointone! - Fix useChat status staying "ready" during stream resumption after page refresh.

    Four issues prevented stream resumption from working:

    1. addEventListener race: onAgentMessage always handled CF_AGENT_STREAM_RESUMING before the transport's listener, bypassing the AI SDK pipeline.
    2. Transport instance instability: useMemo created new transport instances across renders and Strict Mode cycles. When _pk changed (async queries, socket recreation), the resolver was stranded on the old transport while onAgentMessage called handleStreamResuming on the new one.
    3. Chat recreation on _pk change: Using agent._pk as the useChat id caused the AI SDK to recreate the Chat when the socket changed, abandoning the in-flight makeRequest (including resume). The resume effect wouldn't re-fire on the new Chat.
    4. Double STREAM_RESUMING: The server sends STREAM_RESUMING from both onConnect and the RESUME_REQUEST handler, causing duplicate ACKs and double replay without deduplication.

    Fixes:

    • Replace addEventListener-based detection with handleStreamResuming() — a synchronous method onAgentMessage calls directly, eliminating the race.
    • Make the transport a true singleton (useRef, created once). Update transport.agent every render so sends/listeners always use the latest socket. The resolver survives _pk changes because the transport instance never changes.
    • Use a stable Chat ID (initialMessagesCacheKey based on URL + agent + name) instead of agent._pk, preventing Chat recreation on socket changes.
    • Add localRequestIdsRef guard to skip duplicate STREAM_RESUMING messages for streams already handled by the transport.
  • #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.

@cloudflare/codemode@0.1.2

Patch Changes

hono-agents@3.0.7

Patch Changes

@github-actions
github-actions Bot force-pushed the changeset-release/main branch 23 times, most recently from 6bc1872 to 7abab09 Compare March 2, 2026 10:45
@github-actions
github-actions Bot force-pushed the changeset-release/main branch from 7abab09 to bf66177 Compare March 2, 2026 11:31
@threepointone
threepointone merged commit 64d9ad6 into main Mar 2, 2026
@threepointone
threepointone deleted the changeset-release/main branch March 2, 2026 11:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant