agents@0.24.0
NodePassProject/Anywhereagents@0.24.0Sep 18, 2026by github-actions[bot]
Upgrading?
AI Summary
A major feature update introducing the `Queue` and `State` Lifecycle capabilities, enhancing WebSockets with a new Cap'n Web transport, and restructuring how lifecycle capabilities claim traffic.
Key Highlights
- New `Queue` Lifecycle capability for durable background work.
- WebSockets gains Cap'n Web transport for native calls and memory persistence.
- Agent state moved into a dedicated `State` Lifecycle capability.
- Lifecycle capabilities declare traffic claims with `claims` strategy.
- Experimental `?__agents_rpc=capnweb` endpoint removed.
Breaking Changes
- Experimental `?__agents_rpc=capnweb` endpoint removed.
- `LifecycleServices.starting()` replaced by `LifecycleServices.status()`.
- `QueueItem.created_at` renamed to `createdAt`.
- `cf_agents_queues` table and in-isolate drain removed (legacy migration).
- `cf_think_workflow_notifications` table removed (legacy migration).
New Features
- Native Cap'n Web transport for WebSockets.
- New `Queue` capability for job management.
- New `State` capability for validated state persistence.
- WebSockets capability now owns connection state and flags.
Full Release Notes
### Minor Changes
- [#2249](https://github.com/cloudflare/agents/pull/2249) [`ebab868`](https://github.com/cloudflare/agents/commit/ebab8684d162c617f1f6d5253c152b498fbfcc18) Thanks [@mattzcarey](https://github.com/mattzcarey)! - `WebSockets` speaks the Agent protocol for plain hosts, and gains a Cap'n Web transport.
- A plain Durable Object composed with `WebSockets` now works with `useAgent` and `AgentClient`: the capability sends the identity frame on connect (`protocol: false` leaves the connect sequence to the host) and serves `callables` — an `RpcTarget` whose prototype methods are the host's remote interface.
- `useAgent({ transport })` and `AgentClient({ transport })` pick the wire. `"cf-websocket"` (default) is the hibernating socket; `call()`/`stub` send JSON `rpc` frames. `"capnweb"` runs one Cap'n Web session that carries protocol frames and serves `callables` natively: `call()`/`stub` invoke them directly, an `RpcTarget` result is a live stub, a `ReadableStream` streams, calls pipeline. The Durable Object stays in memory while a capnweb connection is open. PartySocket still owns reconnection on both.
- `@callable()` decorators remain the JSON-wire interface of `Agent`; an Agent wanting native calls passes `callables`.
- The experimental `?__agents_rpc=capnweb` endpoint from 0.23.0 is removed.
- `LifecycleServices` exposes `name` and `className`.
- [#2245](https://github.com/cloudflare/agents/pull/2245) [`beff78a`](https://github.com/cloudflare/agents/commit/beff78a8f7dbe6c6de303ed32b709876adba94f1) Thanks [@mattzcarey](https://github.com/mattzcarey)! - Add the `Queue` Lifecycle capability (`agents/queue`) for durable background work. Each pushed item is a job in the Lifecycle job queue, due immediately, run from the alarm loop one at a time in push order with Lifecycle's retry, deadman, and memory-limit policy. Callbacks are registered in the constructor and typed at declaration and push; `push()` accepts a stable `id` (upsert) and per-item `retry`.
`Agent.queue()` and friends now delegate to the capability. Queued callbacks run from the alarm loop in a fresh invocation, so they no longer see the enqueuing request's `connection` or `request` through `getCurrentAgent()` (the agent itself is still available). The `cf_agents_queues` table and the in-isolate drain are gone; legacy rows migrate into the job queue on the next start. `queue()` accepts `options.id`; `dequeue`, `dequeueAll`, `dequeueAllByCallback`, `getQueue`, and `getQueues` are now asynchronous; and `QueueItem.created_at` is renamed `createdAt`. `LifecycleServices.starting()` is replaced by `status()`, which returns `"zero" | "starting" | "started"`.
Think's workflow-notification outbox and submission drain now run as queue items; the `cf_think_workflow_notifications` table migrates and is dropped on start.
Both one-shot migrations (`cf_agents_queues` in Queue, `cf_think_workflow_notifications` in Think) are temporary upgrade paths and will be removed in the next minor release, by which point every started object has migrated. Deployments skipping this release should upgrade through it. Workflow-notification delivery retries with backoff capped at ten minutes (previously five) and gives up after twelve hours of continuous failure (previously never), reporting the failure through `onError`.
- [#2179](https://github.com/cloudflare/agents/pull/2179) [`43a58a1`](https://github.com/cloudflare/agents/commit/43a58a1014fbe6f1fe3a1fcc38ad08d53bb5b112) Thanks [@AntoniTok](https://github.com/AntoniTok)! - Move agent state into the opt-in `State` Lifecycle capability.
State was one method doing four jobs inside `Agent` — validate,
persist, broadcast, notify. It moves wholesale into a `State`
capability that owns storage and change ordering, so any Lifecycle
host gets durable, validated state without inheriting `Agent`:
```ts
new State({
initialState: { count: 0 },
validateStateChange: (next, source) => validate(next, source),
onChanged: (state, source) => notify(state, source),
});
```
The capability owns the `cf_agents_state` state row, lazy load with an
in-memory cache, initial-state seeding, and validated persistence. It
runs only the `onStart` hook (versioned schema init under its own
`cf_agents:state_schema_version` key) and reaches Lifecycle only for
`storage` — no alarm, no request path. It never touches connections.
Host-owned behavior is injected, not moved: `validateStateChange`
stays an overridable `Agent` method and the post-change `onChanged`
hook is passed into `State` as a plain option. Synchronous and
asynchronous notification hooks are both supported. `Agent` keeps its
`initialState` field and seeds it from the `state` getter (standalone
hosts pass `initialState` to `State` directly). Broadcast and the
notification hook stay on `Agent`, and the `onMessage` state branch
stays too; only its inner write delegates to the capability.
`State` is the only owner of `cf_agents_state`: it creates the table,
holds the state row, and runs the legacy `cf_state_was_changed`
cleanup in its own versioned migration. `Agent` used to keep its
global schema version as a row in that table; it now lives under the
`cf_agents:schema_version` KV key like every other capability's
version. A DO created under the old layout has the row read once,
moved to the key, and deleted on its next construction.
`Agent`'s public API and wire protocol are unchanged: `state`,
`setState()`, `onStateChanged`, and the `CF_AGENT_STATE` frames behave
identically.
- [#2257](https://github.com/cloudflare/agents/pull/2257) [`46760e6`](https://github.com/cloudflare/agents/commit/46760e635ce9599add0abbfe6c1a34af0d5d44f1) Thanks [@mattzcarey](https://github.com/mattzcarey)! - The `WebSockets` capability now owns the Agent protocol's state sync and per-connection flags, for `Agent` and plain hosts alike.
- `state` takes a `State` capability. The current value is pushed to each new connection after identity, a client's `cf_agent_state` frame goes through the host's `validateStateChange` (a readonly connection is refused, a rejected change gets a generic `cf_agent_state_error`), and `broadcastState(source)` pushes a change to every protocol-enabled connection but its source — wire it to the `State`'s `onChanged`. So `useAgent().state` and `setState()` work against a plain Durable Object.
- `protocol` and `readonly` are per-connection policies decided at accept time; `sendIdentity()`, `sendState()`, `applyStateFrame()`, `isReadonly()`/`setReadonly()`, and `isProtocolEnabled()`/`setProtocolEnabled()` are the capability's surface for hosts that drive the sequence themselves. The `identity` option from the previous changeset is replaced by `protocol`.
- The readonly and no-protocol flags, their `_cf_` storage in connection state, and the wrapper that hides them from `connection.state` move out of `Agent` into `agents/websockets` (`registerInternalConnectionKeys` for a host's own keys). `Agent`'s public methods and wire behaviour are unchanged; it passes `protocol: false` and drives its connect sequence through the capability after its facet routing decision.
### Patch Changes
- [#2248](https://github.com/cloudflare/agents/pull/2248) [`c96418d`](https://github.com/cloudflare/agents/commit/c96418d5334e1c0aa1fb1614de8ae0787753b3ff) Thanks [@mattzcarey](https://github.com/mattzcarey)! - Lifecycle capabilities declare how they claim traffic with `claims: "selective" | "catch-all"` instead of hosts passing `{ fallback: true }` to `lifecycle.use()`. A catch-all always dispatches last, whenever it was installed. Catch-alls are unique per dispatch hook, so one for `onRequest` and one for `onWebSocketUpgrade` coexist while a second for the same hook is refused. `WebSockets` declares itself a catch-all for upgrades and never declines one: without `handlers` it still accepts and tracks connections; handlers only add behavior on connect, message, close and error. `LifecycleUseOptions` is removed.
- [#2244](https://github.com/cloudflare/agents/pull/2244) [`f6e556f`](https://github.com/cloudflare/agents/commit/f6e556f3c24003ae9fb9b74160b314e2be7c76b2) Thanks [@mattzcarey](https://github.com/mattzcarey)! - Preserve a legacy `runFiber` result when deleting its bookkeeping row fails. The cleanup error is logged and the row is left for the existing recovery pass instead of replacing the fiber body's outcome.
- [#2297](https://github.com/cloudflare/agents/pull/2297) [`0fd3c8b`](https://github.com/cloudflare/agents/commit/0fd3c8b9fe7e7805f18226114b925066e33904fe) Thanks [@ben-reitz](https://github.com/ben-reitz)! - Expose `ResumableStream.pendingCutoverId` so callers can detect whether a finished stream still awaits its cutover, and clear the pending-cutover marker only after settlement succeeds in `finalizePending`, keeping a failed settlement retryable instead of silently leaving the stream row live.
- [#2288](https://github.com/cloudflare/agents/pull/2288) [`9efee5f`](https://github.com/cloudflare/agents/commit/9efee5f0c23d6a217fe4ba5e73c078e64e9cd28c) Thanks [@ben-reitz](https://github.com/ben-reitz)! - Bump `valibot` to 1.4.2, picking up the fix for a moderate-severity
advisory affecting `record()` schemas and `flatten()`.
Related Repositories
A tool for creating and running Linux containers using lightweight virtual machines on a Mac. It is written in Swift, and optimized for Apple silicon.
Lightning-Fast, On-Device, Multilingual TTS — running natively via ONNX.
Fastest and only macOS Dictation app with on-device STT and custom trained AI enhancement model. Windows, iOS testflight! Android, Linux waitlist open! A local Wispr Flow alternative. Follow https://x.com/fluidvoiceapp for more
High performance distributed S3 compatible object storage focused on speed and designed to be an open alternative to MinIO and RustFS.
73 MB open source ScreenStudio alternative. Tiny, powerful with incredible performance. Native Swift app.