agents 0.16.1 → 0.17.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.
Files changed (127) hide show
  1. package/README.md +11 -8
  2. package/dist/{agent-tool-types-NofdbL9X.d.ts → agent-tool-types-Cd1TZPfB.d.ts} +595 -137
  3. package/dist/agent-tool-types.d.ts +34 -18
  4. package/dist/agent-tool-types.js +20 -1
  5. package/dist/agent-tool-types.js.map +1 -0
  6. package/dist/agent-tools-BXlsuX0d.js +304 -0
  7. package/dist/agent-tools-BXlsuX0d.js.map +1 -0
  8. package/dist/agent-tools-_E8wxUIK.d.ts +119 -0
  9. package/dist/agent-tools.d.ts +24 -18
  10. package/dist/agent-tools.js.map +1 -1
  11. package/dist/ai-chat-agent.d.ts +1 -1
  12. package/dist/ai-chat-agent.js +1 -2
  13. package/dist/ai-chat-agent.js.map +1 -1
  14. package/dist/ai-chat-v5-migration.d.ts +1 -1
  15. package/dist/ai-chat-v5-migration.js +1 -2
  16. package/dist/ai-chat-v5-migration.js.map +1 -1
  17. package/dist/ai-react.d.ts +1 -1
  18. package/dist/ai-react.js +1 -2
  19. package/dist/ai-react.js.map +1 -1
  20. package/dist/ai-types.d.ts +1 -7
  21. package/dist/ai-types.js +2 -8
  22. package/dist/ai-types.js.map +1 -1
  23. package/dist/browser/ai.js +1 -1
  24. package/dist/browser/index.js +1 -1
  25. package/dist/chat/index.d.ts +1923 -76
  26. package/dist/chat/index.js +1784 -278
  27. package/dist/chat/index.js.map +1 -1
  28. package/dist/chat/react.d.ts +602 -0
  29. package/dist/chat/react.js +1506 -0
  30. package/dist/chat/react.js.map +1 -0
  31. package/dist/chat-sdk/index.d.ts +4 -4
  32. package/dist/{classPrivateFieldGet2-CZ7QjTXN.js → classPrivateFieldGet2-DZBYAB34.js} +5 -5
  33. package/dist/{classPrivateMethodInitSpec-D-0__zd9.js → classPrivateMethodInitSpec-qMjJ6sHQ.js} +2 -2
  34. package/dist/{client-FUizKzj2.js → client-BZ-B3NhC.js} +19 -3
  35. package/dist/client-BZ-B3NhC.js.map +1 -0
  36. package/dist/client-tools-aIBO0Fk7.d.ts +53 -0
  37. package/dist/client.d.ts +76 -57
  38. package/dist/client.js +33 -5
  39. package/dist/client.js.map +1 -1
  40. package/dist/{compaction-helpers-DVcu5lPN.d.ts → compaction-helpers-wUz6M3us.d.ts} +20 -1
  41. package/dist/{connector-CrKhowfD.js → connector-CdldGF3h.js} +5 -5
  42. package/dist/{connector-CrKhowfD.js.map → connector-CdldGF3h.js.map} +1 -1
  43. package/dist/email.js +1 -1
  44. package/dist/email.js.map +1 -1
  45. package/dist/experimental/memory/session/index.d.ts +1 -1
  46. package/dist/experimental/memory/session/index.js +20 -2
  47. package/dist/experimental/memory/session/index.js.map +1 -1
  48. package/dist/experimental/memory/utils/index.d.ts +1 -1
  49. package/dist/{index-B7IbEeze.d.ts → index-CcbnKkNh.d.ts} +188 -14
  50. package/dist/index.d.ts +91 -71
  51. package/dist/index.js +562 -24
  52. package/dist/index.js.map +1 -1
  53. package/dist/mcp/client.d.ts +18 -14
  54. package/dist/mcp/client.js +1 -1
  55. package/dist/mcp/index.d.ts +30 -30
  56. package/dist/mcp/index.js +7 -7
  57. package/dist/mcp/index.js.map +1 -1
  58. package/dist/{agent-tools-3zLG7MgA.js → message-builder-BymO4N_D.js} +45 -126
  59. package/dist/message-builder-BymO4N_D.js.map +1 -0
  60. package/dist/observability/index.d.ts +1 -1
  61. package/dist/observability/index.js +4 -2
  62. package/dist/observability/index.js.map +1 -1
  63. package/dist/react.d.ts +123 -110
  64. package/dist/react.js +44 -11
  65. package/dist/react.js.map +1 -1
  66. package/dist/serializable.d.ts +1 -1
  67. package/dist/skills/compile.js +1 -1
  68. package/dist/skills/compile.js.map +1 -1
  69. package/dist/skills/index.js +5 -5
  70. package/dist/skills/index.js.map +1 -1
  71. package/dist/sub-routing.d.ts +6 -6
  72. package/dist/utils.js +1 -1
  73. package/dist/utils.js.map +1 -1
  74. package/dist/vite.js +5 -5
  75. package/dist/vite.js.map +1 -1
  76. package/dist/wire-types-nflOzNuU.js +240 -0
  77. package/dist/wire-types-nflOzNuU.js.map +1 -0
  78. package/dist/{workflow-types-SrZK_o9p.d.ts → workflow-types-Baz_PO5v.d.ts} +42 -22
  79. package/dist/workflow-types.d.ts +25 -21
  80. package/dist/workflow-types.js.map +1 -1
  81. package/dist/workflows.d.ts +22 -21
  82. package/dist/workflows.js +31 -7
  83. package/dist/workflows.js.map +1 -1
  84. package/docs/adding-to-existing-project.md +450 -0
  85. package/docs/agent-class.md +503 -0
  86. package/docs/agent-tools.md +552 -0
  87. package/docs/browse-the-web.md +430 -0
  88. package/docs/callable-methods.md +627 -0
  89. package/docs/chat-agents.md +1687 -0
  90. package/docs/chat-sdk.md +181 -0
  91. package/docs/client-sdk.md +520 -0
  92. package/docs/client-tools-continuation.md +177 -0
  93. package/docs/codemode.md +440 -0
  94. package/docs/configuration.md +775 -0
  95. package/docs/cross-domain-authentication.md +171 -0
  96. package/docs/durable-execution.md +537 -0
  97. package/docs/email.md +663 -0
  98. package/docs/get-current-agent.md +204 -0
  99. package/docs/getting-started.md +305 -0
  100. package/docs/http-websockets.md +668 -0
  101. package/docs/human-in-the-loop.md +661 -0
  102. package/docs/index.md +151 -0
  103. package/docs/long-running-agents.md +730 -0
  104. package/docs/mcp-client.md +620 -0
  105. package/docs/mcp-servers.md +526 -0
  106. package/docs/mcp-transports.md +308 -0
  107. package/docs/migration-to-ai-sdk-v5.md +96 -0
  108. package/docs/migration-to-ai-sdk-v6.md +163 -0
  109. package/docs/observability.md +261 -0
  110. package/docs/push-notifications.md +367 -0
  111. package/docs/queue.md +329 -0
  112. package/docs/readonly-connections.md +278 -0
  113. package/docs/resumable-streaming.md +127 -0
  114. package/docs/retries.md +444 -0
  115. package/docs/routing.md +749 -0
  116. package/docs/scheduling.md +898 -0
  117. package/docs/securing-mcp-servers.md +359 -0
  118. package/docs/server-driven-messages.md +477 -0
  119. package/docs/sessions.md +1024 -0
  120. package/docs/state.md +512 -0
  121. package/docs/sub-agents.md +389 -0
  122. package/docs/webhooks.md +604 -0
  123. package/docs/workflows.md +877 -0
  124. package/package.json +47 -15
  125. package/dist/agent-tools-3zLG7MgA.js.map +0 -1
  126. package/dist/agent-tools-DLquv-dp.d.ts +0 -14
  127. package/dist/client-FUizKzj2.js.map +0 -1
