Version Packages - #2174
Open
github-actions[bot] wants to merge 1 commit into
Open
Conversation
github-actions
Bot
force-pushed
the
changeset-release/main
branch
4 times, most recently
from
August 31, 2026 01:01
de5a996 to
4c122de
Compare
github-actions
Bot
force-pushed
the
changeset-release/main
branch
4 times, most recently
from
September 1, 2026 14:51
15212d9 to
af71017
Compare
github-actions
Bot
force-pushed
the
changeset-release/main
branch
from
September 1, 2026 15:45
af71017 to
9ec5b44
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.23.0
Minor Changes
#2193
87bd594Thanks @mattzcarey! - Extract facet ("sub-agent") machinery intopackages/agents/src/dynamic-agents/, add thethis.dynamicAgentscapability facade, and reposition facets as an isolation primitive rather than the recommended way to model many chat sessions.Agent's facet routing, WebSocket forwarding, virtual connections, and registry (~2,400 ofindex.ts's ~12,150 lines) move into a dedicated module registered as a Lifecycle capability (capabilityId: "dynamic-agents"); its hot paths stay composition-root wired since the capability-runner hook contract can't express request-rewrite-and-continue or post-claim WebSocket forwarding. No wire- or storage-visible identifier changes.The public surface gains
this.dynamicAgents.{get,abort,delete,has,list}plus theDynamicAgentClassandDynamicAgentStubtype names.SubAgentClassandSubAgentStubremain as compatibility aliases.subAgent()/abortSubAgent()/deleteSubAgent()/hasSubAgent()/listSubAgents()are unchanged in behavior and now delegate to the same capability —@deprecatedin place, not removed./sub/URLs,useAgent({ sub }),parentAgent(), andonBeforeSubAgentare untouched.docs/agents/sub-agents.mdis rewritten: verified workerd facet semantics (separate isolate, own SQLite, no independent alarms, bounded nesting depth, machine-pinned tree), a corrected claim about WebSocket frame forwarding (every frame wakes the root parent — it was never true that frames go directly to the child post-upgrade), and an explicit decision rule for facets vs. independent Durable Objects. Two new examples:examples/next/dynamic-agents(a supervisor running user-submitted Durable Object code as facets via Worker Loader — what facets are for) andexamples/next/chats(one top-level DO per chat plus a per-user push-based index — the recommended many-chats pattern), both with a React + Vite UI and workers-pool tests.#2175
8ffb3adThanks @mattzcarey! - Lifecycle owns a durable job queue, driven as an alarm event loop.The thing in the queue is a job: a serialisable callback address — the
owning capability plus a function name — with a due time and a payload.
Capabilities and the host push jobs through the scoped
jobssurface;Lifecycle drives due jobs in timestamp order when the alarm fires, owns
dispatch retries and platform-failure deferral, arms a deadman pre-alarm
before driving so an isolate death mid-drive still wakes the object, and
derives the physical alarm purely from queue state (queue mutations re-arm
automatically; an exclusive job suppresses ordinary candidates).
The pull-based alarm-contribution model is removed: capability
getNextAlarm()/onAlarm(), hostgetNextAlarm(),LifecycleServices.alarms(rearm/disabled), andAlarmContributionare gone. Host
onAlarm()remains and runs once per alarm invocationafter due jobs are driven. Terminal application failures reach the
owner's
onJobError(), whose drive result decides advancement.The alarm memory-limit circuit breaker (#1825) moves from
Agent.alarm()into the Lifecycle event loop, targeting the exact executing job; Agent
contributes domain policy through the new
onAlarmMemoryLimit()hosthook, and Scheduler's
__DO_NOT_USE_WILL_BREAK__handleAlarmMemoryLimitescape hatch is gone. After recording a strike the breaker now finishes by
resetting the isolate with
ctx.abort(reason, { retryAlarm: false })(retry of the handled alarm suppressed; the backoff alarm owns the next
wake), and
Agent.destroy()uses the same no-retry abort so a completedteardown's alarm cannot be retried into a fresh constructor that recreates
the deleted schema.
Scheduler keeps its entire public API and loses its storage and due-row
loop: a schedule is one job whose
fnis the callback name, and intervalschedules are single-flight jobs. Existing
cf_agents_schedulesrows aremigrated into the
cf_agents_jobsqueue on startup and the legacy tableis dropped. Agent's public scheduling and
keepAlive()APIs areunchanged; its keep-alive, fiber-recovery/facet housekeeping, and
deferred-destroy wakes are now host jobs, and Think's
workflow-notification wake replaces the removed
_getExtensionAlarm().#2190
58c586aThanks @mattzcarey! - Make the alarm memory-limit circuit breaker (#1825) a self-containedLifecycle concern instead of an Agent-mediated one.
Recovery-loop membership is now a property of the job row
(
LifecycleJobPushOptions.recoveryLoop): flagged jobs are backed off bythe breaker on a strike and purged when it seals at the strike budget,
without disturbing unrelated rows — a recovery schedule can no longer
silently escape the breaker. The public
ScheduleOptionsvocabulary isunchanged: schedules only shape future work, and chat recovery reaches the
flag through internal scaffolding (
RecoveryLoopScheduleOptions) that willbe deleted when recovery migrates onto the Tasks capability, where
OOM-prone loops belong.
Capabilities can react to a strike through the new optional
onMemoryLimithook, hosts through
onAlarmMemoryLimit, and the context identifies the jobthat was executing when one exists. The strike budget is real Lifecycle
configuration (
Lifecycle.install(host, { maxAlarmMemoryLimitStrikes }))rather than a composition-root side channel. Until chat recovery moves to
Tasks, a sealed routed recovery schedule also forwards the seal to its owning
dynamic agent so a chat child under a plain Agent root persists its exhausted
incident and terminal notification.
Removed accordingly:
Agent.onAlarmMemoryLimit's policy relay, the_cf_recoveryAlarmCallbackstemplate hook,Scheduler.applyMemoryLimitPolicy,and
setLifecycleAlarmMemoryLimitStrikes.AIChatAgentandThinkflag theirrecovery schedules via
chatRecoverySchedulePolicyand seal in-flightincidents from their own protected
onAlarmMemoryLimithooks; both nowrequire
agents >= 0.23.0(they consume newagents/chatexports and nolonger implement the old template-method breaker hooks). Agent retains a
sealed-only call to
_cf_sealMemoryLimitedRecoveryso already-published chatpackages whose peer ranges accept agents 0.23 keep terminal notifications;
that fallback carries no callback-name or queue policy.
#2169
b12dc0bThanks @mattzcarey! - Move WebSockets out of Lifecycle into the opt-inWebSocketscapability, with callables served from an
RpcTarget.Lifecycle no longer models WebSockets — many hosts never use sockets.
Hosts that want connections install the capability, which owns the
subsystem end to end:
The capability claims WebSocket upgrades, accepts hibernating sockets,
dispatches handlers inside the host invocation boundary, reciprocates
close handshakes, closes owned connections on host destruction, and
answers
getConnections()/getConnection(). Without it installed,upgrades are declined.
callablesexposes anRpcTarget's prototype methods to remotecallers over a Cap'n Web session (
?__agents_rpc=capnweb), with nativeReadableStreamstreaming.Agentadds no new surface for this: its@callable()-decorated methods are its interface, served on every wire— natively over the legacy JSON RPC protocol and, through the
decorator-derived target, over the Cap'n Web endpoint. There is no
separate browser client either:
useAgent().stub/callreach thesame interface, and a plain host's endpoint is one
newWebSocketRpcSession(new WebSocket(callablesRpcUrl(url)))away.Agentinstalls the capability itself, so itsonConnect/onMessage/onClose/onError/getConnectionTagsoverrides and connection APIsbehave exactly as before (same wire, same hibernation attachment
format). The Lifecycle host contract drops the WebSocket hooks and
Lifecycle's
getConnections/getConnection/broadcastare removed.Lifecycle keeps only generic platform pass-throughs —
onWebSocketUpgradeplusonWebSocketMessage/Close/Errorforcapability-owned hibernation wakes — and
LifecycleServicesgains anarrow
socketssurface (accept/get) and a connection/request scope onrunInHostContext. The capability interaction contract (threechannels: hooks, services, composition-root apertures) is now
documented on
DurableObjectCapability.Patch Changes
#2173
71ce28aThanks @mattzcarey! - Define the Lifecycle job dispatch contract. Job ids are now scoped to theirowning capability: a cross-owner id collision throws instead of silently
replacing the other owner's job. A same-id
push()orreschedule()madewhile a job is dispatching supersedes the returned drive result, so a wake
pushed mid-drive can no longer be lost — and each due job is refetched
before dispatch, so a job replaced earlier in the same alarm cycle is
skipped instead of dispatched from its stale snapshot. A dispatch that
outlives its job's
hung timeout logs a warning and emits
job:slow_dispatchtelemetry —onJobmust stay bounded and detach unbounded work.#2173
71ce28aThanks @mattzcarey! - Replatform chat's resumable streams onto theagents/streamscapability.ResumableStreamis now a thin adapter overStreams: chat's in-flight turn output lives in the shared durable chunk log (cf_agents_streams/cf_agents_stream_chunks), packed ~10 wire chunks per stored segment for write economy, with completion/error mapped onto stream settlement and retention keyed off the stream row'supdated_at(sweeps no longer scan the chunk table). Existingcf_ai_chat_stream_*tables migrate wholesale — including an in-flight stream — on first construction after upgrade, then are dropped.AIChatAgentandThinkexpose the backing capability asreadonly streams, so anystreams.read()consumer on the same Durable Object can observe chat streams. The chat wire protocol, replay handshake, and recovery behavior are unchanged.#2191
b40bc5bThanks @mattzcarey! - Cut storage row writes across Streams, the chat adapter, and Tasks — the streaming hot path now writes exactly what the pre-capability chat pattern wrote.Streams: the append fence is a read instead of a guarded UPDATE (a Durable Object executes one synchronous block at a time, so state-check + tail-read + INSERT is exactly as atomic), removing one stream-row write per append. The stream row is written only at open and settle; settlement stamps the final cursor, and live cursors/liveness derive from the chunk log's tail.
readBatchestermination and the reader liveness checks moved to narrow reads.Chat adapter: the retention sweep decides abandonment in two phases (coarse row cutoff, then one indexed chunk-tail read per candidate) so an actively appending stream is never swept; the legacy migration imports rows complete (final count and last-activity stamped up front, chunk imports are bare INSERTs — 1+N writes instead of 1+2N);
destroy()no longer flushes chunks it deletes in the same call; the cleanup alarm no longer scans the table twice; dead_segmentIndexstate removed.Tasks: claim refreshes amortize to one row write per half claim-slack of wall time instead of one per step; already-elapsed sleeps journal born-completed in one INSERT; duplicate status messages skip their write; startup reconcile skips job-queue upserts that already match; a parked-run cancel settles in one row write; settle paths only re-sync the wake mirror when their write actually landed.
Replay memory is bounded: the chat adapter's chunk replay iterates the stored log in pages (a generator over paged reads) instead of materializing the whole turn per reconnecting client.
Schema: the hot-write capability tables (stream chunks, task runs, task steps, jobs — none released) are now WITHOUT ROWID. Cloudflare bills index maintenance as rows written, and an ordinary rowid table's PRIMARY KEY is a hidden UNIQUE index — so every chunk append was billing 2 rows despite being one table write. WITHOUT ROWID makes it exactly 1. The stream metadata table deliberately stays a rowid table: rowid is the insertion-order tiebreak that keeps newest-first deterministic for same-millisecond rows, at one billed row per stream open. The task runs table also drops its
(state, next_at)index, which taxed every claim/refresh/settle write to speed one startup scan.The in-suite storage-ops benchmark now pins adapter/legacy write parity exactly (12 table rows per 100-chunk turn, ~8.5× under naive per-chunk appends), models the two-phase sweep, and a write-accounting test pins the billed model per statement (a 100-chunk turn bills 14 rows vs the legacy schema's 33).
#2173
71ce28aThanks @mattzcarey! - Addagents/streams: durable incremental output as a Lifecycle capability (experimental).One
Streamsinstance per Durable Object owns an ordered, durable chunk log per stream with a monotonic cursor:open()(idempotent on the id), synchronous durableappend()that wakes live readers,close()/error()settlement, replay-then-tailread({ from, signal })plus its batched formreadBatches({ from, signal, batchSize, onUpToDate })(arrays per replay slice and per live-tail wakeup, with a caught-up-to-tail signal), indexed non-uniquetags for find-the-latest-stream-of-an-operation lookups (open(id, { tag })/list({ tag })),sseResponse()for one-call SSE serving with nativeLast-Event-IDresume andup-to-date/done/errorcontrol events, andstatus()reporting state, cursor, and last activity. Reads are independent of producer liveness; the capability needs no alarm, so it also works on facets.Streams is the incremental-output half of the pattern the Tasks migration validated, composed without coupling: a task step appends to a stream and checkpoints
{ streamId, cursor }, and itsrecovercallback readsstreams.status()as durable interruption evidence — proven across a real SIGKILL by the e2e suite, where recovery finalizes the stream at exactly the chunks that survived. Design record:design/rfc-streams.md.#2173
71ce28aThanks @mattzcarey! - Addagents/tasks: durable, replayable background execution as a Lifecycle capability (experimental).One
Tasksinstance per Durable Object owns any number of named Task definitions declared in its constructor (new Tasks({ definitions: {...} }), mirroring the Scheduler's callbacks map), so the registry is rebuilt on every wake and recovery of in-flight runs is correct by construction. Runs start with the typedtasks.run(name, input, options), andtasks.handle(name)gives a typed lens scoped to one definition. A run survives process loss and deployments by replaying its handler from the top: completedstep.do()steps return journaled results,step.sleep()/step.sleepUntil()consult persisted deadlines, and execution continues from the first unfinished step under generation fencing. Steps carry per-attempt retry and timeout policy, stable idempotency keys for external deduplication, andstep.status()progress with a replay live gate that never re-publishes old progress as new.There is no separate recovery mode: an unclean interruption replays the handler on the next wake, and handlers make replay safe with step idempotency keys for external writes and durable evidence (a stream's cursor, a rows-written count) read at the top of the work. The interrupted step is first-class evidence:
step.interruptedis{ name, attempt }on a replay after process loss (nullon clean attempts), and atask:attempt:interruptedevent carries the same step. Clean step failures are not interruptions; the retry policy owns them.Agentinstalls the capability automatically as experimentalthis.tasks, with subclass definitions declared on the overridabletaskDefinitionsfield and framework-internal definitions attached through a composition-root aperture. The internal chat frameworks now run on it: Think and AIChatAgent chat turns and Think's messenger replies each execute as a journaled step withstash()persisted in host storage, and a replay whose live closure is gone branches into the unchanged ChatRecoveryEngine (and messenger recovery) on durable evidence. The legacyrunFiber()/startFiber()APIs are unchanged and still recovered by their own scan; facet-hosted turns stay on the legacy engine until routed Fibers land.Runs are durably accepted (
tasks.run()returns a receipt; idempotency keys join existing runs), inspectable (get,getByIdempotencyKey,list), cooperatively cancellable, and retained until deleted. The capability stores run deadlines in its own tables and mirrors each non-terminal run as one job in the Lifecycle work queue (never touching the physical alarm), so it composes with the Scheduler and other capabilities on one shared, queue-derived alarm. Design record:design/rfc-fibers.md(shipped under the name Tasks).@cloudflare/think@0.18.0
Minor Changes
#2175
8ffb3adThanks @mattzcarey! - Lifecycle owns a durable job queue, driven as an alarm event loop.The thing in the queue is a job: a serialisable callback address — the
owning capability plus a function name — with a due time and a payload.
Capabilities and the host push jobs through the scoped
jobssurface;Lifecycle drives due jobs in timestamp order when the alarm fires, owns
dispatch retries and platform-failure deferral, arms a deadman pre-alarm
before driving so an isolate death mid-drive still wakes the object, and
derives the physical alarm purely from queue state (queue mutations re-arm
automatically; an exclusive job suppresses ordinary candidates).
The pull-based alarm-contribution model is removed: capability
getNextAlarm()/onAlarm(), hostgetNextAlarm(),LifecycleServices.alarms(rearm/disabled), andAlarmContributionare gone. Host
onAlarm()remains and runs once per alarm invocationafter due jobs are driven. Terminal application failures reach the
owner's
onJobError(), whose drive result decides advancement.The alarm memory-limit circuit breaker (#1825) moves from
Agent.alarm()into the Lifecycle event loop, targeting the exact executing job; Agent
contributes domain policy through the new
onAlarmMemoryLimit()hosthook, and Scheduler's
__DO_NOT_USE_WILL_BREAK__handleAlarmMemoryLimitescape hatch is gone. After recording a strike the breaker now finishes by
resetting the isolate with
ctx.abort(reason, { retryAlarm: false })(retry of the handled alarm suppressed; the backoff alarm owns the next
wake), and
Agent.destroy()uses the same no-retry abort so a completedteardown's alarm cannot be retried into a fresh constructor that recreates
the deleted schema.
Scheduler keeps its entire public API and loses its storage and due-row
loop: a schedule is one job whose
fnis the callback name, and intervalschedules are single-flight jobs. Existing
cf_agents_schedulesrows aremigrated into the
cf_agents_jobsqueue on startup and the legacy tableis dropped. Agent's public scheduling and
keepAlive()APIs areunchanged; its keep-alive, fiber-recovery/facet housekeeping, and
deferred-destroy wakes are now host jobs, and Think's
workflow-notification wake replaces the removed
_getExtensionAlarm().Patch Changes
#2173
71ce28aThanks @mattzcarey! - Replatform chat's resumable streams onto theagents/streamscapability.ResumableStreamis now a thin adapter overStreams: chat's in-flight turn output lives in the shared durable chunk log (cf_agents_streams/cf_agents_stream_chunks), packed ~10 wire chunks per stored segment for write economy, with completion/error mapped onto stream settlement and retention keyed off the stream row'supdated_at(sweeps no longer scan the chunk table). Existingcf_ai_chat_stream_*tables migrate wholesale — including an in-flight stream — on first construction after upgrade, then are dropped.AIChatAgentandThinkexpose the backing capability asreadonly streams, so anystreams.read()consumer on the same Durable Object can observe chat streams. The chat wire protocol, replay handshake, and recovery behavior are unchanged.#2190
58c586aThanks @mattzcarey! - Make the alarm memory-limit circuit breaker (#1825) a self-containedLifecycle concern instead of an Agent-mediated one.
Recovery-loop membership is now a property of the job row
(
LifecycleJobPushOptions.recoveryLoop): flagged jobs are backed off bythe breaker on a strike and purged when it seals at the strike budget,
without disturbing unrelated rows — a recovery schedule can no longer
silently escape the breaker. The public
ScheduleOptionsvocabulary isunchanged: schedules only shape future work, and chat recovery reaches the
flag through internal scaffolding (
RecoveryLoopScheduleOptions) that willbe deleted when recovery migrates onto the Tasks capability, where
OOM-prone loops belong.
Capabilities can react to a strike through the new optional
onMemoryLimithook, hosts through
onAlarmMemoryLimit, and the context identifies the jobthat was executing when one exists. The strike budget is real Lifecycle
configuration (
Lifecycle.install(host, { maxAlarmMemoryLimitStrikes }))rather than a composition-root side channel. Until chat recovery moves to
Tasks, a sealed routed recovery schedule also forwards the seal to its owning
dynamic agent so a chat child under a plain Agent root persists its exhausted
incident and terminal notification.
Removed accordingly:
Agent.onAlarmMemoryLimit's policy relay, the_cf_recoveryAlarmCallbackstemplate hook,Scheduler.applyMemoryLimitPolicy,and
setLifecycleAlarmMemoryLimitStrikes.AIChatAgentandThinkflag theirrecovery schedules via
chatRecoverySchedulePolicyand seal in-flightincidents from their own protected
onAlarmMemoryLimithooks; both nowrequire
agents >= 0.23.0(they consume newagents/chatexports and nolonger implement the old template-method breaker hooks). Agent retains a
sealed-only call to
_cf_sealMemoryLimitedRecoveryso already-published chatpackages whose peer ranges accept agents 0.23 keep terminal notifications;
that fallback carries no callback-name or queue policy.
@cloudflare/ai-chat@0.11.1
Patch Changes
#2173
71ce28aThanks @mattzcarey! - Replatform chat's resumable streams onto theagents/streamscapability.ResumableStreamis now a thin adapter overStreams: chat's in-flight turn output lives in the shared durable chunk log (cf_agents_streams/cf_agents_stream_chunks), packed ~10 wire chunks per stored segment for write economy, with completion/error mapped onto stream settlement and retention keyed off the stream row'supdated_at(sweeps no longer scan the chunk table). Existingcf_ai_chat_stream_*tables migrate wholesale — including an in-flight stream — on first construction after upgrade, then are dropped.AIChatAgentandThinkexpose the backing capability asreadonly streams, so anystreams.read()consumer on the same Durable Object can observe chat streams. The chat wire protocol, replay handshake, and recovery behavior are unchanged.#2190
58c586aThanks @mattzcarey! - Make the alarm memory-limit circuit breaker (#1825) a self-containedLifecycle concern instead of an Agent-mediated one.
Recovery-loop membership is now a property of the job row
(
LifecycleJobPushOptions.recoveryLoop): flagged jobs are backed off bythe breaker on a strike and purged when it seals at the strike budget,
without disturbing unrelated rows — a recovery schedule can no longer
silently escape the breaker. The public
ScheduleOptionsvocabulary isunchanged: schedules only shape future work, and chat recovery reaches the
flag through internal scaffolding (
RecoveryLoopScheduleOptions) that willbe deleted when recovery migrates onto the Tasks capability, where
OOM-prone loops belong.
Capabilities can react to a strike through the new optional
onMemoryLimithook, hosts through
onAlarmMemoryLimit, and the context identifies the jobthat was executing when one exists. The strike budget is real Lifecycle
configuration (
Lifecycle.install(host, { maxAlarmMemoryLimitStrikes }))rather than a composition-root side channel. Until chat recovery moves to
Tasks, a sealed routed recovery schedule also forwards the seal to its owning
dynamic agent so a chat child under a plain Agent root persists its exhausted
incident and terminal notification.
Removed accordingly:
Agent.onAlarmMemoryLimit's policy relay, the_cf_recoveryAlarmCallbackstemplate hook,Scheduler.applyMemoryLimitPolicy,and
setLifecycleAlarmMemoryLimitStrikes.AIChatAgentandThinkflag theirrecovery schedules via
chatRecoverySchedulePolicyand seal in-flightincidents from their own protected
onAlarmMemoryLimithooks; both nowrequire
agents >= 0.23.0(they consume newagents/chatexports and nolonger implement the old template-method breaker hooks). Agent retains a
sealed-only call to
_cf_sealMemoryLimitedRecoveryso already-published chatpackages whose peer ranges accept agents 0.23 keep terminal notifications;
that fallback carries no callback-name or queue policy.
@cloudflare/agent-think@0.0.8
Patch Changes
71ce28a,71ce28a,87bd594,b40bc5b,8ffb3ad,71ce28a,58c586a,b12dc0b,71ce28a]: