Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changeset/young-buses-replay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"agents": patch
---

Add `agents/tasks`: durable, replayable background execution as a Lifecycle capability (experimental).

One `Tasks` instance 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 typed `tasks.run(name, input, options)`, and `tasks.handle(name)` gives a typed lens scoped to one definition. A run survives process loss and deployments by replaying its handler from the top: completed `step.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, and `step.status()` progress with a replay live gate that never re-publishes old progress as new.

A definition may pair its handler with a `recover` callback (`{ run, recover }` in the map) that owns unclean interruptions instead of automatic replay: it receives the run input and the interrupted step — including its stable idempotency key and the last `checkpoint()` the lost attempt wrote — and decides `replay` (now or later), `complete`, `fail`, or `cancel`, with a bounded backoff budget when recovery itself throws. Clean step failures never invoke recovery; the retry policy owns them.

`Agent` installs the capability automatically as experimental `this.tasks`, with subclass definitions declared on the overridable `taskDefinitions` field 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 with checkpoint-backed `stash()`, and unclean interruptions route through the unchanged ChatRecoveryEngine (and messenger recovery) as the definitions' `recover` callbacks. The legacy `runFiber()`/`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 follows the Lifecycle alarm-contribution model: it stores run deadlines in its own tables, contributes the earliest through `getNextAlarm()`, and never touches the physical alarm, so it composes with the Scheduler and other capabilities on one shared alarm. Design record: `design/rfc-fibers.md` (shipped under the name Tasks).
2 changes: 2 additions & 0 deletions design/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ Keep it concise. A few paragraphs is fine. These are records, not essays.
| `mcp.md` | design doc | Stateless, Legacy compatibility, Legacy sessionful, client, package boundary, and conformance architecture |
| `durable-object-lifecycle.md` | design doc | Lifecycle Objects, capability and host phases, Scheduler/alarm ownership, host context, identity, and always-hibernating WebSockets |
| `rfc-durable-object-lifecycle.md` | RFC | Constructor-composed Durable Object lifecycle with reusable components and always-hibernating WebSockets |
| `rfc-fibers.md` | RFC | Tasks (née Fibers) — durable replayable execution as one Lifecycle capability: named definitions, journaled steps, sleeps, optional recovery callback (accepted, amended) |
| `rfc-streams.md` | RFC | Streams — durable incremental output as a Lifecycle capability: chunk log, cursor, replay-then-tail reads, composed with Tasks via checkpointed cursors (proposed) |

## Relationship to `/docs`

Expand Down
4 changes: 4 additions & 0 deletions design/alarm-coordination.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ resource so capabilities do not overwrite one another's wake-ups.
capability keeps its own schema, retry policy, and recovery semantics.
- **Scheduler** — the capability for persistent named callbacks. It is one alarm
contributor, not the general alarm service.
- **Tasks** — the capability for durable replayable execution. Every
non-terminal run carries an authoritative `next_at` deadline (acceptance,
sleeps, retries, and claim backstops all write it), and the capability
contributes the minimum as one more ordinary contribution.
- **Host contribution** — temporary support for host work that has not yet been
extracted into a capability.

Expand Down
9 changes: 6 additions & 3 deletions design/durable-object-lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ recalculation, chooses the earliest contribution, runs every capability's
`onAlarm()` followed by the host's `onAlarm()`, then recalculates once more.

Capabilities own their durable work. Scheduler stores named callback rows in
its table; a future Fiber capability can store resumable jobs in its own table;
an MCP capability can store reconnect state in its own table. They coordinate
only through Lifecycle's alarm contract and do not depend on Scheduler.
its table; the Tasks capability stores replayable runs and step journals in
its own tables and contributes its earliest run deadline the same way
([rfc-fibers.md](./rfc-fibers.md)); an MCP capability can store reconnect
state in its own table. They coordinate only through Lifecycle's alarm
contract and do not depend on Scheduler.

A host can also implement `getNextAlarm()` for work not yet extracted into a
capability. Exclusive contributions replace ordinary wake-time candidates,
Expand Down Expand Up @@ -177,3 +179,4 @@ a migration fallback. It never writes a duplicate name.

- [Alarm coordination](./alarm-coordination.md)
- [Durable Object lifecycle composition](./rfc-durable-object-lifecycle.md)
- [Tasks (née Fibers): durable replayable execution as a Lifecycle capability](./rfc-fibers.md)
Loading
Loading