@@ -0,0 +1,389 @@
1
+ # Sub-agents
2
+
3
+ Sub-agents are child Durable Objects colocated under a parent agent. Each sub-agent has its own isolated SQLite storage and its own WebSocket connections, but shares the parent's machine and is spawned, listed, and torn down through the parent. Clients reach a sub-agent directly via a nested URL; inside an agent, sub-agents are typed RPC stubs.
4
+
5
+ Use sub-agents when a single user or entity owns an open-ended set of long-lived agents — chats, documents, sessions, shards, projects — and you want each one to run in parallel with its own state while keeping one parent agent as the coordinator.
6
+
7
+ If you want a parent chat agent to dispatch another chat-capable agent during a
8
+ single turn and render that child's progress inline, use [Agent Tools](./agent-tools.md).
9
+ Agent tools are built on sub-agents, but add a parent-side run registry,
10
+ streaming `agent-tool-event` frames, replay, cancellation, and cleanup.
11
+
12
+ ## Overview
13
+
14
+ ```typescript
15
+ import { Agent, callable } from "agents";
16
+
17
+ export class Inbox extends Agent {
18
+ @callable()
19
+ async createChat() {
20
+ const id = crypto.randomUUID();
21
+ // Spawn a child Chat Durable Object under this Inbox.
22
+ await this.subAgent(Chat, id);
23
+ return id;
24
+ }
25
+
26
+ @callable()
27
+ listChats() {
28
+ return this.listSubAgents(Chat);
29
+ }
30
+ }
31
+
32
+ export class Chat extends Agent {
33
+ async say(text: string) {
34
+ // Reach back up to the inbox by class — the framework fills in
35
+ // the parent's instance name from `this.parentPath`.
36
+ const inbox = await this.parentAgent(Inbox);
37
+ await inbox.recordTurn(this.name, text);
38
+ }
39
+ }
40
+ ```
41
+
42
+ ```tsx
43
+ // Client
44
+ import { useAgent } from "agents/react";
45
+
46
+ // Connect to the inbox for the sidebar:
47
+ const inbox = useAgent({ agent: "Inbox", name: userId });
48
+
49
+ // Connect to a specific chat child:
50
+ const chat = useAgent({
51
+ agent: "Inbox",
52
+ name: userId,
53
+ sub: [{ agent: "Chat", name: chatId }]
54
+ });
55
+ ```
56
+
57
+ The resulting URL for the chat connection is `/agents/inbox/{userId}/sub/chat/{chatId}`.
58
+
59
+ ## Concepts
60
+
61
+ ```
62
+ ┌──────────────────────────────────────────────────────────┐
63
+ │ Inbox (Durable Object, "user-123") │
64
+ │ - parent of all chats for this user │
65
+ │ - owns chat list + shared memory │
66
+ │ - runs `onBeforeSubAgent` on every incoming /sub/ hop │
67
+ └────┬─────────────────────┬───────────────────┬───────────┘
68
+ │ │ │
69
+ ▼ ▼ ▼
70
+ Chat ("chat-a") Chat ("chat-b") Chat ("chat-c")
71
+ - own SQLite - own SQLite - own SQLite
72
+ - own WS clients - own WS clients - own WS clients
73
+ - runs in parallel with siblings
74
+ ```
75
+
76
+ ### Colocation
77
+
78
+ Sub-agents (also called **facets**) live on the **same machine** as their parent. They are not independent Durable Objects scattered across the edge — they are colocated with the parent for RPC latency and shared-memory patterns. Two chats belonging to the same inbox can run in parallel because each is its own single-threaded isolate, but they all share the parent's physical location.
79
+
80
+ ### Independent state
81
+
82
+ Each sub-agent has its own SQLite database and its own in-memory state. Writes from one sibling never leak into another. When a sub-agent is deleted with `deleteSubAgent()`, its storage is wiped.
83
+
84
+ ### Scheduling
85
+
86
+ Sub-agents can schedule their own callbacks with `this.schedule()` and `this.scheduleEvery()`:
87
+
88
+ ```typescript
89
+ export class Chat extends Agent {
90
+ async onStart() {
91
+ await this.scheduleEvery(60, "compactHistory");
92
+ }
93
+
94
+ async compactHistory() {
95
+ // Runs inside the Chat sub-agent.
96
+ // this.sql points at the Chat SQLite database.
97
+ }
98
+ }
99
+ ```
100
+
101
+ The top-level parent still owns the underlying Durable Object alarm because facets do not have independent alarm slots. The Agents SDK stores a logical owner path for the sub-agent schedule, wakes the parent when the alarm fires, then dispatches the callback back into the sub-agent. The callback runs with the sub-agent as `this`, so it uses the sub-agent's SQLite storage, state, `parentPath`, and `getCurrentAgent()` context.
102
+
103
+ `cancelSchedule()`, `getScheduleById()`, and `listSchedules()` also work inside sub-agents. They are scoped to the calling sub-agent — a sub-agent cannot cancel or list a sibling's schedules by id. To clear every schedule under a sub-agent (and any of its descendants), call `parent.deleteSubAgent(Cls, name)` from the parent. The older synchronous `getSchedule()` and `getSchedules()` APIs throw inside sub-agents because scheduled rows are stored on the top-level parent.
104
+
105
+ Calling `this.destroy()` inside a sub-agent delegates the same teardown back to the parent: it cancels the sub-agent's parent-owned schedules (and descendants), removes the sub-agent from the parent's registry, and asks the runtime to wipe the sub-agent's storage. Because the underlying `ctx.facets.delete` call aborts the sub-agent's isolate, treat `this.destroy()` as fire-and-forget — it may not return cleanly to the caller.
106
+
107
+ ### Durable execution and chat recovery
108
+
109
+ Sub-agents can use `runFiber()` and Think's `chatRecovery` just like top-level agents. Fiber rows live in the sub-agent's own SQLite database, so recovery hooks run with the sub-agent as `this` and see the sub-agent's state, storage, `parentPath`, and `getCurrentAgent()` context.
110
+
111
+ Because facets do not have independent alarm slots, the top-level parent owns the physical alarm heartbeat for sub-agent fibers. The sub-agent still stores fiber rows and snapshots in its own SQLite database, while the parent stores a small root-side index of active facet fibers. When the parent alarm fires, it checks that index and routes recovery checks back into the owning sub-agent. Think's chat recovery can schedule its recovered continuation from inside the sub-agent; the parent owns the physical alarm and routes the continuation back to the child.
112
+
113
+ Sub-agents can also start [Workflows](./workflows.md) with `this.runWorkflow()`. Workflow tracking is local to the sub-agent's SQLite database, and `AgentWorkflow.agent` routes RPC, callbacks, state updates, and broadcasts back to the originating sub-agent. Parent agents do not automatically list or control child-started workflows. Because `SubAgentStub<T>` only exposes user-defined child methods, add child wrapper methods for controls such as `getWorkflow()`, `approveWorkflow()`, or `terminateWorkflow()`, then call those wrappers through `await this.subAgent(Child, name)`. If you pass `runWorkflow(..., { agentBinding })` from a sub-agent, use the root Agent binding name, not a child binding name.
114
+
115
+ For sub-agent workflow origins, `AgentWorkflow.agent` is RPC-only. Use it to call Agent methods, but use `routeSubAgentRequest()` or the nested `/agents/{parent}/{name}/sub/{child}/{name}` URL shape for external HTTP or WebSocket routing instead of `this.agent.fetch()`.
116
+
117
+ ### Shared identity
118
+
119
+ Sub-agents know who their parent is via `this.parentPath` (root-first ancestor chain) and `this.parentAgent(ParentClass)` (typed stub). A sub-agent with no parent (top-level agent) has `parentPath === []`.
120
+
121
+ ## Server API
122
+
123
+ ### `this.subAgent(Cls, name)`
124
+
125
+ Get or create a sub-agent. Lazy: the first call for `(Cls, name)` spawns the child; subsequent calls return the existing instance. Returns a typed RPC stub.
126
+
127
+ ```typescript
128
+ const chat = await this.subAgent(Chat, "chat-abc");
129
+ await chat.ping();
130
+ ```
131
+
132
+ The child class must:
133
+
134
+ - Extend `Agent`
135
+ - Be exported from the worker entry point (so `ctx.exports[Cls.name]` can find it)
136
+ - Does NOT need to be registered under `new_sqlite_classes` unless the same class is also bound as a top-level Durable Object elsewhere. Facet storage is created through the top-level parent.
137
+ - _Not_ share a name with the reserved token `"Sub"` (any class whose kebab-cased name equals `"sub"` is rejected; it would collide with the `/sub/` URL separator)
138
+
139
+ The parent class also has requirements that are implicit for normal usage but worth knowing if you hit the related error:
140
+
141
+ - Be bound as a Durable Object namespace in `wrangler.jsonc durable_objects.bindings`. (Top-level agents always are — this matters only if you try to call `subAgent()` from a class that's exported but unbound.)
142
+ - Have its class name preserved by your bundler. The framework looks the parent up via `ctx.exports[this.constructor.name].idFromName(name)` to give the child its own `ctx.id.name`. If your bundler minifies class identifiers (e.g. esbuild without `keepNames: true`), `this.constructor.name` becomes a short id like `_a` and the lookup fails. The framework throws a descriptive error in that case pointing at the bundler config.
143
+
144
+ ### Notes for testing
145
+
146
+ Tests that use `@cloudflare/vitest-pool-workers` may need to list facet classes as test-only Durable Object bindings so `ctx.exports` provides a facet-compatible class value. Keep those facet classes out of `new_sqlite_classes`; the extra binding belongs only in test `wrangler.jsonc` files and is not a production Worker requirement.
147
+
148
+ ### `this.deleteSubAgent(Cls, name)`
149
+
150
+ Abort a running sub-agent, cancel its pending schedules, and permanently wipe its storage. Idempotent — safe to call for a never-spawned or already-deleted child.
151
+
152
+ ```typescript
153
+ await this.deleteSubAgent(Chat, "chat-abc");
154
+ ```
155
+
156
+ ### `this.abortSubAgent(Cls, name, reason?)`
157
+
158
+ Forcefully abort a running sub-agent without wiping its storage. The child stops executing immediately and will be restarted on next `subAgent()` access.
159
+
160
+ ```typescript
161
+ this.abortSubAgent(Chat, "chat-abc", new Error("quota exceeded"));
162
+ ```
163
+
164
+ ### `this.hasSubAgent(Cls | className, name)`
165
+
166
+ Check whether a child has been spawned and not deleted. Backed by a framework-maintained SQLite registry.
167
+
168
+ ```typescript
169
+ if (!this.hasSubAgent(Chat, id)) {
170
+ return new Response("not found", { status: 404 });
171
+ }
172
+ ```
173
+
174
+ ### `this.listSubAgents(Cls?)`
175
+
176
+ List spawned sub-agents, optionally filtered by class. Returns `{ className, name, createdAt }` rows in creation order.
177
+
178
+ ```typescript
179
+ const chats = this.listSubAgents(Chat);
180
+ // → [{ className: "Chat", name: "...", createdAt: 1700... }, ...]
181
+ ```
182
+
183
+ ### `this.onBeforeSubAgent(req, { className, name })`
184
+
185
+ Override this middleware hook on the parent to gate, mutate, or short-circuit incoming `/sub/` requests **before** the framework wakes the child. Mirrors `onBeforeConnect` / `onBeforeRequest`.
186
+
187
+ Return one of:
188
+
189
+ | Return value | Effect |
190
+ | ------------ | ---------------------------------------------------------------------- |
191
+ | `void` | Forward the original request to the child (permissive) |
192
+ | `Request` | Forward this modified request instead |
193
+ | `Response` | Short-circuit: send this response to the client, do not wake the child |
194
+
195
+ ```typescript
196
+ export class Inbox extends Agent {
197
+ override async onBeforeSubAgent(_req, { className, name }) {
198
+ // Strict-registry gate: only allow clients to reach chats that
199
+ // have actually been created via `createChat`.
200
+ if (!this.hasSubAgent(className, name)) {
201
+ return new Response(`${className} "${name}" not found`, {
202
+ status: 404
203
+ });
204
+ }
205
+ }
206
+ }
207
+ ```
208
+
209
+ The hook receives the **original** request with its URL intact — including the `/sub/{class}/{name}` segment. The routing decision for which facet to wake is fixed at parse time; headers, body, method, and query string on a returned `Request` flow through to the child, but the **pathname** the child sees is always the tail after `/sub/{class}/{name}`.
210
+
211
+ WebSocket upgrade requests flow through this hook the same way as plain HTTP. If you return a mutated `Request`, keep the original `Upgrade: websocket` and `Sec-WebSocket-*` headers — cloning via `new Headers(req.headers)` and only adding or replacing entries is the safest recipe.
212
+
213
+ ### `this.parentPath` and `this.selfPath`
214
+
215
+ Root-first ancestor chains. `parentPath` covers strict ancestors; `selfPath` includes the current agent.
216
+
217
+ ```typescript
218
+ // Inside a Chat that was spawned by an Inbox:
219
+ this.parentPath;
220
+ // → [{ className: "Inbox", name: "user-123" }]
221
+
222
+ this.selfPath;
223
+ // → [{ className: "Inbox", name: "user-123" }, { className: "Chat", name: "chat-abc" }]
224
+ ```
225
+
226
+ `parentPath` is **root-first**, so the direct parent is always `parentPath.at(-1)`. Top-level agents have `parentPath === []`.
227
+
228
+ ### `this.parentAgent(Cls)`
229
+
230
+ Typed parent stub to the **immediate** parent, resolved from `parentPath`. Symmetric with `subAgent(Cls, name)`: one opens a stub parent→child, the other opens a stub child→parent.
231
+
232
+ ```typescript
233
+ const inbox = await this.parentAgent(Inbox);
234
+ await inbox.recordTurn(this.name, "...");
235
+ ```
236
+
237
+ The framework:
238
+
239
+ 1. Verifies `Cls.name` matches the recorded direct-parent class (catches the "wrong class" mistake early).
240
+ 2. If the direct parent is a top-level Durable Object, opens the namespace for the exported parent class and returns a stub for the recorded parent name.
241
+ 3. If the direct parent is itself a facet, returns a proxy that routes method calls through the top-level root and then down the recorded facet path.
242
+
243
+ For grandparents and further ancestors that are top-level Durable Objects, iterate `this.parentPath` and call `getAgentByName(env.X, this.parentPath[i].name)` directly. Facet ancestors do not have their own `env` namespace binding; `parentAgent` is intentionally single-hop and only resolves the direct parent.
244
+
245
+ When `parentAgent()` returns a facet-parent proxy, RPC methods and normal HTTP `.fetch()` calls use the same internal bridge and do not run `onBeforeSubAgent`. WebSocket upgrade requests are not supported through `parentAgent().fetch()` yet because WebSocket handles cannot be serialized over RPC. Use externally routed sub-agent URLs for WebSocket connections.
246
+
247
+ | Capability | `parentAgent(Cls)` | External `/sub/...` routing |
248
+ | ----------------------- | ------------------------------ | -------------------------------------------- |
249
+ | Use case | Internal child-to-parent calls | Client or worker requests into a child facet |
250
+ | RPC methods | Yes | No |
251
+ | Normal HTTP `.fetch()` | Yes | Yes |
252
+ | WebSocket upgrades | No | Yes |
253
+ | Runs `onBeforeSubAgent` | No | Yes |
254
+
255
+ ## Client API
256
+
257
+ ### `useAgent({ sub: [...] })`
258
+
259
+ Extend any `useAgent` call with a `sub` chain to connect to a descendant facet:
260
+
261
+ ```tsx
262
+ const chat = useAgent({
263
+ agent: "Inbox",
264
+ name: userId,
265
+ sub: [{ agent: "Chat", name: chatId }]
266
+ });
267
+ ```
268
+
269
+ - `agent` / `name` identify the **top-level** agent (the one bound in `env`).
270
+ - `sub` is a root-first array of `{ agent, name }` hops into descendants.
271
+ - The hook builds the URL `/agents/inbox/{userId}/sub/chat/{chatId}` and opens a direct WebSocket to the `Chat` child.
272
+ - `.path` on the returned hook object gives you the full chain including the leaf.
273
+
274
+ Every other `useAgent` feature works as usual: `state` sync, `stub.method()` calls, `@callable` RPCs, `useAgentChat` on top of the returned socket.
275
+
276
+ ### Direct HTTP / custom routing
277
+
278
+ For fetch handlers that do their own top-level URL parsing, use `routeSubAgentRequest` to dispatch a request into a sub-agent from an already-resolved parent stub:
279
+
280
+ ```typescript
281
+ import { getAgentByName, routeSubAgentRequest } from "agents";
282
+
283
+ export default {
284
+ async fetch(req, env) {
285
+ const url = new URL(req.url);
286
+ const match = url.pathname.match(/^\/api\/u\/([^/]+)(\/.*)$/);
287
+ if (!match) return new Response("Not found", { status: 404 });
288
+
289
+ const [, userId, rest] = match;
290
+ const parent = await getAgentByName(env.Inbox, userId);
291
+ return routeSubAgentRequest(req, parent, { fromPath: rest });
292
+ }
293
+ };
294
+ ```
295
+
296
+ `fromPath` takes the sub-agent tail (something like `/sub/chat/chat-abc/...`). The helper parses it, runs the parent's `onBeforeSubAgent` hook, and forwards into the facet.
297
+
298
+ ### External typed RPC
299
+
300
+ From inside the parent DO, `this.subAgent(Cls, name)` returns a typed stub. From **outside** the parent, use `getSubAgentByName`:
301
+
302
+ ```typescript
303
+ import { getAgentByName, getSubAgentByName } from "agents";
304
+
305
+ const inbox = await getAgentByName(env.Inbox, userId);
306
+ const chat = await getSubAgentByName(inbox, Chat, chatId);
307
+
308
+ await chat.addMessage({ role: "user", content: "hi" });
309
+ ```
310
+
311
+ `getSubAgentByName` returns an RPC-only Proxy — method calls work; `.fetch()` throws (use `routeSubAgentRequest` for HTTP/WS). Arguments and return values must be structured-cloneable.
312
+
313
+ ## Lifecycle
314
+
315
+ ### Creation
316
+
317
+ `subAgent(Cls, name)` is lazy and idempotent:
318
+
319
+ - The first call for a name triggers the child's `onStart()`.
320
+ - Subsequent calls are no-ops and return the existing instance.
321
+ - The child is registered in the parent's `cf_agents_sub_agents` SQLite table.
322
+
323
+ ### Access from a client
324
+
325
+ When a client connects to `/agents/{parent}/{name}/sub/{child}/{childName}`:
326
+
327
+ 1. The request hits the top-level router and wakes the parent DO.
328
+ 2. The parent's `onBeforeSubAgent` fires.
329
+ 3. If the hook does not short-circuit, the framework resolves the facet (creating it on first access, unless the hook rejected with a `Response`).
330
+ 4. The request is forwarded to the child, which handles the WebSocket upgrade or HTTP response.
331
+ 5. After the upgrade, subsequent WebSocket frames flow **directly** to the child — the parent is no longer on the hot path.
332
+
333
+ ### Deletion
334
+
335
+ `deleteSubAgent(Cls, name)` aborts any running instance, removes pending schedules for that sub-agent tree, deletes its storage, and removes its registry entry. Idempotent.
336
+
337
+ ### Hibernation
338
+
339
+ Sub-agents hibernate when idle, same as any Durable Object. `this.name` is restored automatically from the facet's `ctx.id` (the runtime carries it across eviction). `this.parentPath` is persisted during `_cf_initAsFacet` and restored on wake.
340
+
341
+ ## Scheduling and durable work in sub-agents
342
+
343
+ Sub-agents can schedule their own callbacks and run durable fibers:
344
+
345
+ - `this.schedule()` / `this.scheduleEvery()` / `this.cancelSchedule()` work on a sub-agent.
346
+ - `this.getScheduleById()` / `this.listSchedules()` work on a sub-agent.
347
+ - `this.runFiber()` and Think `chatRecovery` work on a sub-agent.
348
+
349
+ The top-level parent still owns the physical alarm because facets do not have independent alarm slots. The Agents SDK stores the child owner path with each schedule row, wakes the parent, and routes the callback back into the child. `keepAlive()` and `keepAliveWhile()` work in sub-agents by delegating their heartbeat ref to the top-level parent. `runFiber()` also works in sub-agents: fiber rows and snapshots live in the child's own SQLite database, and the parent keeps a small root-side index so alarm housekeeping can route recovery checks back into idle children.
350
+
351
+ ## Broadcasts
352
+
353
+ `this.broadcast(msg)` and `setState()`-driven broadcasts work the same way inside a sub-agent as in a top-level agent — they go to the sub-agent's own WebSocket clients. Siblings do not see each other's broadcasts; reach them explicitly via RPC if needed.
354
+
355
+ ## When to use sub-agents
356
+
357
+ | Situation | Sub-agents? |
358
+ | ---------------------------------------------------------------------------------- | -------------------------------------------------- |
359
+ | One user owns an open-ended set of long-lived contexts (chats, docs, sessions) | Yes |
360
+ | You want each context to run in parallel with isolated state | Yes |
361
+ | You want a single parent DO to own the index, the shared memory, and the lifecycle | Yes |
362
+ | You need a worker pool, scatter/gather, or ephemeral task isolation | Often yes |
363
+ | You have a single conversation per user and no need for per-context isolation | No — just use one agent |
364
+ | The children need independent geographic placement | No — top-level DOs instead |
365
+ | The children need their own logical scheduled callbacks or chat recovery | Yes |
366
+ | The children need independent physical alarm slots | No — top-level DOs; revisit when facet alarms ship |
367
+
368
+ ## Example
369
+
370
+ See [`examples/multi-ai-chat`](https://github.com/cloudflare/agents/tree/main/examples/multi-ai-chat) for a complete multi-session chat app:
371
+
372
+ - `Inbox` is a top-level agent per user — owns the chat list, shared memory, and the strict-registry gate.
373
+ - `Chat` is an `AIChatAgent` facet. Each chat runs in parallel; storage is isolated.
374
+ - Server spawns via `this.subAgent(Chat, id)`; client connects via `useAgent({ sub: [...] })`.
375
+ - Shared-memory tools inside the chat use `this.parentAgent(Inbox)` to write into the parent.
376
+
377
+ ## Related
378
+
379
+ - [Think sub-agents and programmatic turns](https://github.com/cloudflare/agents/blob/main/docs/think/sub-agents.md) — Think's `chat()` RPC method for streaming from a parent to a Think-based child
380
+ - [Agent Tools](./agent-tools.md) — run Think or `AIChatAgent` sub-agents as tools with inline streaming child timelines
381
+ - [Long-running agents](./long-running-agents.md) — how sub-agents fit alongside `schedule`, `runFiber`, and workflows
382
+ - [Callable methods](./callable-methods.md) — `@callable` methods work unchanged on sub-agents
383
+ - [Scheduling](./scheduling.md) — scheduling primitives for top-level agents and sub-agents
384
+
385
+ ## See also
386
+
387
+ - RFC: [sub-agents](https://github.com/cloudflare/agents/blob/main/design/rfc-sub-agents.md) — why sub-agents were added
388
+ - RFC: [sub-agent routing](https://github.com/cloudflare/agents/blob/main/design/rfc-sub-agent-routing.md) — external addressability, URL shape, `onBeforeSubAgent`
389
+ - Design doc: [sub-agent routing](https://github.com/cloudflare/agents/blob/main/design/sub-agent-routing.md) — current mechanics and invariants