Releases: cloudflare/agents
agents@0.11.9
Patch Changes
-
#1412
8fb7c03Thanks @threepointone! - MakeapplyChunkToPartsidempotent against an existing tool part with the sametoolCallId, and addisReplayChunk(parts, chunk)for stream broadcasters that want to drop provider replay chunks (#1404).Some providers (notably the OpenAI Responses API) re-emit a prior tool call in continuation streams. The previous
tool-input-starthandler unconditionally pushed a fresh tool part, which produced duplicate parts in the message;tool-input-deltaandtool-input-availableoverwrote a fully resolved input/state if a chunk happened to arrive for an already-known toolCallId. The new behavior:tool-input-startfor atoolCallIdthat already exists inpartsis a no-op (it does not push a duplicate or regress state).tool-input-deltaonly mutates input while the existing part is stillinput-streaming.tool-input-availableonly advances frominput-streamingtoinput-available; replays against parts that have already moved pastinput-streaming(includingapproval-requested/approval-respondedand any terminal state) are no-ops.
isReplayChunk(parts, chunk)is exported fromagents/chatfor stream broadcasters (e.g.AIChatAgent._streamSSEReply) that want to detect "this chunk is a replay of an already-known tool call" and skip re-broadcasting it. AI SDK v6'supdateToolParton the client mutates an existing tool part in place when the toolCallId matches, so re-broadcasting these replay chunks would visibly regress anoutput-availablepart toinput-streamingon connected clients.tool-output-availableis not treated as a replay because its in-place update is safe when the output already matches.Tool calls that the model genuinely wants to re-issue always carry a new toolCallId, so an existing match is never a legitimate "start over".
@cloudflare/ai-chat@0.5.4
Patch Changes
-
#1412
8fb7c03Thanks @threepointone! - Stop provider tool-call replays from regressing tool part state during continuation streams (#1404).Some providers (notably the OpenAI Responses API) re-emit prior tool calls in continuation streams as a
tool-input-start→tool-input-delta→tool-input-available→tool-output-availablesequence carrying the sametoolCallIdand the sameoutputthe part already holds. The AI SDK'supdateToolPartmutates an existing tool part in place when the toolCallId matches, so a replayedtool-input-startwas clobbering anoutput-availablepart back toinput-streamingon the client and producing the worker warn_applyToolResult: Tool part with toolCallId X not in expected state.Two fixes:
_streamSSEReplynow drops replay tool-input chunks before broadcasting them to clients or storing them for resume, using the new sharedisReplayChunkhelper. The cloned server-side streaming message is never corrupted becauseapplyChunkToPartsis idempotent against existing toolCallIds for these chunk types (also fixed below)._applyToolResultacceptsoutput-availableandoutput-erroras valid starting states for idempotent re-application. A duplicatecf_agent_tool_result(cross-tab re-run, redelivered WS frame, provider replay round-trip) is now a silent no-op rather than a warn + skipped update. The cross-messagetool-output-available/tool-output-errorfallback in_streamSSEReplygets the same tolerance.
_findAndUpdateToolPartskips the SQLite write andMESSAGE_UPDATEDbroadcast when the apply produced no semantic change, so idempotent re-applies don't churn UI on connected tabs.
hono-agents@3.0.11
Patch Changes
-
ca510d4Thanks @threepointone! - Tighten theagentspeer dependency floor from>=0.9.0to>=0.11.7to reflect the current monorepo set we actually test against. Upper bound (<1.0.0) is unchanged. The correspondingagentsdevDependency is also bumped from^0.11.0to^0.11.7so dev and peer floors line up.No runtime change in
hono-agentsitself. The visible effect for consumers: pairing the latesthono-agentswith a staleagents(<0.11.7) now produces a peer warning where it previously did not. That's the intended signal —agentsversions older than 0.11.7 are no longer tested against thishono-agents.
agents@0.11.8
Patch Changes
-
#1411
2fa68beThanks @threepointone! - AddAbortRegistry.linkExternal(id, signal)for connecting externalAbortSignals to per-request abort controllers, and add"aborted"to theSaveMessagesResult.statusunion (#1406).linkExternalis the integration point for callers that drive a chat turn programmatically and want to cancel it from outside without knowing the internally-generated request id (the helper-as-sub-agent pattern, where a parent'sAbortSignalfrom the AI SDK toolexecuteneeds to land inside a sub-agent'ssaveMessagescall). When the external signal aborts, the registry's controller foridis cancelled — the same pathchat-request-canceltakes over the WebSocket. The returned detacher must be called infinallyto avoid leaking listeners on long-lived parent signals.SaveMessagesResult.statusnow includes"aborted"alongside"completed"and"skipped". Existing callers that only switch on"completed"are unaffected; turns cancelled via the new signal API surface as"aborted"rather than"completed".Also exposes
SaveMessagesOptionsfromagents/chatfor use by@cloudflare/thinkand@cloudflare/ai-chattyped APIs.AbortRegistry.cancel(id, reason?)now accepts an optional reason that flows through tosignal.reasonon the cancelled controller.See
cloudflare/agents#1406for the motivating use case. -
ca510d4Thanks @threepointone! - Tighten internal peer dependency floors to reflect the current monorepo set we actually test against:@cloudflare/ai-chat(>=0.0.8→>=0.5.2) and@cloudflare/codemode(>=0.0.7→>=0.3.4). Upper bound (<1.0.0) is unchanged.No runtime change in
agentsitself. The visible effect for consumers: pairing the latestagentswith a stale@cloudflare/ai-chat(<0.5.2) or@cloudflare/codemode(<0.3.4) now produces a peer warning where it previously did not. That's the intended signal — those older combinations are no longer tested in the monorepo.
agents@0.11.7
Patch Changes
- #1405
03620a6Thanks @threepointone! - Bumppartyserverpeer dependency to^0.5.4. 0.5.4 closescloudflare/partykit#390: fresh 0.5.x DOs withcompatibility_dateolder than 2026-03-15 could losethis.nameon alarm wake (noctx.id.namepropagation in older runtimes, and 0.5.x had stopped writing the__ps_namelegacy fallback record). The fix is a defensive one-time__ps_namewrite on first fetch — idempotent, restores the safety net pre-0.5.x had. Affects any project on a pre-cutoffcompatibility_datewhose DOs schedule alarms (which includes Think's_chatRecoveryContinue).
@cloudflare/worker-bundler@0.1.3
Patch Changes
-
19a4c08Thanks @threepointone! - Bumpes-module-lexerfrom^2.0.0to^2.1.0. Caret upper bound (<3.0.0) is unchanged.No API or runtime behavior change in
@cloudflare/worker-bundleritself.
@cloudflare/voice@0.1.3
Patch Changes
-
ca510d4Thanks @threepointone! - Tighten theagentspeer dependency floor from>=0.9.0to>=0.11.7to reflect the current monorepo set we actually test against. Upper bound (<1.0.0) is unchanged.No runtime change in
@cloudflare/voiceitself. The visible effect for consumers: pairing the latest@cloudflare/voicewith a staleagents(<0.11.7) now produces a peer warning where it previously did not. That's the intended signal —agentsversions older than 0.11.7 are no longer tested against this@cloudflare/voice.
@cloudflare/think@0.4.2
Patch Changes
-
ca510d4Thanks @threepointone! - Tighten internal peer dependency floors to reflect the current monorepo set we actually test against:agents(>=0.8.7→>=0.11.7),@cloudflare/codemode(>=0.0.7→>=0.3.4), and@cloudflare/shell(>=0.2.0→>=0.3.4). Upper bounds (<1.0.0) are unchanged.No runtime change in
@cloudflare/thinkitself. The visible effect for consumers: pairing the latest@cloudflare/thinkwith a staleagents(<0.11.7),@cloudflare/codemode(<0.3.4), or@cloudflare/shell(<0.3.4) now produces a peer warning where it previously did not. That's the intended signal — those older combinations are no longer tested in the monorepo. -
#1411
2fa68beThanks @threepointone! - Addoptions.signaltoThink.saveMessagesandThink.continueLastTurnfor external cancellation of programmatic turns, plus protectedabortRequest(id)/abortAllRequests()methods to replace bracket access into the private_abortsregistry (#1406).saveMessagesandcontinueLastTurnaccept a secondSaveMessagesOptionsargument:const result = await this.saveMessages(messages, { signal: controller.signal, }); if (result.status === "aborted") { // Inference loop terminated mid-stream; partial chunks persisted. }
The signal is linked to Think's per-turn
AbortControllerfor the duration of the call. When it aborts:- the inference loop's signal aborts (the same path
chat-request-canceltakes); - partial chunks already streamed are persisted to the resumable stream;
saveMessagesresolves with{ status: "aborted" };onChatResponsefires withstatus: "aborted".
Pre-aborted signals short-circuit before any model work runs. Listeners are detached cleanly when the turn finishes, so passing the same long-lived
AbortSignalto many turns (e.g. a parent chat-turn signal driving multiple sub-agent calls) is safe and leak-free.abortRequest(id, reason?)andabortAllRequests()are protected entry points for DO subclasses (e.g. RPC-driven helpers) that want to cancel turns without tracking ids — they replace the historical(this as unknown as { _aborts: ... })._aborts.destroyAll()workaround used by helper-as-sub-agent implementations.SaveMessagesResult.statusnow includes"aborted"alongside"completed"and"skipped". Existing callers that only switch on"completed"are unaffected.Limitations.
AbortSignalcannot cross Durable Object RPC. Construct the controller inside the DO that callssaveMessages. To bridge a parent's intent into a child DO, return aReadableStreamfrom the child whosecancelcallback aborts a per-turn controller —examples/agents-as-toolsshows the canonical pattern.- The signal lives in memory only. If the DO hibernates mid-turn and
chatRecoveryis enabled, the recovered turn callscontinueLastTurn()internally without the original signal — an abort fired after restart has no effect on the recovered turn.
See
cloudflare/agents#1406for the motivating use case. - the inference loop's signal aborts (the same path
@cloudflare/shell@0.3.5
Patch Changes
-
19a4c08Thanks @threepointone! - Bump dependencies:isomorphic-gitfrom^1.37.5to^1.37.6(runtime) and@cloudflare/vitest-pool-workersfrom^0.15.0to^0.15.1(devDependency, test-only — does not affect the published artifact).No API or runtime behavior change in
@cloudflare/shellitself.
@cloudflare/ai-chat@0.5.3
Patch Changes
-
ca510d4Thanks @threepointone! - Tighten theagentspeer dependency floor from>=0.8.7to>=0.11.7to reflect the current monorepo set we actually test against. Upper bound (<1.0.0) is unchanged.No runtime change in
@cloudflare/ai-chatitself. The visible effect for consumers: pairing the latest@cloudflare/ai-chatwith a staleagents(<0.11.7) now produces a peer warning where it previously did not. That's the intended signal —agentsversions older than 0.11.7 are no longer tested against this@cloudflare/ai-chat. -
#1411
2fa68beThanks @threepointone! - Addoptions.signaltoAIChatAgent.saveMessagesandcontinueLastTurnfor external cancellation of programmatic turns, plus protectedabortRequest(id)/abortAllRequests()methods (#1406).saveMessagesandcontinueLastTurnaccept a secondSaveMessagesOptionsargument:const result = await this.saveMessages(messages, { signal: controller.signal, }); if (result.status === "aborted") { // Inference loop terminated mid-stream; partial chunks persisted. }
The signal is linked to AIChatAgent's per-turn
AbortControllerand produces the same end state as achat-request-cancelWebSocket message: the inference loop's signal aborts, partial chunks persist, the result reportsstatus: "aborted", andonChatResponsefires with the same status. Pre-aborted signals short-circuit before any model work runs. Listeners are detached cleanly when the turn finishes, so the same long-lived signal can be passed to many turns without leaking.abortRequest(id, reason?)andabortAllRequests()are protected entry points for subclasses that want to cancel turns without tracking ids.SaveMessagesResult.statusnow includes"aborted"alongside"completed"and"skipped". Existing callers that only switch on"completed"are unaffected.Limitations.
AbortSignalcannot cross Durable Object RPC. Construct the controller inside the DO that callssaveMessages.- The signal lives in memory only. If the DO hibernates mid-turn and
chatRecoveryis enabled, the recovered turn runs without the original signal.
See
cloudflare/agents#1406for the motivating use case.