agents 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -1
- package/dist/{agent-routing-CnkaHb-v.d.ts → agent-routing-DE5zmCQ8.d.ts} +1038 -394
- package/dist/agent-routing.d.ts +1 -1
- package/dist/agent-tool-types.d.ts +26 -26
- package/dist/{agent-tools-C0XJqWzB.d.ts → agent-tools-DtXMTDGM.d.ts} +7 -7
- package/dist/agent-tools.d.ts +21 -21
- package/dist/browser/ai.js +4 -1
- package/dist/browser/ai.js.map +1 -1
- package/dist/browser/tanstack-ai.js.map +1 -1
- package/dist/callable-decorator-DP__HhBA.d.ts +72 -0
- package/dist/callable-decorator.d.ts +18 -0
- package/dist/callable-decorator.js +71 -0
- package/dist/callable-decorator.js.map +1 -0
- package/dist/capability-BjSKYpzg.js.map +1 -1
- package/dist/capability-runner-Be_-PLR1.d.ts +459 -0
- package/dist/channel-Bnm4S7T2.d.ts +491 -0
- package/dist/channels/ai-sdk.d.ts +46 -0
- package/dist/channels/ai-sdk.js +120 -0
- package/dist/channels/ai-sdk.js.map +1 -0
- package/dist/channels/email.d.ts +95 -0
- package/dist/channels/email.js +323 -0
- package/dist/channels/email.js.map +1 -0
- package/dist/channels/index.d.ts +233 -0
- package/dist/channels/index.js +608 -0
- package/dist/channels/index.js.map +1 -0
- package/dist/channels/slack.d.ts +140 -0
- package/dist/channels/slack.js +614 -0
- package/dist/channels/slack.js.map +1 -0
- package/dist/channels/tanstack-ai.d.ts +39 -0
- package/dist/channels/tanstack-ai.js +17 -0
- package/dist/channels/tanstack-ai.js.map +1 -0
- package/dist/channels/telegram.d.ts +106 -0
- package/dist/channels/telegram.js +427 -0
- package/dist/channels/telegram.js.map +1 -0
- package/dist/channels/voice.d.ts +45 -0
- package/dist/channels/voice.js +122 -0
- package/dist/channels/voice.js.map +1 -0
- package/dist/chat/index.d.ts +2325 -2019
- package/dist/chat/index.js +888 -518
- package/dist/chat/index.js.map +1 -1
- package/dist/chat-sdk/index.d.ts +7 -7
- package/dist/chat-sdk/index.js +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/context/index.d.ts +216 -0
- package/dist/context/index.js +454 -0
- package/dist/context/index.js.map +1 -0
- package/dist/{current-agent-CuMErtly.d.ts → current-agent-Da_C9a3b.d.ts} +90 -107
- package/dist/current-agent-DhoDkSnH.js.map +1 -1
- package/dist/{diagnostics-CaBjfz4J.js → diagnostics-BzvaX2UT.js} +5 -1
- package/dist/diagnostics-BzvaX2UT.js.map +1 -0
- package/dist/diagnostics-C4jcz3VK.js +360 -0
- package/dist/diagnostics-C4jcz3VK.js.map +1 -0
- package/dist/index-BB0kqhIz.d.ts +101 -0
- package/dist/index-BVVgDSdq.d.ts +1 -0
- package/dist/index-XDkuQ7zm.d.ts +89 -0
- package/dist/{index-DcSAZKsB.d.ts → index-YSKgfgg9.d.ts} +3 -1
- package/dist/index.d.ts +91 -82
- package/dist/index.js +3 -2
- package/dist/ingress-BfetZbMO.js +83 -0
- package/dist/ingress-BfetZbMO.js.map +1 -0
- package/dist/internal-CYlgHl1l.js +59 -0
- package/dist/internal-CYlgHl1l.js.map +1 -0
- package/dist/internal_context.d.ts +1 -1
- package/dist/lifecycle/index.d.ts +35 -17
- package/dist/lifecycle/index.js +1 -1
- package/dist/lifecycle-CMRGjZdw.js +1299 -0
- package/dist/lifecycle-CMRGjZdw.js.map +1 -0
- package/dist/mcp/client/index.d.ts +20 -20
- package/dist/mcp/index.d.ts +35 -35
- package/dist/mcp/index.js +1 -1
- package/dist/observability/index.d.ts +1 -1
- package/dist/observability/index.js +1 -1
- package/dist/react.d.ts +4 -4
- package/dist/{retries-CAvxtG9d.d.ts → retries-D9Ds-1lz.d.ts} +17 -6
- package/dist/retries.d.ts +8 -6
- package/dist/retries.js +13 -1
- package/dist/retries.js.map +1 -1
- package/dist/routing/index.d.ts +137 -0
- package/dist/routing/index.js +244 -0
- package/dist/routing/index.js.map +1 -0
- package/dist/sanitize-D9TujEK8.js +79 -0
- package/dist/sanitize-D9TujEK8.js.map +1 -0
- package/dist/scheduler-DD9NdYbF.js +665 -0
- package/dist/scheduler-DD9NdYbF.js.map +1 -0
- package/dist/{scheduler-DQoTGoAW.d.ts → scheduler-Dwh85ZGl.d.ts} +21 -22
- package/dist/schedules/index.d.ts +1 -1
- package/dist/schedules/index.js +1 -1
- package/dist/sentence-chunker-BAidJ4DA.d.ts +68 -0
- package/dist/serializable.d.ts +1 -1
- package/dist/sessions/index.d.ts +441 -0
- package/dist/sessions/index.js +2063 -0
- package/dist/sessions/index.js.map +1 -0
- package/dist/skills/index.d.ts +99 -0
- package/dist/skills/index.js +254 -5
- package/dist/skills/index.js.map +1 -1
- package/dist/{src-5W6JNKVb.js → src-DlSHshb2.js} +1460 -1110
- package/dist/src-DlSHshb2.js.map +1 -0
- package/dist/streams/index.d.ts +120 -0
- package/dist/streams/index.js +107 -0
- package/dist/streams/index.js.map +1 -0
- package/dist/streams-D6tJ0NN9.d.ts +370 -0
- package/dist/streams-DZKgAj9b.js +709 -0
- package/dist/streams-DZKgAj9b.js.map +1 -0
- package/dist/sub-routing.d.ts +12 -12
- package/dist/surface-bZZJqBka.js +17 -0
- package/dist/surface-bZZJqBka.js.map +1 -0
- package/dist/tasks/index.d.ts +64 -0
- package/dist/tasks/index.js +2 -0
- package/dist/tasks-BRJ5zgya.d.ts +517 -0
- package/dist/tasks-ylZgBjhj.js +1656 -0
- package/dist/tasks-ylZgBjhj.js.map +1 -0
- package/dist/text-segment-joiner-BtAFQSA_.js +57 -0
- package/dist/text-segment-joiner-BtAFQSA_.js.map +1 -0
- package/dist/text-stream-CpdiKrJB.js +272 -0
- package/dist/text-stream-CpdiKrJB.js.map +1 -0
- package/dist/tokens-nHAKcN6M.js +52 -0
- package/dist/tokens-nHAKcN6M.js.map +1 -0
- package/dist/tool-schema-CBjGPrsQ.js +31 -0
- package/dist/tool-schema-CBjGPrsQ.js.map +1 -0
- package/dist/types-B7LojTe4.d.ts +202 -0
- package/dist/types-_Faxb570.d.ts +439 -0
- package/dist/voice/client.d.ts +226 -0
- package/dist/voice/client.js +932 -0
- package/dist/voice/client.js.map +1 -0
- package/dist/voice/errors.d.ts +43 -0
- package/dist/voice/errors.js +41 -0
- package/dist/voice/errors.js.map +1 -0
- package/dist/voice/index.d.ts +271 -0
- package/dist/voice/index.js +1812 -0
- package/dist/voice/index.js.map +1 -0
- package/dist/voice/react.d.ts +167 -0
- package/dist/voice/react.js +234 -0
- package/dist/voice/react.js.map +1 -0
- package/dist/voice/sfu.d.ts +71 -0
- package/dist/voice/sfu.js +157 -0
- package/dist/voice/sfu.js.map +1 -0
- package/dist/voice/text.d.ts +6 -0
- package/dist/voice/text.js +2 -0
- package/dist/voice/types.d.ts +58 -0
- package/dist/voice/types.js +18 -0
- package/dist/voice/types.js.map +1 -0
- package/dist/voice/workers-ai.d.ts +136 -0
- package/dist/voice/workers-ai.js +568 -0
- package/dist/voice/workers-ai.js.map +1 -0
- package/dist/websockets/index.d.ts +192 -0
- package/dist/websockets/index.js +2 -0
- package/dist/websockets-DUfRHPRq.js +502 -0
- package/dist/websockets-DUfRHPRq.js.map +1 -0
- package/dist/workflow-types.d.ts +25 -25
- package/dist/workflows.d.ts +21 -21
- package/dist/workflows.js +1 -1
- package/docs/agent-class.md +2 -2
- package/docs/agent-tools.md +2 -1
- package/docs/channels.md +323 -0
- package/docs/chat-agents.md +6 -13
- package/docs/context.md +131 -0
- package/docs/index.md +15 -12
- package/docs/lifecycle.md +102 -55
- package/docs/long-running-agents.md +2 -2
- package/docs/mcp-servers.md +5 -1
- package/docs/resumable-streaming.md +1 -1
- package/docs/routing.md +105 -0
- package/docs/sessions.md +237 -871
- package/docs/streams.md +213 -0
- package/docs/sub-agents.md +184 -124
- package/docs/tasks.md +246 -0
- package/docs/voice.md +745 -0
- package/package.json +115 -13
- package/dist/capability-runner-CvHGZqUu.d.ts +0 -150
- package/dist/compaction-helpers-iiKMr2TQ.js +0 -340
- package/dist/compaction-helpers-iiKMr2TQ.js.map +0 -1
- package/dist/compaction-helpers-wUz6M3us.d.ts +0 -621
- package/dist/diagnostics-CaBjfz4J.js.map +0 -1
- package/dist/durable-object-lifecycle-D6nNQJJd.js +0 -862
- package/dist/durable-object-lifecycle-D6nNQJJd.js.map +0 -1
- package/dist/experimental/memory/session/index.d.ts +0 -671
- package/dist/experimental/memory/session/index.js +0 -2379
- package/dist/experimental/memory/session/index.js.map +0 -1
- package/dist/experimental/memory/utils/index.d.ts +0 -96
- package/dist/experimental/memory/utils/index.js +0 -79
- package/dist/experimental/memory/utils/index.js.map +0 -1
- package/dist/scheduler-CR9RHGos.js +0 -857
- package/dist/scheduler-CR9RHGos.js.map +0 -1
- package/dist/src-5W6JNKVb.js.map +0 -1
- package/dist/tool-output-truncation-CNnnGZQ3.js +0 -98
- package/dist/tool-output-truncation-CNnnGZQ3.js.map +0 -1
package/docs/lifecycle.md
CHANGED
|
@@ -26,7 +26,7 @@ export class MyObject extends DurableObject<Env> {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
onAlarm(): void {
|
|
29
|
-
// Runs after
|
|
29
|
+
// Runs once per alarm invocation, after due jobs are driven.
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
```
|
|
@@ -65,24 +65,34 @@ export default {
|
|
|
65
65
|
The default URL shape is `/agents/:binding/:name`. Direct
|
|
66
66
|
`env.MY_OBJECT.getByName(name).fetch(request)` calls work as well.
|
|
67
67
|
|
|
68
|
-
`Agent` already constructs this lifecycle
|
|
69
|
-
|
|
70
|
-
`onError`
|
|
68
|
+
`Agent` already constructs this lifecycle (and installs the `WebSockets`
|
|
69
|
+
capability for its connections). Existing Agent classes continue to override
|
|
70
|
+
`onStart`, `onRequest`, `onConnect`, `onMessage`, `onClose`, and `onError`
|
|
71
|
+
normally.
|
|
71
72
|
|
|
72
73
|
## Request call path
|
|
73
74
|
|
|
75
|
+
The lifecycle-installed `fetch` is the request handler. It offloads each
|
|
76
|
+
request to the installed capabilities, which act as middleware: the first
|
|
77
|
+
capability registered that matches the request handles it by returning a
|
|
78
|
+
`Response`. A capability that returns `undefined` passes the request on to
|
|
79
|
+
the next capability, and a request no capability claims falls through to the
|
|
80
|
+
host's `onRequest`.
|
|
81
|
+
|
|
74
82
|
```text
|
|
75
83
|
routeAgentRequest(request)
|
|
76
84
|
└─ named Durable Object stub.fetch(request)
|
|
77
85
|
└─ lifecycle-installed fetch
|
|
78
86
|
├─ lifecycle startup capabilities
|
|
79
87
|
├─ host onStart
|
|
80
|
-
├─
|
|
81
|
-
│ └─ first Response
|
|
88
|
+
├─ capability middleware, in registration order
|
|
89
|
+
│ └─ first Response handles the request
|
|
82
90
|
└─ host onRequest
|
|
83
91
|
```
|
|
84
92
|
|
|
85
|
-
A warm object skips startup but still offers every request to its
|
|
93
|
+
A warm object skips startup but still offers every request to its middleware.
|
|
94
|
+
There is no `next()` today: a capability either handles a request or declines
|
|
95
|
+
it, and cannot wrap or observe a downstream response.
|
|
86
96
|
|
|
87
97
|
## Reusable capabilities
|
|
88
98
|
|
|
@@ -107,10 +117,6 @@ class AuditLog implements DurableObjectCapability {
|
|
|
107
117
|
return new Response("ok");
|
|
108
118
|
}
|
|
109
119
|
}
|
|
110
|
-
|
|
111
|
-
onAlarm(): void {
|
|
112
|
-
this.storage.sql.exec("DELETE FROM audit_log");
|
|
113
|
-
}
|
|
114
120
|
}
|
|
115
121
|
```
|
|
116
122
|
|
|
@@ -127,12 +133,19 @@ export class MyObject extends DurableObject<Env> {
|
|
|
127
133
|
}
|
|
128
134
|
```
|
|
129
135
|
|
|
130
|
-
Capabilities run in registration order. Startup
|
|
131
|
-
sequentially. Request handling stops at the first
|
|
136
|
+
Capabilities run in registration order. Startup runs every hook
|
|
137
|
+
sequentially. Request handling is middleware dispatch: it stops at the first
|
|
138
|
+
returned `Response`, and returning `undefined` passes the request on. A phase
|
|
132
139
|
failure propagates, and failed startup can be retried.
|
|
133
140
|
|
|
141
|
+
A capability installed with `{ fallback: true }` dispatches after every
|
|
142
|
+
non-fallback capability, whenever it was installed. This is for a host's
|
|
143
|
+
catch-all: `Agent` installs its WebSockets capability as a fallback, so a
|
|
144
|
+
subclass that installs request or upgrade middleware from its own
|
|
145
|
+
constructor still runs first, even though `Agent`'s constructor ran earlier.
|
|
146
|
+
|
|
134
147
|
Capabilities extending `LifecycleCapability` receive one standard service
|
|
135
|
-
surface: storage, readiness, startup state,
|
|
148
|
+
surface: storage, readiness, startup state, the job queue, a host
|
|
136
149
|
invocation boundary, best-effort events, and capability routing.
|
|
137
150
|
Host-specific bindings, authentication, and protocol adapters remain explicit
|
|
138
151
|
constructor dependencies. Lifecycle never grants a capability the complete
|
|
@@ -143,46 +156,59 @@ Capability hooks run outside host context, but user callbacks run through
|
|
|
143
156
|
Scheduler dispatches its registered callbacks through this boundary, and a
|
|
144
157
|
future capability that calls user code should do the same.
|
|
145
158
|
|
|
146
|
-
##
|
|
159
|
+
## The job queue
|
|
147
160
|
|
|
148
|
-
Lifecycle owns the Durable Object's
|
|
149
|
-
|
|
150
|
-
|
|
161
|
+
Lifecycle owns the Durable Object's queue of durable work and its single
|
|
162
|
+
physical alarm. A job is a serialisable callback address — the owning
|
|
163
|
+
capability plus a function name — with a due time and a payload. A capability
|
|
164
|
+
that needs future work pushes a job and implements `onJob()`:
|
|
151
165
|
|
|
152
166
|
```ts
|
|
153
|
-
import {
|
|
167
|
+
import {
|
|
168
|
+
LifecycleCapability,
|
|
169
|
+
type LifecycleJobContext
|
|
170
|
+
} from "agents/lifecycle";
|
|
154
171
|
|
|
155
172
|
class Cleanup extends LifecycleCapability {
|
|
156
173
|
constructor() {
|
|
157
174
|
super("cleanup");
|
|
158
175
|
}
|
|
159
176
|
|
|
160
|
-
async
|
|
161
|
-
|
|
177
|
+
async scheduleCleanup(time: number): Promise<void> {
|
|
178
|
+
await this.lifecycle.jobs.push({ id: "cleanup", fn: "sweep", time });
|
|
162
179
|
}
|
|
163
180
|
|
|
164
|
-
async
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
await this.lifecycle.storage.delete("cleanup:next");
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
async scheduleCleanup(time: number): Promise<void> {
|
|
171
|
-
await this.lifecycle.storage.put("cleanup:next", time);
|
|
172
|
-
await this.lifecycle.alarms.rearm();
|
|
181
|
+
async onJob({ job }: LifecycleJobContext): Promise<void> {
|
|
182
|
+
// job.fn === "sweep"; returning nothing completes the job.
|
|
183
|
+
await this.lifecycle.storage.delete("cleanup:marker");
|
|
173
184
|
}
|
|
174
185
|
}
|
|
175
186
|
```
|
|
176
187
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
188
|
+
The queue is ordered by timestamp, and every queue mutation re-arms the
|
|
189
|
+
physical alarm automatically — there is no explicit rearm call. When the
|
|
190
|
+
alarm fires, Lifecycle drives due jobs in due order as an event loop, then
|
|
191
|
+
runs host `onAlarm()`, then re-arms from queue state. Before driving any job
|
|
192
|
+
it arms a deadman pre-alarm so an isolate death mid-drive still wakes the
|
|
193
|
+
object to resume.
|
|
194
|
+
|
|
195
|
+
A job's drive result decides what happens next: returning nothing completes
|
|
196
|
+
and deletes it, `{ rescheduleAt }` suspends it until a future time, and
|
|
197
|
+
`"yield"` leaves it due so the object wakes again immediately. Lifecycle also
|
|
198
|
+
owns dispatch retries: a job's `retry` options bound in-process attempts,
|
|
199
|
+
platform-class failures (a superseded isolate after a deploy, a memory-limit
|
|
200
|
+
reset) preserve the job for a fresh invocation, and a terminal application
|
|
201
|
+
failure reaches the owner's `onJobError()`, whose result decides advancement.
|
|
202
|
+
|
|
203
|
+
A job pushed with `exclusive: true` suppresses ordinary alarm candidates
|
|
204
|
+
while it is pending — Agent's deferred destroy uses this so a condemned
|
|
205
|
+
object cannot be kept alive by other work. A `singleflight` job is skipped
|
|
206
|
+
while a previous run is still in flight, until it crosses its hung timeout.
|
|
207
|
+
The host pushes jobs through `lifecycle.jobs` and implements the same
|
|
208
|
+
`onJob()` hook (a host job's terminal failure completes it; the host
|
|
209
|
+
re-derives its jobs from durable state). Capabilities do not depend on
|
|
210
|
+
Scheduler or
|
|
211
|
+
on each other merely to receive wakes.
|
|
186
212
|
|
|
187
213
|
## Capability events
|
|
188
214
|
|
|
@@ -217,7 +243,7 @@ dispatch. A host with child objects supplies the transport internally.
|
|
|
217
243
|
|
|
218
244
|
Agent uses this for facet schedules: Scheduler sends owner-scoped CRUD to the
|
|
219
245
|
root Scheduler and routes due callbacks back to the matching facet Scheduler.
|
|
220
|
-
|
|
246
|
+
Facet schedules live as jobs in the root's queue. Scheduler does not
|
|
221
247
|
implement facet traversal, and Agent exposes only one internal generic Lifecycle
|
|
222
248
|
route aperture.
|
|
223
249
|
|
|
@@ -267,33 +293,54 @@ Host context values follow the invocation:
|
|
|
267
293
|
|
|
268
294
|
- `onStart` and `onAlarm`: object;
|
|
269
295
|
- `onRequest`: object and request;
|
|
270
|
-
- `onConnect`: object, connection, and
|
|
271
|
-
|
|
296
|
+
- `WebSockets` capability handlers `onConnect`: object, connection, and
|
|
297
|
+
upgrade request;
|
|
298
|
+
- `WebSockets` capability handlers `onMessage`, `onClose`, and `onError`:
|
|
299
|
+
object and connection.
|
|
272
300
|
|
|
273
301
|
`getConnectionTags(connection, { request })` remains argument-driven because it
|
|
274
302
|
already receives both values explicitly. The root `agents` package continues
|
|
275
303
|
to export `getCurrentAgent()` for the `Agent` class as a compatibility alias.
|
|
276
304
|
|
|
277
|
-
## WebSockets
|
|
278
|
-
|
|
279
|
-
The lifecycle always uses Cloudflare's WebSocket Hibernation API. Idle clients
|
|
280
|
-
remain connected while the Durable Object can leave memory. When a message
|
|
281
|
-
wakes the object, its constructor and lifecycle startup run again before
|
|
282
|
-
`onMessage`.
|
|
305
|
+
## WebSockets are an opt-in capability
|
|
283
306
|
|
|
284
|
-
|
|
307
|
+
Lifecycle itself does not model WebSockets. Hosts that want connections
|
|
308
|
+
install the `WebSockets` capability, which owns the subsystem end to end —
|
|
309
|
+
it claims upgrades, accepts hibernating sockets, dispatches handlers inside
|
|
310
|
+
the host invocation boundary, and answers `getConnections()`:
|
|
285
311
|
|
|
286
312
|
```ts
|
|
287
|
-
|
|
288
|
-
connection.setState({ authenticated: true });
|
|
289
|
-
}
|
|
313
|
+
import { WebSockets } from "agents/websockets";
|
|
290
314
|
|
|
291
|
-
|
|
292
|
-
|
|
315
|
+
export class MyObject extends DurableObject<Env> {
|
|
316
|
+
readonly webSockets = new WebSockets({
|
|
317
|
+
handlers: {
|
|
318
|
+
onConnect: (connection) => {
|
|
319
|
+
connection.setState({ authenticated: true });
|
|
320
|
+
},
|
|
321
|
+
onMessage: (connection, message) => {
|
|
322
|
+
connection.send(`echo:${message}`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
readonly lifecycle = Lifecycle.install(this).use(this.webSockets);
|
|
293
327
|
}
|
|
294
328
|
```
|
|
295
329
|
|
|
296
|
-
|
|
330
|
+
Without the capability installed, WebSocket upgrades are declined.
|
|
331
|
+
|
|
332
|
+
The capability can also serve remote methods: pass an `RpcTarget` as
|
|
333
|
+
`callables` and its prototype methods become the complete remote interface,
|
|
334
|
+
served over a Cap'n Web session (`?__agents_rpc=capnweb`). An `Agent` adds
|
|
335
|
+
no new surface for this — its `@callable()`-decorated methods are its
|
|
336
|
+
interface, served on every wire: natively over the legacy JSON RPC protocol
|
|
337
|
+
and, through the decorator-derived target, over the Cap'n Web endpoint.
|
|
338
|
+
|
|
339
|
+
Connections use Cloudflare's WebSocket Hibernation API. Idle clients remain
|
|
340
|
+
connected while the Durable Object can leave memory; when a message wakes the
|
|
341
|
+
object, its constructor and lifecycle startup run again before `onMessage`.
|
|
342
|
+
State needed after a wake must be stored durably or through
|
|
343
|
+
`connection.setState()`. There is no non-hibernating mode.
|
|
297
344
|
|
|
298
345
|
## Native RPC
|
|
299
346
|
|
|
@@ -505,7 +505,7 @@ This pattern has several advantages for long-running agents:
|
|
|
505
505
|
|
|
506
506
|
## Delegating to sub-agents
|
|
507
507
|
|
|
508
|
-
A project manager does not do everything itself. It delegates specialized work to sub-agents — child Durable Objects (facets) spawned under the parent. Each facet has its own isolated SQLite state and runs in parallel, but stays colocated on the same machine as the parent.
|
|
508
|
+
A project manager does not do everything itself. It delegates specialized work to sub-agents — child Durable Objects (facets) spawned under the parent. Each facet has its own isolated SQLite state and runs in parallel, but stays colocated on the same machine as the parent. This per-run, parent-supervised delegation is the use case facets are for; see [When to use dynamic agents](./sub-agents.md#when-to-use-dynamic-agents) before reaching for one to model long-lived independent peers.
|
|
509
509
|
|
|
510
510
|
```typescript
|
|
511
511
|
export class ProjectManager extends Agent<Env, ProjectState> {
|
|
@@ -623,7 +623,7 @@ The [Session API](./sessions.md) addresses this directly:
|
|
|
623
623
|
|
|
624
624
|
- **Compaction** — automatically summarizes older messages when the estimated token count exceeds a threshold. The summary replaces the middle of the conversation as a non-destructive overlay. Original messages remain in SQLite for audit.
|
|
625
625
|
- **Context blocks** — persistent structured sections injected into the system prompt (identity, memory, learned facts). The agent or the LLM can write to these blocks, and they survive hibernation and eviction.
|
|
626
|
-
- **
|
|
626
|
+
- **Conversation isolation** — use one Durable Object per user-facing conversation and keep the conversation directory in a parent Durable Object. Sessions can still fork paths or use named handles for local drafts and namespaces.
|
|
627
627
|
|
|
628
628
|
For simpler cases: keep only the last N messages in the active context (sliding window), or selectively retain messages that contain decisions and approvals while pruning routine exchanges.
|
|
629
629
|
|
package/docs/mcp-servers.md
CHANGED
|
@@ -78,7 +78,11 @@ createMcpHandler(() => createServer(), {
|
|
|
78
78
|
});
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
`createMcpHandler` supports upstream SDK v2 handler options except `bus`, which is not exposed by the Agents SDK. Supplying `bus` throws a `TypeError`.
|
|
82
|
+
|
|
83
|
+
To publish change events, use the returned handler's `notify` methods, such as `handler.notify.toolsChanged()`. Create the handler once at module scope and reuse it for requests and notifications. Notifications are isolate-local: they do not reach subscriptions in other Worker isolates. See the [handler notification API](https://developers.cloudflare.com/agents/model-context-protocol/apis/handler-api/#publish-list-and-resource-changes) for the available methods.
|
|
84
|
+
|
|
85
|
+
Use `createLegacyMcpHandler` for SDK v1 WorkerTransport, storage, session, and event-store options. The SDK v2 `bus` option is not available on that handler either.
|
|
82
86
|
|
|
83
87
|
The handler validates every present `Origin` header before serving the request. It rejects malformed, opaque, and non-HTTP origins. Requests without `Origin` remain valid for non-browser MCP clients.
|
|
84
88
|
|
|
@@ -70,7 +70,7 @@ function Chat() {
|
|
|
70
70
|
- Chunks are batched (every 10 chunks) and flushed to SQLite for performance
|
|
71
71
|
- When a client sends `CF_AGENT_STREAM_RESUME_REQUEST`, the server checks for active streams and responds with `CF_AGENT_STREAM_RESUMING`
|
|
72
72
|
- Stale streams (older than 5 minutes) are cleaned up on restore
|
|
73
|
-
- Stream buffers are
|
|
73
|
+
- Stream buffers are deleted in the same transaction that persists the assistant message (the cutover), so a finished turn leaves nothing behind and no cleanup alarm is armed. A buffer a crash left behind is reclaimed when the next stream starts: finished streams immediately, abandoned in-flight streams after 1 hour without a chunk (recovery has until then to rebuild the message from it)
|
|
74
74
|
|
|
75
75
|
### Client-side (`useAgentChat`)
|
|
76
76
|
|
package/docs/routing.md
CHANGED
|
@@ -601,6 +601,94 @@ Each agent is accessed via its own path:
|
|
|
601
601
|
|
|
602
602
|
---
|
|
603
603
|
|
|
604
|
+
## Routing to independent Agents
|
|
605
|
+
|
|
606
|
+
A hub Agent often owns an open-ended set of independent peers: one Durable Object per chat, document, or session for a user. `RoutedAgents` from `agents/routing` codifies that topology as a Lifecycle capability. The hub keeps a durable catalog of public IDs mapped to opaque physical names, and forwards requests under one route segment to the selected Agent:
|
|
607
|
+
|
|
608
|
+
```typescript
|
|
609
|
+
import { Agent, callable, routeAgentRequest } from "agents";
|
|
610
|
+
import { RoutedAgents } from "agents/routing";
|
|
611
|
+
|
|
612
|
+
export class ChatAgent extends Agent<Env> {
|
|
613
|
+
// An ordinary top-level Agent: its own storage, alarms, and placement.
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
export class UserAgent extends Agent<Env> {
|
|
617
|
+
readonly chats = new RoutedAgents<ChatAgent, { title: string }>({
|
|
618
|
+
namespace: this.env.ChatAgent,
|
|
619
|
+
route: "chats"
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
constructor(ctx: DurableObjectState, env: Env) {
|
|
623
|
+
super(ctx, env);
|
|
624
|
+
this.lifecycle.use(this.chats);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
@callable()
|
|
628
|
+
createChat(title: string) {
|
|
629
|
+
return this.chats.create({ metadata: { title } });
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
@callable()
|
|
633
|
+
listChats() {
|
|
634
|
+
return this.chats.list();
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
@callable()
|
|
638
|
+
deleteChat(id: string) {
|
|
639
|
+
return this.chats.delete(id);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
export default {
|
|
644
|
+
async fetch(request: Request, env: Env) {
|
|
645
|
+
// Routes both /agents/user-agent/{id} and the forwarded
|
|
646
|
+
// /agents/user-agent/{id}/chats/{id}/... paths — RoutedAgents claims
|
|
647
|
+
// the latter from inside UserAgent once the request reaches it.
|
|
648
|
+
return (
|
|
649
|
+
(await routeAgentRequest(request, env)) ??
|
|
650
|
+
new Response("Not found", { status: 404 })
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
} satisfies ExportedHandler<Env>;
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
The client keeps one connection to the hub and one to the active chat, both addressed through the hub:
|
|
657
|
+
|
|
658
|
+
```
|
|
659
|
+
/agents/user-agent/alice -> UserAgent "alice"
|
|
660
|
+
/agents/user-agent/alice/chats/{id} -> the ChatAgent behind that entry
|
|
661
|
+
/agents/user-agent/alice/chats/{id}/... -> same ChatAgent, suffix preserved
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
```tsx
|
|
665
|
+
const user = useAgent({ agent: "UserAgent", name: "alice" });
|
|
666
|
+
const chat = useAgent({
|
|
667
|
+
agent: "ChatAgent",
|
|
668
|
+
basePath: `agents/user-agent/alice/chats/${encodeURIComponent(chatId)}`
|
|
669
|
+
});
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
What the capability guarantees:
|
|
673
|
+
|
|
674
|
+
- `create()`, `list()`, and `setMetadata()` touch only the hub's SQLite. No target wakes.
|
|
675
|
+
- `list()` orders most-recently-updated first, ties broken by write order rather than by the random entry ID. Each write scans the route's own entries to derive that order — an intentional trade for a route sized like one owner's own catalog, not a route meant to hold thousands of entries.
|
|
676
|
+
- `get(id)` returns an initialized, typed stub for RPC, or `null` for an unknown or deleted ID.
|
|
677
|
+
- A WebSocket upgrade is answered by the target, which then owns the socket. Chat frames never wake the hub. This is the same two-socket shape as connecting to the chat directly, but the hub stays the authority that resolves an ID, so it can gate, migrate, or redirect entries later.
|
|
678
|
+
- `delete(id)` hides the entry first, condemns the target, then removes the row. The target wipes its own storage on its next wake, moments later, and the condemned marker survives interruption. A failed call leaves a hidden row, and calling `delete` again retries.
|
|
679
|
+
- Physical names are random UUIDs that never leave the hub. Clients only ever see entry IDs.
|
|
680
|
+
- `namespace` is any `DurableObjectNamespace`, including a binding to a class exported by another Worker via `script_name`, so the hub and its targets can be deployed and scaled independently.
|
|
681
|
+
- Destroying the hub retries condemning every remaining entry before its own storage is wiped, but this is best-effort: the platform wipes the hub's storage right after disposal regardless of outcome, so a target that is still unreachable after retries is orphaned, with no catalog row left to retry from later.
|
|
682
|
+
|
|
683
|
+
The catalog stores existence, ownership, and application metadata. Conversation data stays in the target, and a target that needs its hub calls back with `getAgentByName(this.env.UserAgent, ownerName)`. When to prefer this over facets is covered in [Dynamic agents](./sub-agents.md#when-to-use-dynamic-agents).
|
|
684
|
+
|
|
685
|
+
Two sharp edges to design around:
|
|
686
|
+
|
|
687
|
+
- **Pick a route that can't collide.** Forwarding matches every occurrence of the route segment anywhere in the path, so if the hub's own name, class, or another one of its own routes is also literally `"chats"`, a coincidental match with no active entry behind it returns `404` instead of reaching the hub's own handler for that path.
|
|
688
|
+
- **A routed suffix can't address a target's own dynamic agents.** `Agent.fetch()` resolves a `/sub/{class}/{name}` marker against the _hub's_ exported classes before this capability's request handling ever runs, so `/chats/{id}/sub/{class}/{name}` is served as a facet of the hub, not forwarded to the chat. Reach a target's dynamic agents through a direct connection to that target instead of through the hub's route.
|
|
689
|
+
|
|
690
|
+
---
|
|
691
|
+
|
|
604
692
|
## Routing with Authentication
|
|
605
693
|
|
|
606
694
|
Check authentication before routing to agents:
|
|
@@ -737,6 +825,23 @@ Get an agent instance by name for server-side RPC or request forwarding.
|
|
|
737
825
|
|
|
738
826
|
**Returns:** `Promise<DurableObjectStub<T>>` - Typed stub for calling agent methods or forwarding requests
|
|
739
827
|
|
|
828
|
+
### `new RoutedAgents(options)` (from `agents/routing`)
|
|
829
|
+
|
|
830
|
+
A Lifecycle capability installed on a hub Agent with `this.lifecycle.use(...)`. See [Routing to independent Agents](#routing-to-independent-agents).
|
|
831
|
+
|
|
832
|
+
| Parameter | Type | Description |
|
|
833
|
+
| ------------------- | --------------------------- | ------------------------------------------------ |
|
|
834
|
+
| `options.namespace` | `DurableObjectNamespace<T>` | Target binding the entries are created in |
|
|
835
|
+
| `options.route` | `string` | One URL-safe path segment this capability claims |
|
|
836
|
+
|
|
837
|
+
| Method | Returns | Description |
|
|
838
|
+
| --------------------------- | ---------------------------------------------------- | ----------------------------------------------------- |
|
|
839
|
+
| `create(options?)` | `Promise<RoutedAgentEntry<Metadata>>` | Create an entry without waking the target |
|
|
840
|
+
| `get(id)` | `Promise<DurableObjectStub<T> \| null>` | Resolve an active entry to an initialized stub |
|
|
841
|
+
| `list()` | `Promise<ReadonlyArray<RoutedAgentEntry<Metadata>>>` | Active entries, most recently updated first |
|
|
842
|
+
| `setMetadata(id, metadata)` | `Promise<boolean>` | Replace an active entry's metadata |
|
|
843
|
+
| `delete(id)` | `Promise<boolean>` | Hide the entry, condemn the target, then drop the row |
|
|
844
|
+
|
|
740
845
|
### `useAgent(options)` / `AgentClient` Options
|
|
741
846
|
|
|
742
847
|
Client connection options:
|