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.
Files changed (186) hide show
  1. package/README.md +20 -1
  2. package/dist/{agent-routing-CnkaHb-v.d.ts → agent-routing-DE5zmCQ8.d.ts} +1038 -394
  3. package/dist/agent-routing.d.ts +1 -1
  4. package/dist/agent-tool-types.d.ts +26 -26
  5. package/dist/{agent-tools-C0XJqWzB.d.ts → agent-tools-DtXMTDGM.d.ts} +7 -7
  6. package/dist/agent-tools.d.ts +21 -21
  7. package/dist/browser/ai.js +4 -1
  8. package/dist/browser/ai.js.map +1 -1
  9. package/dist/browser/tanstack-ai.js.map +1 -1
  10. package/dist/callable-decorator-DP__HhBA.d.ts +72 -0
  11. package/dist/callable-decorator.d.ts +18 -0
  12. package/dist/callable-decorator.js +71 -0
  13. package/dist/callable-decorator.js.map +1 -0
  14. package/dist/capability-BjSKYpzg.js.map +1 -1
  15. package/dist/capability-runner-Be_-PLR1.d.ts +459 -0
  16. package/dist/channel-Bnm4S7T2.d.ts +491 -0
  17. package/dist/channels/ai-sdk.d.ts +46 -0
  18. package/dist/channels/ai-sdk.js +120 -0
  19. package/dist/channels/ai-sdk.js.map +1 -0
  20. package/dist/channels/email.d.ts +95 -0
  21. package/dist/channels/email.js +323 -0
  22. package/dist/channels/email.js.map +1 -0
  23. package/dist/channels/index.d.ts +233 -0
  24. package/dist/channels/index.js +608 -0
  25. package/dist/channels/index.js.map +1 -0
  26. package/dist/channels/slack.d.ts +140 -0
  27. package/dist/channels/slack.js +614 -0
  28. package/dist/channels/slack.js.map +1 -0
  29. package/dist/channels/tanstack-ai.d.ts +39 -0
  30. package/dist/channels/tanstack-ai.js +17 -0
  31. package/dist/channels/tanstack-ai.js.map +1 -0
  32. package/dist/channels/telegram.d.ts +106 -0
  33. package/dist/channels/telegram.js +427 -0
  34. package/dist/channels/telegram.js.map +1 -0
  35. package/dist/channels/voice.d.ts +45 -0
  36. package/dist/channels/voice.js +122 -0
  37. package/dist/channels/voice.js.map +1 -0
  38. package/dist/chat/index.d.ts +2325 -2019
  39. package/dist/chat/index.js +888 -518
  40. package/dist/chat/index.js.map +1 -1
  41. package/dist/chat-sdk/index.d.ts +7 -7
  42. package/dist/chat-sdk/index.js +1 -1
  43. package/dist/client.d.ts +1 -1
  44. package/dist/context/index.d.ts +216 -0
  45. package/dist/context/index.js +454 -0
  46. package/dist/context/index.js.map +1 -0
  47. package/dist/{current-agent-CuMErtly.d.ts → current-agent-Da_C9a3b.d.ts} +90 -107
  48. package/dist/current-agent-DhoDkSnH.js.map +1 -1
  49. package/dist/{diagnostics-CaBjfz4J.js → diagnostics-BzvaX2UT.js} +5 -1
  50. package/dist/diagnostics-BzvaX2UT.js.map +1 -0
  51. package/dist/diagnostics-C4jcz3VK.js +360 -0
  52. package/dist/diagnostics-C4jcz3VK.js.map +1 -0
  53. package/dist/index-BB0kqhIz.d.ts +101 -0
  54. package/dist/index-BVVgDSdq.d.ts +1 -0
  55. package/dist/index-XDkuQ7zm.d.ts +89 -0
  56. package/dist/{index-DcSAZKsB.d.ts → index-YSKgfgg9.d.ts} +3 -1
  57. package/dist/index.d.ts +91 -82
  58. package/dist/index.js +3 -2
  59. package/dist/ingress-BfetZbMO.js +83 -0
  60. package/dist/ingress-BfetZbMO.js.map +1 -0
  61. package/dist/internal-CYlgHl1l.js +59 -0
  62. package/dist/internal-CYlgHl1l.js.map +1 -0
  63. package/dist/internal_context.d.ts +1 -1
  64. package/dist/lifecycle/index.d.ts +35 -17
  65. package/dist/lifecycle/index.js +1 -1
  66. package/dist/lifecycle-CMRGjZdw.js +1299 -0
  67. package/dist/lifecycle-CMRGjZdw.js.map +1 -0
  68. package/dist/mcp/client/index.d.ts +20 -20
  69. package/dist/mcp/index.d.ts +35 -35
  70. package/dist/mcp/index.js +1 -1
  71. package/dist/observability/index.d.ts +1 -1
  72. package/dist/observability/index.js +1 -1
  73. package/dist/react.d.ts +4 -4
  74. package/dist/{retries-CAvxtG9d.d.ts → retries-D9Ds-1lz.d.ts} +17 -6
  75. package/dist/retries.d.ts +8 -6
  76. package/dist/retries.js +13 -1
  77. package/dist/retries.js.map +1 -1
  78. package/dist/routing/index.d.ts +137 -0
  79. package/dist/routing/index.js +244 -0
  80. package/dist/routing/index.js.map +1 -0
  81. package/dist/sanitize-D9TujEK8.js +79 -0
  82. package/dist/sanitize-D9TujEK8.js.map +1 -0
  83. package/dist/scheduler-DD9NdYbF.js +665 -0
  84. package/dist/scheduler-DD9NdYbF.js.map +1 -0
  85. package/dist/{scheduler-DQoTGoAW.d.ts → scheduler-Dwh85ZGl.d.ts} +21 -22
  86. package/dist/schedules/index.d.ts +1 -1
  87. package/dist/schedules/index.js +1 -1
  88. package/dist/sentence-chunker-BAidJ4DA.d.ts +68 -0
  89. package/dist/serializable.d.ts +1 -1
  90. package/dist/sessions/index.d.ts +441 -0
  91. package/dist/sessions/index.js +2063 -0
  92. package/dist/sessions/index.js.map +1 -0
  93. package/dist/skills/index.d.ts +99 -0
  94. package/dist/skills/index.js +254 -5
  95. package/dist/skills/index.js.map +1 -1
  96. package/dist/{src-5W6JNKVb.js → src-DlSHshb2.js} +1460 -1110
  97. package/dist/src-DlSHshb2.js.map +1 -0
  98. package/dist/streams/index.d.ts +120 -0
  99. package/dist/streams/index.js +107 -0
  100. package/dist/streams/index.js.map +1 -0
  101. package/dist/streams-D6tJ0NN9.d.ts +370 -0
  102. package/dist/streams-DZKgAj9b.js +709 -0
  103. package/dist/streams-DZKgAj9b.js.map +1 -0
  104. package/dist/sub-routing.d.ts +12 -12
  105. package/dist/surface-bZZJqBka.js +17 -0
  106. package/dist/surface-bZZJqBka.js.map +1 -0
  107. package/dist/tasks/index.d.ts +64 -0
  108. package/dist/tasks/index.js +2 -0
  109. package/dist/tasks-BRJ5zgya.d.ts +517 -0
  110. package/dist/tasks-ylZgBjhj.js +1656 -0
  111. package/dist/tasks-ylZgBjhj.js.map +1 -0
  112. package/dist/text-segment-joiner-BtAFQSA_.js +57 -0
  113. package/dist/text-segment-joiner-BtAFQSA_.js.map +1 -0
  114. package/dist/text-stream-CpdiKrJB.js +272 -0
  115. package/dist/text-stream-CpdiKrJB.js.map +1 -0
  116. package/dist/tokens-nHAKcN6M.js +52 -0
  117. package/dist/tokens-nHAKcN6M.js.map +1 -0
  118. package/dist/tool-schema-CBjGPrsQ.js +31 -0
  119. package/dist/tool-schema-CBjGPrsQ.js.map +1 -0
  120. package/dist/types-B7LojTe4.d.ts +202 -0
  121. package/dist/types-_Faxb570.d.ts +439 -0
  122. package/dist/voice/client.d.ts +226 -0
  123. package/dist/voice/client.js +932 -0
  124. package/dist/voice/client.js.map +1 -0
  125. package/dist/voice/errors.d.ts +43 -0
  126. package/dist/voice/errors.js +41 -0
  127. package/dist/voice/errors.js.map +1 -0
  128. package/dist/voice/index.d.ts +271 -0
  129. package/dist/voice/index.js +1812 -0
  130. package/dist/voice/index.js.map +1 -0
  131. package/dist/voice/react.d.ts +167 -0
  132. package/dist/voice/react.js +234 -0
  133. package/dist/voice/react.js.map +1 -0
  134. package/dist/voice/sfu.d.ts +71 -0
  135. package/dist/voice/sfu.js +157 -0
  136. package/dist/voice/sfu.js.map +1 -0
  137. package/dist/voice/text.d.ts +6 -0
  138. package/dist/voice/text.js +2 -0
  139. package/dist/voice/types.d.ts +58 -0
  140. package/dist/voice/types.js +18 -0
  141. package/dist/voice/types.js.map +1 -0
  142. package/dist/voice/workers-ai.d.ts +136 -0
  143. package/dist/voice/workers-ai.js +568 -0
  144. package/dist/voice/workers-ai.js.map +1 -0
  145. package/dist/websockets/index.d.ts +192 -0
  146. package/dist/websockets/index.js +2 -0
  147. package/dist/websockets-DUfRHPRq.js +502 -0
  148. package/dist/websockets-DUfRHPRq.js.map +1 -0
  149. package/dist/workflow-types.d.ts +25 -25
  150. package/dist/workflows.d.ts +21 -21
  151. package/dist/workflows.js +1 -1
  152. package/docs/agent-class.md +2 -2
  153. package/docs/agent-tools.md +2 -1
  154. package/docs/channels.md +323 -0
  155. package/docs/chat-agents.md +6 -13
  156. package/docs/context.md +131 -0
  157. package/docs/index.md +15 -12
  158. package/docs/lifecycle.md +102 -55
  159. package/docs/long-running-agents.md +2 -2
  160. package/docs/mcp-servers.md +5 -1
  161. package/docs/resumable-streaming.md +1 -1
  162. package/docs/routing.md +105 -0
  163. package/docs/sessions.md +237 -871
  164. package/docs/streams.md +213 -0
  165. package/docs/sub-agents.md +184 -124
  166. package/docs/tasks.md +246 -0
  167. package/docs/voice.md +745 -0
  168. package/package.json +115 -13
  169. package/dist/capability-runner-CvHGZqUu.d.ts +0 -150
  170. package/dist/compaction-helpers-iiKMr2TQ.js +0 -340
  171. package/dist/compaction-helpers-iiKMr2TQ.js.map +0 -1
  172. package/dist/compaction-helpers-wUz6M3us.d.ts +0 -621
  173. package/dist/diagnostics-CaBjfz4J.js.map +0 -1
  174. package/dist/durable-object-lifecycle-D6nNQJJd.js +0 -862
  175. package/dist/durable-object-lifecycle-D6nNQJJd.js.map +0 -1
  176. package/dist/experimental/memory/session/index.d.ts +0 -671
  177. package/dist/experimental/memory/session/index.js +0 -2379
  178. package/dist/experimental/memory/session/index.js.map +0 -1
  179. package/dist/experimental/memory/utils/index.d.ts +0 -96
  180. package/dist/experimental/memory/utils/index.js +0 -79
  181. package/dist/experimental/memory/utils/index.js.map +0 -1
  182. package/dist/scheduler-CR9RHGos.js +0 -857
  183. package/dist/scheduler-CR9RHGos.js.map +0 -1
  184. package/dist/src-5W6JNKVb.js.map +0 -1
  185. package/dist/tool-output-truncation-CNnnGZQ3.js +0 -98
  186. package/dist/tool-output-truncation-CNnnGZQ3.js.map +0 -1
@@ -3,23 +3,1179 @@ import { getAgentByName } from "./agent-routing.js";
3
3
  import { AGENT_TOOL_MILESTONE_PART } from "./agent-tool-types.js";
4
4
  import { n as getCurrentAgent$1, t as __DO_NOT_USE_WILL_BREAK__agentContext } from "./current-agent-DhoDkSnH.js";
5
5
  import "./internal_context.js";
6
+ import { copyCallableMetadata, decoratedMethods, getCallableMetadata, isCallableMethod } from "./callable-decorator.js";
6
7
  import "./types.js";
7
8
  import { signAgentHeaders } from "./email.js";
8
9
  import { sendAgentEmail } from "./email-send.js";
9
- import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-DZBYAB34.js";
10
+ import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, r as _assertClassBrand, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-DZBYAB34.js";
10
11
  import { n as withInvocationScope, r as writeSpanAttributes, t as tracer } from "./cloudflare-Dzvc7V2N.js";
11
12
  import { parseSubAgentPath } from "./sub-routing.js";
12
- import { i as setLifecycleRouteTransport, n as setLifecycleEventSink, r as setLifecycleHostInvoker, t as Lifecycle } from "./durable-object-lifecycle-D6nNQJJd.js";
13
+ import { t as LifecycleCapability } from "./capability-BjSKYpzg.js";
14
+ import { t as _classPrivateMethodInitSpec } from "./classPrivateMethodInitSpec-qMjJ6sHQ.js";
15
+ import { a as abortWithoutAlarmRetry, i as setLifecycleRouteTransport, n as setLifecycleEventSink, r as setLifecycleHostInvoker, t as Lifecycle } from "./lifecycle-CMRGjZdw.js";
16
+ import { isErrorRetryable, tryN, validateRetryOptions } from "./retries.js";
13
17
  import { SqlError } from "./sql-error.js";
14
- import { isDurableObjectMemoryLimitReset, isErrorRetryable, tryN, validateRetryOptions } from "./retries.js";
18
+ import { o as callablesFromDecorated, t as WebSockets } from "./websockets-DUfRHPRq.js";
15
19
  import { a as ensureMcpServerTable, d as DisposableStore, i as normalizeServerId, l as RPC_DO_PREFIX, o as MCPConnectionState, t as MCPClientManager } from "./client-jagG8a9_.js";
16
20
  import { DurableObjectOAuthClientProvider } from "./mcp/client/do-oauth-client-provider.js";
17
21
  import { genericObservability } from "./observability/index.js";
18
- import { n as ensureScheduleTable, r as setSchedulerCallbackResolver, t as Scheduler } from "./scheduler-CR9RHGos.js";
22
+ import { n as setSchedulerCallbackResolver, t as Scheduler } from "./scheduler-DD9NdYbF.js";
23
+ import { n as setTaskDefinitionResolver, r as setTaskRoutedMemoryLimitHandler, t as Tasks } from "./tasks-ylZgBjhj.js";
19
24
  import { AsyncLocalStorage } from "node:async_hooks";
25
+ import { DurableObject, RpcTarget, exports } from "cloudflare:workers";
20
26
  import { nanoid } from "nanoid";
21
27
  import { EmailMessage } from "cloudflare:email";
22
- import { DurableObject, RpcTarget, exports } from "cloudflare:workers";
28
+ //#region src/dynamic-agents/bridges.ts
29
+ function isClosedWebSocketSendError(error) {
30
+ return error instanceof TypeError && error.message.includes("WebSocket send() after close");
31
+ }
32
+ const dynamicAgentRpcReplyContext = new AsyncLocalStorage();
33
+ function sendFacetRpcResponseIfOpen(target, response) {
34
+ try {
35
+ return {
36
+ sent: true,
37
+ completion: Promise.resolve(target.send(JSON.stringify(response))).catch((error) => {
38
+ if (!isClosedWebSocketSendError(error)) console.error("[Agent] Facet RPC response delivery failed:", error);
39
+ })
40
+ };
41
+ } catch (error) {
42
+ if (isClosedWebSocketSendError(error)) return {
43
+ sent: false,
44
+ completion: Promise.resolve()
45
+ };
46
+ throw error;
47
+ }
48
+ }
49
+ const facetStreamingResponseDeliveryStates = /* @__PURE__ */ new WeakMap();
50
+ /**
51
+ * Mark a StreamingResponse as facet-bridged: its chunks are delivered to
52
+ * `replyTarget` (the RPC frame that carried the request into the facet)
53
+ * instead of the connection's native WebSocket.
54
+ */
55
+ function registerFacetStreamingDelivery(stream, replyTarget) {
56
+ facetStreamingResponseDeliveryStates.set(stream, {
57
+ replyTarget,
58
+ pending: /* @__PURE__ */ new Set()
59
+ });
60
+ }
61
+ /**
62
+ * Deliver one streamed RPC response for a facet-bridged stream, tracking
63
+ * its completion. Returns null when the stream is not facet-bridged (the
64
+ * caller should send on the native connection instead).
65
+ */
66
+ function sendFacetStreamingResponse(stream, response) {
67
+ const state = facetStreamingResponseDeliveryStates.get(stream);
68
+ if (!state) return null;
69
+ const delivery = sendFacetRpcResponseIfOpen(state.replyTarget, response);
70
+ state.pending.add(delivery.completion);
71
+ delivery.completion.finally(() => state.pending.delete(delivery.completion));
72
+ return delivery.sent;
73
+ }
74
+ async function waitForFacetStreamingResponseDeliveries(stream) {
75
+ const state = facetStreamingResponseDeliveryStates.get(stream);
76
+ if (!state) return;
77
+ try {
78
+ await Promise.all(state.pending);
79
+ } finally {
80
+ facetStreamingResponseDeliveryStates.delete(stream);
81
+ }
82
+ }
83
+ var _connection = /* @__PURE__ */ new WeakMap();
84
+ var _broadcast = /* @__PURE__ */ new WeakMap();
85
+ /**
86
+ * Parent-side bridge handed to a facet over RPC: wraps a live root-owned
87
+ * `Connection` so the facet can send/close/setState on it, and carries
88
+ * the root's broadcast entry point for facet-scoped broadcasts.
89
+ */
90
+ var DynamicAgentConnectionBridge = class extends RpcTarget {
91
+ constructor(connection, broadcast) {
92
+ super();
93
+ _classPrivateFieldInitSpec(this, _connection, void 0);
94
+ _classPrivateFieldInitSpec(this, _broadcast, void 0);
95
+ _classPrivateFieldSet2(_connection, this, connection);
96
+ _classPrivateFieldSet2(_broadcast, this, broadcast);
97
+ }
98
+ send(message) {
99
+ _classPrivateFieldGet2(_connection, this).send(message);
100
+ }
101
+ close(code, reason) {
102
+ _classPrivateFieldGet2(_connection, this).close(code, reason);
103
+ }
104
+ setState(state) {
105
+ return _classPrivateFieldGet2(_connection, this).setState(state);
106
+ }
107
+ broadcast(ownerPath, message, without) {
108
+ return _classPrivateFieldGet2(_broadcast, this)?.call(this, ownerPath, message, without);
109
+ }
110
+ };
111
+ var _root = /* @__PURE__ */ new WeakMap();
112
+ var _connectionId = /* @__PURE__ */ new WeakMap();
113
+ /**
114
+ * Facet-side bridge used after the originating RPC frame has completed:
115
+ * routes connection operations back to the root over a fresh RPC call.
116
+ */
117
+ var RootDynamicAgentConnectionBridge = class {
118
+ constructor(root, connectionId) {
119
+ _classPrivateFieldInitSpec(this, _root, void 0);
120
+ _classPrivateFieldInitSpec(this, _connectionId, void 0);
121
+ _classPrivateFieldSet2(_root, this, root);
122
+ _classPrivateFieldSet2(_connectionId, this, connectionId);
123
+ }
124
+ send(message) {
125
+ return _classPrivateFieldGet2(_root, this)._cf_sendToSubAgentConnection(_classPrivateFieldGet2(_connectionId, this), message);
126
+ }
127
+ close(code, reason) {
128
+ return _classPrivateFieldGet2(_root, this)._cf_closeSubAgentConnection(_classPrivateFieldGet2(_connectionId, this), code, reason);
129
+ }
130
+ setState(state) {
131
+ return _classPrivateFieldGet2(_root, this)._cf_setSubAgentConnectionState(_classPrivateFieldGet2(_connectionId, this), state);
132
+ }
133
+ broadcast(ownerPath, message, without) {
134
+ return _classPrivateFieldGet2(_root, this)._cf_broadcastToSubAgent(ownerPath, message, without);
135
+ }
136
+ };
137
+ //#endregion
138
+ //#region src/dynamic-agents/identity.ts
139
+ const SUB_AGENT_IDENTITY_VERSION_LEGACY = "legacy";
140
+ const SUB_AGENT_IDENTITY_VERSION_PATH_V2 = "path-v2";
141
+ const SUB_AGENT_IDENTITY_PATH_V2_PREFIX = "cf-agents:v2:";
142
+ async function sha256Hex(value) {
143
+ const bytes = new TextEncoder().encode(value);
144
+ const digest = await crypto.subtle.digest("SHA-256", bytes);
145
+ return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
146
+ }
147
+ function pathV2IdentityName(logicalName, digest) {
148
+ return `${SUB_AGENT_IDENTITY_PATH_V2_PREFIX}${encodeURIComponent(logicalName)}:${digest}`;
149
+ }
150
+ function logicalNameFromPathV2Identity(identityName) {
151
+ if (!identityName.startsWith("cf-agents:v2:")) return null;
152
+ const rest = identityName.slice(13);
153
+ const separator = rest.lastIndexOf(":");
154
+ if (separator === -1) return null;
155
+ try {
156
+ return decodeURIComponent(rest.slice(0, separator));
157
+ } catch {
158
+ return null;
159
+ }
160
+ }
161
+ /**
162
+ * Validate that a stored `parentPath` has the expected shape. Used
163
+ * when restoring from DO storage to guard against corrupted data.
164
+ */
165
+ function isValidParentPath(value) {
166
+ if (!Array.isArray(value)) return false;
167
+ return value.every((entry) => entry != null && typeof entry === "object" && typeof entry.className === "string" && typeof entry.name === "string");
168
+ }
169
+ function agentPathKey(path) {
170
+ if (!path) return null;
171
+ return path.map((step) => `${encodeURIComponent(step.className)}:${encodeURIComponent(step.name)}`).join("/");
172
+ }
173
+ //#endregion
174
+ //#region src/dynamic-agents/registry.ts
175
+ var _host$1 = /* @__PURE__ */ new WeakMap();
176
+ var _ready = /* @__PURE__ */ new WeakMap();
177
+ var _DynamicAgentRegistry_brand = /* @__PURE__ */ new WeakSet();
178
+ /**
179
+ * The parent-side registry of spawned dynamic agents (facets), stored
180
+ * in the parent's own SQLite. Backs `hasSubAgent` / `listSubAgents`
181
+ * and the identity-versioning decision (legacy bare-name facets vs
182
+ * path-scoped v2 identities).
183
+ *
184
+ * Table and column names are storage-frozen — never rename them.
185
+ */
186
+ var DynamicAgentRegistry = class {
187
+ constructor(host) {
188
+ _classPrivateMethodInitSpec(this, _DynamicAgentRegistry_brand);
189
+ _classPrivateFieldInitSpec(this, _host$1, void 0);
190
+ _classPrivateFieldInitSpec(this, _ready, false);
191
+ _classPrivateFieldSet2(_host$1, this, host);
192
+ }
193
+ ensure() {
194
+ if (_classPrivateFieldGet2(_ready, this)) return;
195
+ _classPrivateFieldGet2(_host$1, this).sql`
196
+ CREATE TABLE IF NOT EXISTS cf_agents_sub_agents (
197
+ class TEXT NOT NULL,
198
+ name TEXT NOT NULL,
199
+ created_at INTEGER NOT NULL,
200
+ identity_version TEXT,
201
+ identity_name TEXT,
202
+ PRIMARY KEY (class, name)
203
+ )
204
+ `;
205
+ _assertClassBrand(_DynamicAgentRegistry_brand, this, _addColumnIfNotExists).call(this, "ALTER TABLE cf_agents_sub_agents ADD COLUMN identity_version TEXT");
206
+ _assertClassBrand(_DynamicAgentRegistry_brand, this, _addColumnIfNotExists).call(this, "ALTER TABLE cf_agents_sub_agents ADD COLUMN identity_name TEXT");
207
+ _classPrivateFieldSet2(_ready, this, true);
208
+ }
209
+ record(className, name, identity) {
210
+ this.ensure();
211
+ _classPrivateFieldGet2(_host$1, this).sql`
212
+ INSERT OR IGNORE INTO cf_agents_sub_agents
213
+ (class, name, created_at, identity_version, identity_name)
214
+ VALUES
215
+ (${className}, ${name}, ${Date.now()}, ${identity.version}, ${identity.name})
216
+ `;
217
+ }
218
+ row(className, name) {
219
+ this.ensure();
220
+ return _classPrivateFieldGet2(_host$1, this).sql`
221
+ SELECT identity_version, identity_name
222
+ FROM cf_agents_sub_agents
223
+ WHERE class = ${className} AND name = ${name}
224
+ LIMIT 1
225
+ `[0] ?? null;
226
+ }
227
+ async identity(className, name, childPath) {
228
+ const row = this.row(className, name);
229
+ if (row) {
230
+ if (row.identity_version === "path-v2" && typeof row.identity_name === "string") return {
231
+ version: SUB_AGENT_IDENTITY_VERSION_PATH_V2,
232
+ name: row.identity_name,
233
+ existing: true
234
+ };
235
+ return {
236
+ version: SUB_AGENT_IDENTITY_VERSION_LEGACY,
237
+ name,
238
+ existing: true
239
+ };
240
+ }
241
+ return {
242
+ version: SUB_AGENT_IDENTITY_VERSION_PATH_V2,
243
+ name: pathV2IdentityName(name, await sha256Hex(JSON.stringify(childPath))),
244
+ existing: false
245
+ };
246
+ }
247
+ forget(className, name) {
248
+ this.ensure();
249
+ _classPrivateFieldGet2(_host$1, this).sql`
250
+ DELETE FROM cf_agents_sub_agents
251
+ WHERE class = ${className} AND name = ${name}
252
+ `;
253
+ }
254
+ has(className, name) {
255
+ this.ensure();
256
+ return (_classPrivateFieldGet2(_host$1, this).sql`
257
+ SELECT COUNT(*) AS n FROM cf_agents_sub_agents
258
+ WHERE class = ${className} AND name = ${name}
259
+ `[0]?.n ?? 0) > 0;
260
+ }
261
+ list(className) {
262
+ this.ensure();
263
+ return (className ? _classPrivateFieldGet2(_host$1, this).sql`
264
+ SELECT class, name, created_at FROM cf_agents_sub_agents
265
+ WHERE class = ${className}
266
+ ORDER BY created_at ASC
267
+ ` : _classPrivateFieldGet2(_host$1, this).sql`
268
+ SELECT class, name, created_at FROM cf_agents_sub_agents
269
+ ORDER BY created_at ASC
270
+ `).map((r) => ({
271
+ className: r.class,
272
+ name: r.name,
273
+ createdAt: r.created_at
274
+ }));
275
+ }
276
+ };
277
+ function _addColumnIfNotExists(sql) {
278
+ try {
279
+ _classPrivateFieldGet2(_host$1, this).execRawSql(sql);
280
+ } catch (e) {
281
+ if (!(e instanceof Error ? e.message : String(e)).toLowerCase().includes("duplicate column")) throw e;
282
+ }
283
+ }
284
+ //#endregion
285
+ //#region src/dynamic-agents/dynamic-agents.ts
286
+ /**
287
+ * Internal key used to remember the outer `/sub/...` URL for a
288
+ * WebSocket accepted by the parent on behalf of a child facet.
289
+ * Hibernated events then wake the parent, which forwards frames to
290
+ * the child over serializable RPC while keeping native WebSocket I/O
291
+ * parent-owned.
292
+ *
293
+ * Storage-frozen — never rename.
294
+ */
295
+ const CF_SUB_AGENT_OUTER_URL_KEY = "_cf_subAgentOuterUrl";
296
+ const CF_SUB_AGENT_TAGS_KEY = "_cf_subAgentTags";
297
+ /** Wire-frozen internal header carrying the outer URL on WS upgrades. */
298
+ const SUB_AGENT_OUTER_URL_HEADER = "x-cf-agents-subagent-url";
299
+ var _host = /* @__PURE__ */ new WeakMap();
300
+ var _facetKeepAliveTokens = /* @__PURE__ */ new WeakMap();
301
+ var _bridgeContext = /* @__PURE__ */ new WeakMap();
302
+ var _virtualConnections = /* @__PURE__ */ new WeakMap();
303
+ var _connectionOperationTails = /* @__PURE__ */ new WeakMap();
304
+ var _broadcastOperationTail = /* @__PURE__ */ new WeakMap();
305
+ var _DynamicAgentsInternal_brand = /* @__PURE__ */ new WeakSet();
306
+ /**
307
+ * The facet-backed dynamic-agent machinery, extracted from the Agent
308
+ * class. One instance per Agent, installed as a Lifecycle capability
309
+ * (`capabilityId: "dynamic-agents"`); the host port documents exactly
310
+ * which Agent internals it touches.
311
+ *
312
+ * The capability claims no runner hooks — four integration points are
313
+ * deliberately wired directly through the Agent composition root
314
+ * instead, because the runner's dispatch contract cannot express them:
315
+ * the `/sub/` upgrade path rewrites the request and *continues* into
316
+ * `lifecycle.fetch` (onRequest can only claim), forwarded WS frames run
317
+ * inside the host's onMessage wrapper *after* the WebSockets capability
318
+ * has claimed the wake, this module *implements* the lifecycle route
319
+ * transport rather than consuming it, and facet-context restore has
320
+ * load-bearing startup ordering inside the host's startup span.
321
+ *
322
+ * Nothing here renames any wire- or storage-visible identifier: the
323
+ * `cf_agents_facet_runs` table, `_cf_*` RPC method names, and route
324
+ * key formats are frozen.
325
+ *
326
+ * @internal
327
+ */
328
+ var DynamicAgentsInternal = class extends LifecycleCapability {
329
+ constructor(host) {
330
+ super("dynamic-agents");
331
+ _classPrivateMethodInitSpec(this, _DynamicAgentsInternal_brand);
332
+ _classPrivateFieldInitSpec(this, _host, void 0);
333
+ _classPrivateFieldInitSpec(this, _facetKeepAliveTokens, /* @__PURE__ */ new Set());
334
+ _classPrivateFieldInitSpec(this, _bridgeContext, new AsyncLocalStorage());
335
+ _classPrivateFieldInitSpec(this, _virtualConnections, /* @__PURE__ */ new Map());
336
+ _classPrivateFieldInitSpec(this, _connectionOperationTails, /* @__PURE__ */ new Map());
337
+ _classPrivateFieldInitSpec(this, _broadcastOperationTail, void 0);
338
+ _classPrivateFieldSet2(_host, this, host);
339
+ this.registry = new DynamicAgentRegistry({
340
+ sql: host.sql.bind(host),
341
+ execRawSql: (sql) => void host.ctx.storage.sql.exec(sql)
342
+ });
343
+ }
344
+ runRowsForPrefix(ownerPath) {
345
+ return _classPrivateFieldGet2(_host, this).sql`
346
+ SELECT owner_path, owner_path_key, run_id, created_at
347
+ FROM cf_agents_facet_runs
348
+ `.filter((row) => {
349
+ try {
350
+ const rowOwnerPath = JSON.parse(row.owner_path);
351
+ return _classPrivateFieldGet2(_host, this)._isSameAgentPathPrefix(ownerPath, rowOwnerPath);
352
+ } catch {
353
+ return false;
354
+ }
355
+ });
356
+ }
357
+ deleteRunRowsForPrefix(ownerPath) {
358
+ for (const row of this.runRowsForPrefix(ownerPath)) _classPrivateFieldGet2(_host, this).sql`
359
+ DELETE FROM cf_agents_facet_runs
360
+ WHERE owner_path_key = ${row.owner_path_key}
361
+ AND run_id = ${row.run_id}
362
+ `;
363
+ }
364
+ lifecycleRouteAddress() {
365
+ if (!_classPrivateFieldGet2(_host, this)._isFacet) return void 0;
366
+ const key = agentPathKey(_classPrivateFieldGet2(_host, this).selfPath);
367
+ return key ? {
368
+ key,
369
+ data: JSON.stringify(_classPrivateFieldGet2(_host, this).selfPath)
370
+ } : void 0;
371
+ }
372
+ async routeLifecycleToRoot(envelope) {
373
+ if (!_classPrivateFieldGet2(_host, this)._isFacet) return _classPrivateFieldGet2(_host, this).lifecycle.route(envelope);
374
+ return (await this.rootAlarmOwner())._cf_routeLifecycle(void 0, envelope);
375
+ }
376
+ async routeLifecycleToTarget(target, envelope) {
377
+ let targetPath;
378
+ try {
379
+ targetPath = JSON.parse(target.data);
380
+ } catch {
381
+ throw new Error("Lifecycle route target is not a valid Agent path");
382
+ }
383
+ const selfPath = _classPrivateFieldGet2(_host, this).selfPath;
384
+ if (!_classPrivateFieldGet2(_host, this)._isSameAgentPathPrefix(selfPath, targetPath)) throw new Error(`Lifecycle route does not descend from ${JSON.stringify(selfPath)}.`);
385
+ if (selfPath.length === targetPath.length) return _classPrivateFieldGet2(_host, this).lifecycle.route(envelope);
386
+ const next = targetPath[selfPath.length];
387
+ if (!_classPrivateFieldGet2(_host, this).hasSubAgent(next.className, next.name)) {
388
+ const stalePath = targetPath.slice(0, selfPath.length + 1);
389
+ if (_classPrivateFieldGet2(_host, this)._isFacet) await (await this.rootAlarmOwner())._cf_cleanupFacetPrefix(stalePath);
390
+ else await _classPrivateFieldGet2(_host, this)._cf_cleanupFacetPrefix(stalePath);
391
+ return false;
392
+ }
393
+ return (await _classPrivateFieldGet2(_host, this)._cf_resolveSubAgent(next.className, next.name))._cf_routeLifecycle(target, envelope);
394
+ }
395
+ /** Body of the single native-RPC aperture for routed Lifecycle capabilities. */
396
+ routeLifecycle(target, envelope) {
397
+ return target ? this.routeLifecycleToTarget(target, envelope) : _classPrivateFieldGet2(_host, this).lifecycle.route(envelope);
398
+ }
399
+ async rootAlarmOwner() {
400
+ const root = _classPrivateFieldGet2(_host, this)._parentPath[0];
401
+ if (!root) throw new Error("Facet routing requires a root parent.");
402
+ const binding = _classPrivateFieldGet2(_host, this).ctx.exports?.[root.className];
403
+ if (!binding) throw new Error(`Unable to resolve root "${root.className}" for facet routing.`);
404
+ return await getAgentByName(binding, root.name);
405
+ }
406
+ rootResolvesToSelf() {
407
+ const root = _classPrivateFieldGet2(_host, this)._parentPath[0];
408
+ if (!root) return false;
409
+ const binding = _classPrivateFieldGet2(_host, this).ctx.exports?.[root.className];
410
+ if (!binding?.idFromName) return false;
411
+ return binding.idFromName(root.name).equals(_classPrivateFieldGet2(_host, this).ctx.id);
412
+ }
413
+ /**
414
+ * Clean root-owned bookkeeping for a sub-tree of facets: bulk-cancel
415
+ * schedules and routed Task wake mirrors under the owner-path prefix,
416
+ * and delete root-side facet fiber recovery leases for the same sub-tree.
417
+ */
418
+ async cleanupPrefix(ownerPath) {
419
+ const prefix = agentPathKey(ownerPath);
420
+ if (prefix) {
421
+ await _classPrivateFieldGet2(_host, this).scheduler.__DO_NOT_USE_WILL_BREAK__cleanupRoutePrefix(prefix);
422
+ await _classPrivateFieldGet2(_host, this).tasks.__DO_NOT_USE_WILL_BREAK__cleanupRoutePrefix(prefix);
423
+ }
424
+ this.deleteRunRowsForPrefix(ownerPath);
425
+ await _classPrivateFieldGet2(_host, this)._syncHostJobs();
426
+ }
427
+ /**
428
+ * Acquire a root-owned keepAlive ref on behalf of a descendant facet.
429
+ */
430
+ async acquireKeepAlive(ownerPath) {
431
+ const token = `${agentPathKey(ownerPath) ?? "unknown"}:${nanoid(9)}`;
432
+ _classPrivateFieldGet2(_facetKeepAliveTokens, this).add(token);
433
+ _classPrivateFieldGet2(_host, this)._keepAliveRefs++;
434
+ if (_classPrivateFieldGet2(_host, this)._keepAliveRefs === 1) await _classPrivateFieldGet2(_host, this)._syncHostJobs();
435
+ return token;
436
+ }
437
+ /**
438
+ * Release a root-owned keepAlive ref previously acquired for a facet.
439
+ * Idempotent so disposer calls can safely race or run twice.
440
+ */
441
+ async releaseKeepAlive(token) {
442
+ if (!_classPrivateFieldGet2(_facetKeepAliveTokens, this).delete(token)) return;
443
+ _classPrivateFieldGet2(_host, this)._keepAliveRefs = Math.max(0, _classPrivateFieldGet2(_host, this)._keepAliveRefs - 1);
444
+ await _classPrivateFieldGet2(_host, this)._syncHostJobs();
445
+ }
446
+ /**
447
+ * Register a facet's durable run row in the root-side index so root
448
+ * alarm housekeeping can dispatch recovery checks into idle facets.
449
+ */
450
+ async registerRun(ownerPath, runId) {
451
+ const ownerPathJson = JSON.stringify(ownerPath);
452
+ const ownerPathKey = agentPathKey(ownerPath);
453
+ if (!ownerPathKey) throw new Error("_cf_registerFacetRun requires a non-empty owner path.");
454
+ _classPrivateFieldGet2(_host, this).sql`
455
+ INSERT OR REPLACE INTO cf_agents_facet_runs
456
+ (owner_path, owner_path_key, run_id, created_at)
457
+ VALUES
458
+ (${ownerPathJson}, ${ownerPathKey}, ${runId}, ${Date.now()})
459
+ `;
460
+ await _classPrivateFieldGet2(_host, this)._syncHostJobs();
461
+ }
462
+ /**
463
+ * Root-side scan for durable fibers owned by descendant facets.
464
+ * `cf_agents_facet_runs` is only an index; actual snapshots and
465
+ * recovery hooks live in each facet's own `cf_agents_runs` table.
466
+ */
467
+ async checkRunFibers() {
468
+ if (_classPrivateFieldGet2(_host, this)._parentPath.length > 0) return;
469
+ const rows = _classPrivateFieldGet2(_host, this).sql`
470
+ SELECT owner_path, owner_path_key, run_id, created_at
471
+ FROM cf_agents_facet_runs
472
+ ORDER BY created_at ASC
473
+ `;
474
+ const firstRowByOwner = /* @__PURE__ */ new Map();
475
+ for (const row of rows) if (!firstRowByOwner.has(row.owner_path_key)) firstRowByOwner.set(row.owner_path_key, row);
476
+ for (const row of firstRowByOwner.values()) {
477
+ let ownerPath;
478
+ try {
479
+ ownerPath = JSON.parse(row.owner_path);
480
+ } catch (e) {
481
+ console.warn(`[Agent] Corrupted facet fiber owner path for ${row.owner_path_key}; pruning stale lease.`, e);
482
+ _classPrivateFieldGet2(_host, this).sql`
483
+ DELETE FROM cf_agents_facet_runs
484
+ WHERE owner_path_key = ${row.owner_path_key}
485
+ `;
486
+ continue;
487
+ }
488
+ try {
489
+ if (await _classPrivateFieldGet2(_host, this)._cf_checkRunFibersForFacet(ownerPath) === 0) _classPrivateFieldGet2(_host, this).sql`
490
+ DELETE FROM cf_agents_facet_runs
491
+ WHERE owner_path_key = ${row.owner_path_key}
492
+ `;
493
+ } catch (e) {
494
+ console.error(`[Agent] Facet fiber recovery check failed for ${row.owner_path_key}:`, e);
495
+ }
496
+ }
497
+ }
498
+ /**
499
+ * Dispatch a runFiber recovery check into the facet identified by
500
+ * `ownerPath`. Returns the number of remaining local `cf_agents_runs`
501
+ * rows on the target facet after recovery.
502
+ */
503
+ async checkRunFibersAtPath(ownerPath) {
504
+ const selfPath = _classPrivateFieldGet2(_host, this).selfPath;
505
+ if (!_classPrivateFieldGet2(_host, this)._isSameAgentPathPrefix(selfPath, ownerPath)) throw new Error(`Facet fiber owner path does not descend from ${JSON.stringify(selfPath)}.`);
506
+ if (selfPath.length === ownerPath.length) {
507
+ await _classPrivateFieldGet2(_host, this)._checkRunFibers();
508
+ return _classPrivateFieldGet2(_host, this).sql`
509
+ SELECT COUNT(*) as count FROM cf_agents_runs
510
+ `[0]?.count ?? 0;
511
+ }
512
+ const next = ownerPath[selfPath.length];
513
+ if (!_classPrivateFieldGet2(_host, this).hasSubAgent(next.className, next.name)) return 0;
514
+ return (await this.resolve(next.className, next.name))._cf_checkRunFibersForFacet(ownerPath);
515
+ }
516
+ /**
517
+ * Invoke an RPC method on the host Agent or a descendant facet
518
+ * identified by a root-first path. Used by AgentWorkflow to route
519
+ * callbacks and `this.agent` calls back to the exact sub-agent that
520
+ * started a workflow.
521
+ */
522
+ async invokeAgentPath(targetPath, method, args) {
523
+ await _classPrivateFieldGet2(_host, this).__unsafe_ensureInitialized();
524
+ const selfPath = _classPrivateFieldGet2(_host, this).selfPath;
525
+ if (!_classPrivateFieldGet2(_host, this)._isSameAgentPathPrefix(selfPath, targetPath)) throw new Error(`Workflow origin path does not descend from ${JSON.stringify(selfPath)}.`);
526
+ if (selfPath.length === targetPath.length) {
527
+ const fn = _classPrivateFieldGet2(_host, this)[method];
528
+ if (isInternalJsStubProp(method) || method in Object.prototype || typeof fn !== "function") throw new Error(`Workflow origin method "${method}" is not callable on ${_classPrivateFieldGet2(_host, this).constructor.name}.`);
529
+ return await fn.apply(_classPrivateFieldGet2(_host, this), args);
530
+ }
531
+ const next = targetPath[selfPath.length];
532
+ if (!_classPrivateFieldGet2(_host, this).hasSubAgent(next.className, next.name)) throw new Error(`Workflow origin sub-agent ${next.className} "${next.name}" no longer exists.`);
533
+ return await (await this.resolve(next.className, next.name))._cf_invokeAgentPath(targetPath, method, args);
534
+ }
535
+ /**
536
+ * Recursively destroy a descendant facet identified by `targetPath`.
537
+ * Walks down from `selfPath` until reaching the target's immediate
538
+ * parent, where it cancels the target's parent-owned schedules (and
539
+ * any descendants), removes the target from the registry, and calls
540
+ * `ctx.facets.delete` to wipe the target's storage.
541
+ */
542
+ async destroyDescendant(targetPath) {
543
+ const selfPath = _classPrivateFieldGet2(_host, this).selfPath;
544
+ if (targetPath.length === 0) throw new Error("_cf_destroyDescendantFacet: target path must not be empty.");
545
+ if (selfPath.length >= targetPath.length) throw new Error("_cf_destroyDescendantFacet: target must be a strict descendant.");
546
+ if (!_classPrivateFieldGet2(_host, this)._isSameAgentPathPrefix(selfPath, targetPath)) throw new Error("_cf_destroyDescendantFacet: target path does not descend from this agent.");
547
+ if (_classPrivateFieldGet2(_host, this)._parentPath.length === 0) await _classPrivateFieldGet2(_host, this)._cf_cleanupFacetPrefix(targetPath);
548
+ if (selfPath.length === targetPath.length - 1) {
549
+ const target = targetPath[targetPath.length - 1];
550
+ const ctx = _classPrivateFieldGet2(_host, this).ctx;
551
+ if (!ctx.facets) throw new Error("destroy() (delegated from facet) is not supported in this runtime — `ctx.facets` is unavailable. Update to the latest `compatibility_date` in your wrangler.jsonc.");
552
+ try {
553
+ ctx.facets.delete(`${target.className}\0${target.name}`);
554
+ } catch {}
555
+ this.registry.forget(target.className, target.name);
556
+ return;
557
+ }
558
+ const next = targetPath[selfPath.length];
559
+ if (!_classPrivateFieldGet2(_host, this).hasSubAgent(next.className, next.name)) return;
560
+ await (await this.resolve(next.className, next.name))._cf_destroyDescendantFacet(targetPath);
561
+ }
562
+ /**
563
+ * Shared facet resolution — takes a CamelCase class name string
564
+ * (matching `ctx.exports`) rather than a class reference. Both
565
+ * `subAgent(cls, name)` and `_cf_invokeSubAgent(className, ...)`
566
+ * funnel through here so registry bookkeeping and the
567
+ * `_cf_initAsFacet` handshake are consistent.
568
+ */
569
+ async resolve(className, name) {
570
+ const ctx = _classPrivateFieldGet2(_host, this).ctx;
571
+ if (!ctx.facets || !ctx.exports) throw new Error("subAgent() is not supported in this runtime — `ctx.facets` / `ctx.exports` are unavailable. Update to the latest `compatibility_date` in your wrangler.jsonc.");
572
+ if (camelCaseToKebabCase(className) === "sub") throw new Error(`Sub-agent class name "${className}" kebab-cases to "sub", which collides with the reserved URL separator — rename the class (e.g. "SubThing" or "Subtask").`);
573
+ const Cls = ctx.exports[className];
574
+ if (!Cls) throw new Error(`Sub-agent class "${className}" not found in worker exports. Make sure the class is exported from your worker entry point and that the export name matches the class name.`);
575
+ if (name.includes("\0")) throw new Error(`Sub-agent name contains null character (\\0), which is reserved.`);
576
+ const facetKey = `${className}\0${name}`;
577
+ const childParentPath = _classPrivateFieldGet2(_host, this).selfPath;
578
+ const childPath = [...childParentPath, {
579
+ className,
580
+ name
581
+ }];
582
+ const rootClassName = _classPrivateFieldGet2(_host, this)._parentPath[0]?.className ?? _classPrivateFieldGet2(_host, this).constructor.name;
583
+ const rootNs = ctx.exports[rootClassName];
584
+ if (!rootNs?.idFromName) {
585
+ const minificationHint = /^_*[a-z][a-z0-9]{0,2}$/.test(rootClassName) ? ` The class name "${rootClassName}" looks minified — make sure your bundler preserves class names (e.g. esbuild's \`keepNames: true\`).` : "";
586
+ throw new Error(`Sub-agent bootstrap requires the root agent class "${rootClassName}" to be available as a Durable Object namespace, but ctx.exports["${rootClassName}"] is missing or doesn't expose idFromName.${minificationHint} Make sure the root agent class is exported under that class name and registered in your wrangler.jsonc durable_objects.bindings.`);
587
+ }
588
+ const identity = await this.registry.identity(className, name, childPath);
589
+ const facetId = rootNs.idFromName(identity.name);
590
+ const stub = ctx.facets.get(facetKey, () => ({
591
+ class: Cls,
592
+ id: facetId
593
+ }));
594
+ this.registry.record(className, name, identity);
595
+ try {
596
+ await _classPrivateFieldGet2(_host, this)._runFacetInitInvocation(async () => {
597
+ await stub._cf_initAsFacet(name, childParentPath, identity.name);
598
+ });
599
+ } catch (error) {
600
+ if (!identity.existing) this.registry.forget(className, name);
601
+ throw error;
602
+ }
603
+ return stub;
604
+ }
605
+ /**
606
+ * Forcefully abort a running facet. Transitively aborts the child's
607
+ * own children; storage is preserved.
608
+ */
609
+ abort(className, name, reason) {
610
+ const ctx = _classPrivateFieldGet2(_host, this).ctx;
611
+ if (!ctx.facets) throw new Error("abort() is not supported in this runtime — `ctx.facets` is unavailable. Update to the latest `compatibility_date` in your wrangler.jsonc.");
612
+ const facetKey = `${className}\0${name}`;
613
+ ctx.facets.abort(facetKey, reason);
614
+ }
615
+ /**
616
+ * Delete a facet: abort it if running, then permanently wipe its
617
+ * storage. Transitively deletes the child's own children.
618
+ */
619
+ async delete(className, name) {
620
+ const ctx = _classPrivateFieldGet2(_host, this).ctx;
621
+ if (!ctx.facets) throw new Error("delete() is not supported in this runtime — `ctx.facets` is unavailable. Update to the latest `compatibility_date` in your wrangler.jsonc.");
622
+ const facetKey = `${className}\0${name}`;
623
+ const childPath = [..._classPrivateFieldGet2(_host, this).selfPath, {
624
+ className,
625
+ name
626
+ }];
627
+ if (_classPrivateFieldGet2(_host, this)._isFacet) await (await this.rootAlarmOwner())._cf_cleanupFacetPrefix(childPath);
628
+ else await _classPrivateFieldGet2(_host, this)._cf_cleanupFacetPrefix(childPath);
629
+ try {
630
+ ctx.facets.delete(facetKey);
631
+ } catch {}
632
+ this.registry.forget(className, name);
633
+ }
634
+ /** Drop all facet-side virtual connections (test/rehydration hook). */
635
+ clearVirtualConnections() {
636
+ _classPrivateFieldGet2(_virtualConnections, this).clear();
637
+ }
638
+ /** Facet-side lookup of a virtual connection by id. */
639
+ getVirtualConnection(id) {
640
+ const stored = _classPrivateFieldGet2(_virtualConnections, this).get(id);
641
+ if (!stored) return void 0;
642
+ return this.createBridgeConnection(stored.meta);
643
+ }
644
+ /** Facet-side iteration over virtual connections, optionally by tag. */
645
+ *getVirtualConnections(tag) {
646
+ for (const stored of _classPrivateFieldGet2(_virtualConnections, this).values()) if (!tag || stored.meta.tags.includes(tag)) yield this.createBridgeConnection(stored.meta);
647
+ }
648
+ activeBridge(connectionId) {
649
+ const context = _classPrivateFieldGet2(_bridgeContext, this).getStore();
650
+ if (connectionId !== void 0 && context?.connectionId !== connectionId) return;
651
+ return context?.bridge;
652
+ }
653
+ /**
654
+ * Route a virtual sub-agent connection operation through its live frame
655
+ * bridge, or through the durable root Agent after that frame completes.
656
+ * All operations share one per-connection queue. Facet broadcasts wait for
657
+ * older queued operations; failures do not block later work.
658
+ */
659
+ routeConnectionOperation(connectionId, operationName, operation) {
660
+ const activeBridge = this.activeBridge(connectionId);
661
+ const previousConnectionOperation = _classPrivateFieldGet2(_connectionOperationTails, this).get(connectionId);
662
+ let pending;
663
+ if (activeBridge && !previousConnectionOperation) try {
664
+ pending = Promise.resolve(operation(activeBridge)).then(() => {});
665
+ } catch (error) {
666
+ pending = Promise.reject(error);
667
+ }
668
+ else pending = (previousConnectionOperation ?? Promise.resolve()).then(async () => {
669
+ await operation(new RootDynamicAgentConnectionBridge(await this.rootAlarmOwner(), connectionId));
670
+ });
671
+ const completion = pending.catch((error) => {
672
+ _assertClassBrand(_DynamicAgentsInternal_brand, this, _reportConnectionOperationFailure).call(this, connectionId, operationName, error);
673
+ });
674
+ _classPrivateFieldGet2(_connectionOperationTails, this).set(connectionId, completion);
675
+ _classPrivateFieldGet2(_host, this).ctx.waitUntil(completion);
676
+ completion.then(() => {
677
+ if (_classPrivateFieldGet2(_connectionOperationTails, this).get(connectionId) === completion) _classPrivateFieldGet2(_connectionOperationTails, this).delete(connectionId);
678
+ });
679
+ }
680
+ /**
681
+ * Route a facet broadcast after every older connection operation.
682
+ *
683
+ * This barrier is intentionally one-way: facet startup can broadcast before
684
+ * a child connection has finished initializing its tags and protocol flags.
685
+ * Making those later connection operations wait would let the next frame
686
+ * observe stale root-owned metadata.
687
+ */
688
+ async routeBroadcast(ownerPath, message, without, upstreamBridge) {
689
+ const activeBridge = upstreamBridge ?? this.activeBridge();
690
+ const previousOperations = /* @__PURE__ */ new Set([..._classPrivateFieldGet2(_broadcastOperationTail, this) ? [_classPrivateFieldGet2(_broadcastOperationTail, this)] : [], ..._classPrivateFieldGet2(_connectionOperationTails, this).values()]);
691
+ let pending;
692
+ if (activeBridge && previousOperations.size === 0) try {
693
+ pending = Promise.resolve(activeBridge.broadcast(ownerPath, message, without));
694
+ } catch (error) {
695
+ pending = Promise.reject(error);
696
+ }
697
+ else pending = Promise.all(previousOperations).then(async () => {
698
+ await (await this.rootAlarmOwner())._cf_broadcastToSubAgent(ownerPath, message, without);
699
+ });
700
+ const completion = pending.catch((error) => {
701
+ console.error("[Agent] Sub-agent broadcast operation failed:", {
702
+ operation: "broadcast",
703
+ error
704
+ });
705
+ });
706
+ _classPrivateFieldSet2(_broadcastOperationTail, this, completion);
707
+ _classPrivateFieldGet2(_host, this).ctx.waitUntil(completion);
708
+ completion.then(() => {
709
+ if (_classPrivateFieldGet2(_broadcastOperationTail, this) === completion) _classPrivateFieldSet2(_broadcastOperationTail, this, void 0);
710
+ });
711
+ await completion;
712
+ }
713
+ async broadcastToParent(message, without) {
714
+ await this.routeBroadcast(_classPrivateFieldGet2(_host, this).selfPath, message, without);
715
+ }
716
+ async broadcastToPath(ownerPath, message, without) {
717
+ if (_classPrivateFieldGet2(_host, this)._isFacet) {
718
+ await this.routeBroadcast(ownerPath, message, without);
719
+ return;
720
+ }
721
+ for (const connection of _classPrivateFieldGet2(_host, this)._webSockets.getConnections()) {
722
+ if (without?.includes(connection.id)) continue;
723
+ const targetPath = this.connectionTargetPath(connection);
724
+ if (!targetPath) continue;
725
+ if (!this.isSameAgentPath(targetPath, ownerPath)) continue;
726
+ connection.send(message);
727
+ }
728
+ }
729
+ async connectionMetas(ownerPath) {
730
+ const metas = [];
731
+ for (const connection of _classPrivateFieldGet2(_host, this)._webSockets.getConnections()) {
732
+ const meta = _assertClassBrand(_DynamicAgentsInternal_brand, this, _connectionMetaForPath).call(this, connection, ownerPath);
733
+ if (meta) metas.push(meta);
734
+ }
735
+ return metas;
736
+ }
737
+ async sendToConnection(connectionId, message) {
738
+ const connection = _classPrivateFieldGet2(_host, this)._webSockets.getConnection(connectionId);
739
+ if (!connection || !this.connectionHasChildTarget(connection)) return;
740
+ connection.send(message);
741
+ }
742
+ async closeConnection(connectionId, code, reason) {
743
+ const connection = _classPrivateFieldGet2(_host, this)._webSockets.getConnection(connectionId);
744
+ if (!connection || !this.connectionHasChildTarget(connection)) return;
745
+ connection.close(code, reason);
746
+ }
747
+ async setConnectionState(connectionId, state) {
748
+ const connection = _classPrivateFieldGet2(_host, this)._webSockets.getConnection(connectionId);
749
+ if (!connection || !this.connectionHasChildTarget(connection)) return null;
750
+ _classPrivateFieldGet2(_host, this)._ensureConnectionWrapped(connection);
751
+ connection.setState(state);
752
+ return this.getForwardedState(connection);
753
+ }
754
+ connectionTargetPath(connection) {
755
+ _classPrivateFieldGet2(_host, this)._ensureConnectionWrapped(connection);
756
+ const outerUri = _classPrivateFieldGet2(_host, this)._unsafe_getConnectionFlag(connection, CF_SUB_AGENT_OUTER_URL_KEY);
757
+ if (typeof outerUri !== "string") return null;
758
+ return _assertClassBrand(_DynamicAgentsInternal_brand, this, _pathFromOuterUri).call(this, outerUri)?.path ?? null;
759
+ }
760
+ isSameAgentPath(a, b) {
761
+ if (a.length !== b.length) return false;
762
+ return a.every((step, index) => step.className === b[index]?.className && step.name === b[index]?.name);
763
+ }
764
+ connectionHasChildTarget(connection) {
765
+ _classPrivateFieldGet2(_host, this)._ensureConnectionWrapped(connection);
766
+ return typeof _classPrivateFieldGet2(_host, this)._unsafe_getConnectionFlag(connection, CF_SUB_AGENT_OUTER_URL_KEY) === "string";
767
+ }
768
+ connectionTargetsChild(connection) {
769
+ if (!connection.uri) return false;
770
+ const ctx = _classPrivateFieldGet2(_host, this).ctx;
771
+ return parseSubAgentPath(connection.uri, { knownClasses: ctx.exports ? Object.keys(ctx.exports) : void 0 }) !== null;
772
+ }
773
+ requestTargetsChild(request) {
774
+ const ctx = _classPrivateFieldGet2(_host, this).ctx;
775
+ return parseSubAgentPath(request.url, { knownClasses: ctx.exports ? Object.keys(ctx.exports) : void 0 }) !== null;
776
+ }
777
+ async forwardWebSocketConnect(connection, request, options) {
778
+ const routed = await _assertClassBrand(_DynamicAgentsInternal_brand, this, _resolveConnection).call(this, connection, request, options);
779
+ if (!routed) return false;
780
+ await routed.child._cf_handleSubAgentWebSocketConnect(_assertClassBrand(_DynamicAgentsInternal_brand, this, _createConnectionBridge).call(this, connection), routed.meta);
781
+ return true;
782
+ }
783
+ async forwardWebSocketMessage(connection, message, replyBridge) {
784
+ const routed = await _assertClassBrand(_DynamicAgentsInternal_brand, this, _resolveConnection).call(this, connection);
785
+ if (!routed) return false;
786
+ const bridge = _assertClassBrand(_DynamicAgentsInternal_brand, this, _createConnectionBridge).call(this, connection);
787
+ await routed.child._cf_handleSubAgentWebSocketMessage(message, bridge, routed.meta, replyBridge ?? bridge);
788
+ return true;
789
+ }
790
+ async forwardWebSocketClose(connection, code, reason, wasClean) {
791
+ const routed = await _assertClassBrand(_DynamicAgentsInternal_brand, this, _resolveConnection).call(this, connection);
792
+ if (!routed) return false;
793
+ await routed.child._cf_handleSubAgentWebSocketClose(code, reason, wasClean, _assertClassBrand(_DynamicAgentsInternal_brand, this, _createConnectionBridge).call(this, connection), routed.meta);
794
+ return true;
795
+ }
796
+ async handleWebSocketConnect(bridge, meta) {
797
+ await this.runWithBridge(bridge, meta.id, async () => {
798
+ const connection = this.createBridgeConnection(meta);
799
+ const request = new Request(meta.uri ?? "http://placeholder/", { headers: meta.requestHeaders });
800
+ if (await this.forwardWebSocketConnect(connection, request, { gate: true })) return;
801
+ if (_classPrivateFieldGet2(_host, this).shouldConnectionBeReadonly(connection, { request })) _classPrivateFieldGet2(_host, this).setConnectionReadonly(connection, true);
802
+ if (!_classPrivateFieldGet2(_host, this).shouldSendProtocolMessages(connection, { request })) _classPrivateFieldGet2(_host, this)._setConnectionNoProtocol(connection);
803
+ const childTags = await _classPrivateFieldGet2(_host, this).getConnectionTags(connection, { request });
804
+ connection.tags = [connection.id, ...childTags.filter((tag) => tag !== connection.id)];
805
+ this.storeVirtualConnection(connection);
806
+ await _classPrivateFieldGet2(_host, this).onConnect(connection, { request });
807
+ this.storeVirtualConnection(connection);
808
+ await _classPrivateFieldGet2(_connectionOperationTails, this).get(meta.id);
809
+ });
810
+ }
811
+ async handleWebSocketMessage(message, bridge, meta, replyBridge = bridge) {
812
+ const connection = this.createBridgeConnection(meta);
813
+ this.storeVirtualConnection(connection);
814
+ const replyContext = { bridge: replyBridge };
815
+ try {
816
+ await dynamicAgentRpcReplyContext.run(replyContext, () => this.runWithBridge(bridge, meta.id, () => _classPrivateFieldGet2(_host, this).onMessage(connection, message)));
817
+ } finally {
818
+ replyContext.bridge = void 0;
819
+ }
820
+ }
821
+ async handleWebSocketClose(code, reason, wasClean, bridge, meta) {
822
+ const connection = this.createBridgeConnection(meta);
823
+ this.storeVirtualConnection(connection);
824
+ await this.runWithBridge(bridge, meta.id, () => _classPrivateFieldGet2(_host, this).onClose(connection, code, reason, wasClean));
825
+ _classPrivateFieldGet2(_virtualConnections, this).delete(meta.id);
826
+ }
827
+ async runWithBridge(bridge, connectionId, fn) {
828
+ const context = {
829
+ bridge,
830
+ connectionId
831
+ };
832
+ try {
833
+ return await _classPrivateFieldGet2(_bridgeContext, this).run(context, fn);
834
+ } finally {
835
+ context.bridge = void 0;
836
+ }
837
+ }
838
+ createBridgeConnection(meta) {
839
+ let stored = _classPrivateFieldGet2(_virtualConnections, this).get(meta.id);
840
+ if (stored) {
841
+ stored.meta = meta;
842
+ if (stored.connection) {
843
+ stored.connection.uri = meta.uri;
844
+ stored.connection.tags = meta.tags;
845
+ return stored.connection;
846
+ }
847
+ } else {
848
+ stored = { meta };
849
+ _classPrivateFieldGet2(_virtualConnections, this).set(meta.id, stored);
850
+ }
851
+ const owner = this;
852
+ const getStored = () => _classPrivateFieldGet2(_virtualConnections, this).get(meta.id) ?? stored;
853
+ const updateStoredState = (nextState) => {
854
+ const current = _classPrivateFieldGet2(_virtualConnections, this).get(meta.id);
855
+ if (current) current.meta = {
856
+ ...current.meta,
857
+ state: nextState
858
+ };
859
+ };
860
+ const connection = {
861
+ id: meta.id,
862
+ uri: meta.uri,
863
+ tags: meta.tags,
864
+ get state() {
865
+ return getStored().meta.state;
866
+ },
867
+ setState(next) {
868
+ const currentState = getStored().meta.state;
869
+ const state = typeof next === "function" ? next(currentState) : next;
870
+ updateStoredState(state);
871
+ owner.routeConnectionOperation(meta.id, "setState", (bridge) => bridge.setState(state));
872
+ return state;
873
+ },
874
+ send(message) {
875
+ owner.routeConnectionOperation(meta.id, "send", (bridge) => bridge.send(message));
876
+ },
877
+ close(code, reason) {
878
+ owner.routeConnectionOperation(meta.id, "close", (bridge) => bridge.close(code, reason));
879
+ },
880
+ addEventListener() {},
881
+ removeEventListener() {}
882
+ };
883
+ stored.connection = connection;
884
+ _classPrivateFieldGet2(_host, this)._ensureConnectionWrapped(connection);
885
+ return connection;
886
+ }
887
+ storeVirtualConnection(connection) {
888
+ _classPrivateFieldGet2(_host, this)._unsafe_setConnectionFlag(connection, CF_SUB_AGENT_TAGS_KEY, [...connection.tags]);
889
+ const stored = _classPrivateFieldGet2(_virtualConnections, this).get(connection.id);
890
+ _classPrivateFieldGet2(_virtualConnections, this).set(connection.id, {
891
+ meta: {
892
+ id: connection.id,
893
+ uri: connection.uri,
894
+ tags: [...connection.tags],
895
+ state: this.getRawConnectionState(connection)
896
+ },
897
+ connection: stored?.connection ?? connection
898
+ });
899
+ }
900
+ /**
901
+ * Restore the facet identity persisted by `init` (wake after
902
+ * hibernation), then best-effort hydrate the virtual connections
903
+ * from the root's WebSocket state.
904
+ */
905
+ async restoreFacetContext() {
906
+ if (await _classPrivateFieldGet2(_host, this).ctx.storage.get("cf_agents_is_facet")) _classPrivateFieldGet2(_host, this)._isFacet = true;
907
+ const storedFacetName = await _classPrivateFieldGet2(_host, this).ctx.storage.get("cf_agents_facet_name");
908
+ if (typeof storedFacetName === "string") _classPrivateFieldGet2(_host, this)._facetName = storedFacetName;
909
+ const storedParentPath = await _classPrivateFieldGet2(_host, this).ctx.storage.get("cf_agents_parent_path");
910
+ if (isValidParentPath(storedParentPath)) _classPrivateFieldGet2(_host, this)._parentPath = storedParentPath;
911
+ try {
912
+ await this.hydrateConnectionsFromRoot();
913
+ } catch (error) {
914
+ console.warn("[Agent] Unable to hydrate sub-agent WebSocket connections:", error);
915
+ }
916
+ }
917
+ async hydrateConnectionsFromRoot() {
918
+ if (!_classPrivateFieldGet2(_host, this)._isFacet || _classPrivateFieldGet2(_host, this)._parentPath.length === 0) return;
919
+ if (this.rootResolvesToSelf()) return;
920
+ const metas = await (await this.rootAlarmOwner())._cf_subAgentConnectionMetas(_classPrivateFieldGet2(_host, this).selfPath);
921
+ for (const meta of metas) _classPrivateFieldGet2(_virtualConnections, this).set(meta.id, { meta });
922
+ }
923
+ getRawConnectionState(connection) {
924
+ _classPrivateFieldGet2(_host, this)._ensureConnectionWrapped(connection);
925
+ return _classPrivateFieldGet2(_host, this)._rawStateAccessors.get(connection)?.getRaw() ?? null;
926
+ }
927
+ getForwardedState(connection) {
928
+ const raw = this.getRawConnectionState(connection);
929
+ if (raw == null || typeof raw !== "object") return raw;
930
+ const { [CF_SUB_AGENT_OUTER_URL_KEY]: _, ...rest } = raw;
931
+ return Object.keys(rest).length > 0 ? rest : null;
932
+ }
933
+ /**
934
+ * Resolve the facet Fetcher for the match and forward the request to
935
+ * it with `/sub/{class}/{name}` stripped.
936
+ */
937
+ async forward(req, match) {
938
+ let fetcher;
939
+ try {
940
+ fetcher = await this.resolve(match.childClass, match.childName);
941
+ } catch (err) {
942
+ const message = err instanceof Error ? err.message : String(err);
943
+ console.error("[agents] sub-agent route failed:", message);
944
+ if (/null character/i.test(message) || /reserved/i.test(message)) return new Response("Bad Request", { status: 400 });
945
+ return new Response("Not Found", { status: 404 });
946
+ }
947
+ const rewritten = new URL(req.url);
948
+ rewritten.pathname = match.remainingPath;
949
+ const forwardedHeaders = new Headers(req.headers);
950
+ const forwardedInit = {
951
+ method: req.method,
952
+ headers: forwardedHeaders
953
+ };
954
+ if (req.headers.get("Upgrade")?.toLowerCase() === "websocket") forwardedHeaders.set(SUB_AGENT_OUTER_URL_HEADER, req.url);
955
+ if (req.body && req.method !== "GET" && req.method !== "HEAD") forwardedInit.body = req.body;
956
+ const forwarded = new Request(rewritten, forwardedInit);
957
+ return fetcher.fetch(forwarded);
958
+ }
959
+ /**
960
+ * Bridge used by `getSubAgentByName`: resolve the facet and dispatch
961
+ * one RPC method. Stateless — no cached references.
962
+ */
963
+ async invoke(className, name, method, args) {
964
+ const stub = await this.resolve(className, name);
965
+ return await this.invokeStubMethod(stub, className, method, args);
966
+ }
967
+ /**
968
+ * Bridge used by `parentAgent()` when the requested parent is itself
969
+ * a facet (and therefore has no top-level env namespace). The root
970
+ * receives the full root-first target path, then each hop delegates
971
+ * to the next facet using that facet's own `ctx.facets`.
972
+ */
973
+ async invokePath(path, method, args) {
974
+ const [self, next, ...rest] = path;
975
+ if (!self) throw new Error(`Sub-agent path invocation requires a non-empty path.`);
976
+ const ownClassName = _classPrivateFieldGet2(_host, this).constructor.name;
977
+ if (self.className !== ownClassName || self.name !== _classPrivateFieldGet2(_host, this).name) throw new Error(`Sub-agent path invocation reached ${ownClassName}("${_classPrivateFieldGet2(_host, this).name}") but expected ${self.className}("${self.name}").`);
978
+ if (!next) return await this.invokeStubMethod(_classPrivateFieldGet2(_host, this), ownClassName, method, args);
979
+ const child = await this.resolve(next.className, next.name);
980
+ if (rest.length === 0) return await this.invokeStubMethod(child, next.className, method, args);
981
+ return await child._cf_invokeSubAgentPath([next, ...rest], method, args);
982
+ }
983
+ async invokeStubMethod(stub, className, method, args) {
984
+ const handle = stub;
985
+ if (typeof handle[method] !== "function") throw new Error(`Method "${method}" not found on ${className}.`);
986
+ return await handle[method](...args);
987
+ }
988
+ /**
989
+ * Initialize the host agent as a facet in a single RPC. Runs entirely
990
+ * inside the child's isolate, so every storage write and `onStart()`
991
+ * I/O is owned by the child DO.
992
+ */
993
+ async init(name, parentPath = [], identityName = name) {
994
+ const routedName = _classPrivateFieldGet2(_host, this).lifecycle.name;
995
+ if (routedName !== identityName) throw new Error(`Facet bootstrap mismatch: expected routed identity "${identityName}" but got "${routedName}". This usually means the parent passed the wrong id to ctx.facets.get(). See _cf_resolveSubAgent.`);
996
+ _classPrivateFieldGet2(_host, this)._isFacet = true;
997
+ _classPrivateFieldGet2(_host, this)._facetName = name;
998
+ _classPrivateFieldGet2(_host, this)._parentPath = parentPath;
999
+ await Promise.all([
1000
+ _classPrivateFieldGet2(_host, this).ctx.storage.put("cf_agents_is_facet", true),
1001
+ _classPrivateFieldGet2(_host, this).ctx.storage.put("cf_agents_facet_name", name),
1002
+ _classPrivateFieldGet2(_host, this).ctx.storage.put("cf_agents_parent_path", parentPath)
1003
+ ]);
1004
+ await _classPrivateFieldGet2(_host, this).__unsafe_ensureInitialized();
1005
+ }
1006
+ /** Remove a completed facet fiber from the root-side index. */
1007
+ async unregisterRun(ownerPath, runId) {
1008
+ const ownerPathKey = agentPathKey(ownerPath);
1009
+ _classPrivateFieldGet2(_host, this).sql`
1010
+ DELETE FROM cf_agents_facet_runs
1011
+ WHERE owner_path_key IS ${ownerPathKey}
1012
+ AND run_id = ${runId}
1013
+ `;
1014
+ await _classPrivateFieldGet2(_host, this)._syncHostJobs();
1015
+ }
1016
+ };
1017
+ function _reportConnectionOperationFailure(connectionId, operation, error) {
1018
+ console.error("[Agent] Sub-agent connection operation failed:", {
1019
+ connectionId,
1020
+ operation,
1021
+ error
1022
+ });
1023
+ }
1024
+ function _connectionMetaForPath(connection, ownerPath) {
1025
+ _classPrivateFieldGet2(_host, this)._ensureConnectionWrapped(connection);
1026
+ const outerUri = _classPrivateFieldGet2(_host, this)._unsafe_getConnectionFlag(connection, CF_SUB_AGENT_OUTER_URL_KEY);
1027
+ if (typeof outerUri !== "string") return null;
1028
+ const target = _assertClassBrand(_DynamicAgentsInternal_brand, this, _pathFromOuterUri).call(this, outerUri, ownerPath);
1029
+ if (!target) return null;
1030
+ const raw = this.getRawConnectionState(connection);
1031
+ const rawTags = raw != null && typeof raw === "object" ? raw[CF_SUB_AGENT_TAGS_KEY] : void 0;
1032
+ const tags = Array.isArray(rawTags) ? rawTags.filter((tag) => typeof tag === "string") : [...connection.tags];
1033
+ return {
1034
+ id: connection.id,
1035
+ uri: target.uri,
1036
+ tags,
1037
+ state: this.getForwardedState(connection)
1038
+ };
1039
+ }
1040
+ function _pathFromOuterUri(outerUri, stopAt) {
1041
+ const ctx = _classPrivateFieldGet2(_host, this).ctx;
1042
+ const knownClasses = ctx.exports ? Object.keys(ctx.exports) : void 0;
1043
+ const path = [..._classPrivateFieldGet2(_host, this).selfPath];
1044
+ let currentUrl = outerUri;
1045
+ while (true) {
1046
+ const match = parseSubAgentPath(currentUrl, { knownClasses });
1047
+ if (!match) break;
1048
+ path.push({
1049
+ className: match.childClass,
1050
+ name: match.childName
1051
+ });
1052
+ const rewritten = new URL(currentUrl);
1053
+ rewritten.pathname = match.remainingPath;
1054
+ currentUrl = rewritten.toString();
1055
+ if (stopAt && this.isSameAgentPath(path, stopAt)) return {
1056
+ path,
1057
+ uri: currentUrl
1058
+ };
1059
+ }
1060
+ if (path.length === _classPrivateFieldGet2(_host, this).selfPath.length) return null;
1061
+ if (stopAt) return null;
1062
+ return {
1063
+ path,
1064
+ uri: currentUrl
1065
+ };
1066
+ }
1067
+ function _createConnectionBridge(connection) {
1068
+ const upstreamBroadcastBridge = _classPrivateFieldGet2(_host, this)._isFacet ? this.activeBridge(connection.id) : void 0;
1069
+ return new DynamicAgentConnectionBridge(connection, (ownerPath, message, without) => {
1070
+ if (upstreamBroadcastBridge) return this.routeBroadcast(ownerPath, message, without, upstreamBroadcastBridge);
1071
+ return _classPrivateFieldGet2(_host, this)._cf_broadcastToSubAgent(ownerPath, message, without);
1072
+ });
1073
+ }
1074
+ async function _resolveConnection(connection, request, options = { gate: false }) {
1075
+ _classPrivateFieldGet2(_host, this)._ensureConnectionWrapped(connection);
1076
+ const outerUri = _classPrivateFieldGet2(_host, this)._unsafe_getConnectionFlag(connection, CF_SUB_AGENT_OUTER_URL_KEY);
1077
+ const uri = typeof outerUri === "string" ? outerUri : connection.uri;
1078
+ if (!uri) return null;
1079
+ const ctx = _classPrivateFieldGet2(_host, this).ctx;
1080
+ let match = parseSubAgentPath(uri, { knownClasses: ctx.exports ? Object.keys(ctx.exports) : void 0 });
1081
+ if (!match) return null;
1082
+ if (_classPrivateFieldGet2(_host, this)._ParentClass.name === match.childClass && _classPrivateFieldGet2(_host, this).name === match.childName) {
1083
+ const tailUri = new URL(uri);
1084
+ tailUri.pathname = match.remainingPath;
1085
+ match = parseSubAgentPath(tailUri.toString(), { knownClasses: ctx.exports ? Object.keys(ctx.exports) : void 0 });
1086
+ if (!match) return null;
1087
+ }
1088
+ let forwardReq = request;
1089
+ if (request && options.gate) {
1090
+ const decision = await _classPrivateFieldGet2(_host, this).onBeforeSubAgent(request, {
1091
+ className: match.childClass,
1092
+ name: match.childName
1093
+ });
1094
+ if (decision instanceof Response) {
1095
+ connection.close(1008, "Sub-agent connection rejected");
1096
+ return null;
1097
+ }
1098
+ forwardReq = decision instanceof Request ? decision : request;
1099
+ }
1100
+ const child = await this.resolve(match.childClass, match.childName);
1101
+ const childUri = new URL(forwardReq?.url ?? uri);
1102
+ childUri.pathname = match.remainingPath;
1103
+ const raw = this.getRawConnectionState(connection);
1104
+ const rawTags = raw != null && typeof raw === "object" ? raw[CF_SUB_AGENT_TAGS_KEY] : void 0;
1105
+ const tags = Array.isArray(rawTags) ? rawTags.filter((tag) => typeof tag === "string") : [...connection.tags];
1106
+ return {
1107
+ child,
1108
+ meta: {
1109
+ id: connection.id,
1110
+ uri: childUri.toString(),
1111
+ tags,
1112
+ state: this.getForwardedState(connection),
1113
+ requestHeaders: forwardReq ? [...forwardReq.headers] : void 0
1114
+ }
1115
+ };
1116
+ }
1117
+ //#endregion
1118
+ //#region src/dynamic-agents/api.ts
1119
+ var _internal = /* @__PURE__ */ new WeakMap();
1120
+ /**
1121
+ * The public dynamic-agents capability surface, reached via
1122
+ * `this.dynamicAgents` on an Agent.
1123
+ *
1124
+ * A dynamic agent is a facet-backed child: it runs in its own isolate
1125
+ * with its own SQLite database, colocated with — and supervised by —
1126
+ * its parent Agent. Use dynamic agents for code whose class or
1127
+ * lifecycle the parent owns (dynamically-loaded/generated code,
1128
+ * per-run tool agents, sandboxed components). For independent peers
1129
+ * such as one-DO-per-chat, use `getAgentByName` instead.
1130
+ *
1131
+ * @experimental The API surface may change before stabilizing.
1132
+ */
1133
+ var DynamicAgents = class {
1134
+ /** @internal Constructed by Agent; do not instantiate directly. */
1135
+ constructor(internal) {
1136
+ _classPrivateFieldInitSpec(this, _internal, void 0);
1137
+ _classPrivateFieldSet2(_internal, this, internal);
1138
+ }
1139
+ /**
1140
+ * Get (creating or waking if needed) the dynamic agent of the given
1141
+ * class and name, as a typed RPC stub. Idempotent — repeated calls
1142
+ * with the same class and name return the same child.
1143
+ *
1144
+ * @experimental
1145
+ */
1146
+ async get(cls, name) {
1147
+ return await _classPrivateFieldGet2(_internal, this).resolve(cls.name, name);
1148
+ }
1149
+ /**
1150
+ * Forcefully abort a running dynamic agent. The child stops
1151
+ * executing immediately and restarts on the next {@link get} call;
1152
+ * its storage is preserved. Transitively aborts the child's own
1153
+ * children. Pending RPC calls receive the reason as an error.
1154
+ *
1155
+ * @experimental
1156
+ */
1157
+ abort(cls, name, reason) {
1158
+ _classPrivateFieldGet2(_internal, this).abort(cls.name, name, reason);
1159
+ }
1160
+ /**
1161
+ * Delete a dynamic agent: abort it if running, then permanently wipe
1162
+ * its storage. Transitively deletes the child's own children.
1163
+ *
1164
+ * @experimental
1165
+ */
1166
+ delete(cls, name) {
1167
+ return _classPrivateFieldGet2(_internal, this).delete(cls.name, name);
1168
+ }
1169
+ has(classOrName, name) {
1170
+ const className = typeof classOrName === "string" ? classOrName : classOrName.name;
1171
+ return _classPrivateFieldGet2(_internal, this).registry.has(className, name);
1172
+ }
1173
+ list(classOrName) {
1174
+ const className = typeof classOrName === "string" ? classOrName : classOrName?.name;
1175
+ return _classPrivateFieldGet2(_internal, this).registry.list(className);
1176
+ }
1177
+ };
1178
+ //#endregion
23
1179
  //#region src/observability/agent-span-attributes.ts
24
1180
  function agentSpanAttributes(input) {
25
1181
  return {
@@ -42,9 +1198,6 @@ let _Symbol$dispose;
42
1198
  function runInInvocation(store, body, options) {
43
1199
  return __DO_NOT_USE_WILL_BREAK__agentContext.run(store, () => withInvocationScope(body, options));
44
1200
  }
45
- function isClosedWebSocketSendError(error) {
46
- return error instanceof TypeError && error.message.includes("WebSocket send() after close");
47
- }
48
1201
  function sendRpcResponseIfOpen(connection, response) {
49
1202
  try {
50
1203
  connection.send(JSON.stringify(response));
@@ -54,47 +1207,6 @@ function sendRpcResponseIfOpen(connection, response) {
54
1207
  throw error;
55
1208
  }
56
1209
  }
57
- const subAgentRpcReplyContext = new AsyncLocalStorage();
58
- function sendFacetRpcResponseIfOpen(target, response) {
59
- try {
60
- return {
61
- sent: true,
62
- completion: Promise.resolve(target.send(JSON.stringify(response))).catch((error) => {
63
- if (!isClosedWebSocketSendError(error)) console.error("[Agent] Facet RPC response delivery failed:", error);
64
- })
65
- };
66
- } catch (error) {
67
- if (isClosedWebSocketSendError(error)) return {
68
- sent: false,
69
- completion: Promise.resolve()
70
- };
71
- throw error;
72
- }
73
- }
74
- const facetStreamingResponseDeliveryStates = /* @__PURE__ */ new WeakMap();
75
- function createStreamingResponse(connection, id, facetReplyTarget) {
76
- const stream = new StreamingResponse(connection, id);
77
- if (facetReplyTarget) facetStreamingResponseDeliveryStates.set(stream, {
78
- replyTarget: facetReplyTarget,
79
- pending: /* @__PURE__ */ new Set()
80
- });
81
- return stream;
82
- }
83
- function trackFacetStreamingResponseDelivery(stream, completion) {
84
- const state = facetStreamingResponseDeliveryStates.get(stream);
85
- if (!state) return;
86
- state.pending.add(completion);
87
- completion.finally(() => state.pending.delete(completion));
88
- }
89
- async function waitForFacetStreamingResponseDeliveries(stream) {
90
- const state = facetStreamingResponseDeliveryStates.get(stream);
91
- if (!state) return;
92
- try {
93
- await Promise.all(state.pending);
94
- } finally {
95
- facetStreamingResponseDeliveryStates.delete(stream);
96
- }
97
- }
98
1210
  /**
99
1211
  * Type guard for RPC request messages
100
1212
  */
@@ -107,84 +1219,14 @@ function isRPCRequest(msg) {
107
1219
  function isStateUpdateMessage(msg) {
108
1220
  return typeof msg === "object" && msg !== null && "type" in msg && msg.type === "cf_agent_state" && "state" in msg;
109
1221
  }
110
- const callableMetadata = /* @__PURE__ */ new WeakMap();
111
- var _connection2 = /* @__PURE__ */ new WeakMap();
112
- var _broadcast = /* @__PURE__ */ new WeakMap();
113
- var SubAgentConnectionBridge = class extends RpcTarget {
114
- constructor(connection, broadcast) {
115
- super();
116
- _classPrivateFieldInitSpec(this, _connection2, void 0);
117
- _classPrivateFieldInitSpec(this, _broadcast, void 0);
118
- _classPrivateFieldSet2(_connection2, this, connection);
119
- _classPrivateFieldSet2(_broadcast, this, broadcast);
120
- }
121
- send(message) {
122
- _classPrivateFieldGet2(_connection2, this).send(message);
123
- }
124
- close(code, reason) {
125
- _classPrivateFieldGet2(_connection2, this).close(code, reason);
126
- }
127
- setState(state) {
128
- return _classPrivateFieldGet2(_connection2, this).setState(state);
129
- }
130
- broadcast(ownerPath, message, without) {
131
- return _classPrivateFieldGet2(_broadcast, this)?.call(this, ownerPath, message, without);
132
- }
133
- };
134
- var _root = /* @__PURE__ */ new WeakMap();
135
- var _connectionId = /* @__PURE__ */ new WeakMap();
136
- var RootSubAgentConnectionBridge = class {
137
- constructor(root, connectionId) {
138
- _classPrivateFieldInitSpec(this, _root, void 0);
139
- _classPrivateFieldInitSpec(this, _connectionId, void 0);
140
- _classPrivateFieldSet2(_root, this, root);
141
- _classPrivateFieldSet2(_connectionId, this, connectionId);
142
- }
143
- send(message) {
144
- return _classPrivateFieldGet2(_root, this)._cf_sendToSubAgentConnection(_classPrivateFieldGet2(_connectionId, this), message);
145
- }
146
- close(code, reason) {
147
- return _classPrivateFieldGet2(_root, this)._cf_closeSubAgentConnection(_classPrivateFieldGet2(_connectionId, this), code, reason);
148
- }
149
- setState(state) {
150
- return _classPrivateFieldGet2(_root, this)._cf_setSubAgentConnectionState(_classPrivateFieldGet2(_connectionId, this), state);
151
- }
152
- broadcast(ownerPath, message, without) {
153
- return _classPrivateFieldGet2(_root, this)._cf_broadcastToSubAgent(ownerPath, message, without);
154
- }
155
- };
156
- /**
157
- * Decorator that marks a method as callable by clients
158
- * @param metadata Optional metadata about the callable method
159
- */
160
- function callable(metadata = {}) {
161
- return function callableDecorator(target, _context) {
162
- if (!callableMetadata.has(target)) callableMetadata.set(target, metadata);
163
- return target;
164
- };
165
- }
166
- let didWarnAboutUnstableCallable = false;
167
- /**
168
- * Decorator that marks a method as callable by clients
169
- * @deprecated this has been renamed to callable, and unstable_callable will be removed in the next major version
170
- * @param metadata Optional metadata about the callable method
171
- */
172
- const unstable_callable = (metadata = {}) => {
173
- if (!didWarnAboutUnstableCallable) {
174
- didWarnAboutUnstableCallable = true;
175
- console.warn("unstable_callable is deprecated, use callable instead. unstable_callable will be removed in the next major version.");
176
- }
177
- return callable(metadata);
178
- };
179
- function agentPathKey(path) {
180
- if (!path) return null;
181
- return path.map((step) => `${encodeURIComponent(step.className)}:${encodeURIComponent(step.name)}`).join("/");
182
- }
183
1222
  const _fiberALS = new AsyncLocalStorage();
184
1223
  const DEFAULT_KEEP_ALIVE_INTERVAL_MS = 3e4;
185
1224
  const DEFAULT_AGENT_TOOL_RECOVERY_TIMEOUT_MS = 2e3;
186
1225
  const DEFAULT_AGENT_TOOL_RECOVERY_TOTAL_TIMEOUT_MS = 5e3;
187
1226
  const DESTROY_PENDING_KEY = "cf_agents_destroy_pending";
1227
+ const HOST_JOB_KEEP_ALIVE_ID = "cf:keep-alive";
1228
+ const HOST_JOB_HOUSEKEEPING_ID = "cf:housekeeping";
1229
+ const HOST_JOB_DESTROY_ID = "cf:destroy";
188
1230
  const DESTROY_ALARM_DELAY_MS = 1e3;
189
1231
  const FIBER_RECOVERY_MAX_BACKOFF_MS = 5 * 6e4;
190
1232
  const FIBER_RECOVERY_BACKOFF_MAX_EXP = 20;
@@ -202,9 +1244,6 @@ const DETACHED_BACKBONE_CADENCE_S = [
202
1244
  const DETACHED_LIVE_COUNT_WARN_THRESHOLD = 50;
203
1245
  const DETACHED_RECONCILE_CALLBACK = "_cfDetachedReconcileTick";
204
1246
  const DETACHED_NOTIFY_CALLBACK = "_cfDetachedNotifyFinish";
205
- const SUB_AGENT_IDENTITY_VERSION_LEGACY = "legacy";
206
- const SUB_AGENT_IDENTITY_VERSION_PATH_V2 = "path-v2";
207
- const SUB_AGENT_IDENTITY_PATH_V2_PREFIX = "cf-agents:v2:";
208
1247
  /**
209
1248
  * Schema version for the Agent's internal SQLite tables.
210
1249
  * Bump this when adding new tables, columns, or migrations.
@@ -216,33 +1255,6 @@ const SCHEMA_VERSION_ROW_ID = "cf_schema_version";
216
1255
  const STATE_ROW_ID = "cf_state_row_id";
217
1256
  const STATE_WAS_CHANGED = "cf_state_was_changed";
218
1257
  const DEFAULT_STATE = {};
219
- async function sha256Hex(value) {
220
- const bytes = new TextEncoder().encode(value);
221
- const digest = await crypto.subtle.digest("SHA-256", bytes);
222
- return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
223
- }
224
- function pathV2IdentityName(logicalName, digest) {
225
- return `${SUB_AGENT_IDENTITY_PATH_V2_PREFIX}${encodeURIComponent(logicalName)}:${digest}`;
226
- }
227
- function logicalNameFromPathV2Identity(identityName) {
228
- if (!identityName.startsWith(SUB_AGENT_IDENTITY_PATH_V2_PREFIX)) return null;
229
- const rest = identityName.slice(13);
230
- const separator = rest.lastIndexOf(":");
231
- if (separator === -1) return null;
232
- try {
233
- return decodeURIComponent(rest.slice(0, separator));
234
- } catch {
235
- return null;
236
- }
237
- }
238
- /**
239
- * Validate that a stored `parentPath` has the expected shape. Used
240
- * when restoring from DO storage to guard against corrupted data.
241
- */
242
- function isValidParentPath(value) {
243
- if (!Array.isArray(value)) return false;
244
- return value.every((entry) => entry != null && typeof entry === "object" && typeof entry.className === "string" && typeof entry.name === "string");
245
- }
246
1258
  /**
247
1259
  * Internal key used to store the readonly flag in connection state.
248
1260
  * Prefixed with _cf_ to avoid collision with user state keys.
@@ -255,28 +1267,13 @@ const CF_READONLY_KEY = "_cf_readonly";
255
1267
  */
256
1268
  const CF_NO_PROTOCOL_KEY = "_cf_no_protocol";
257
1269
  /**
258
- * Internal key used to store voice call state in connection state.
259
- * Used by the voice mixin to track whether a connection is in an active call.
260
- */
261
- const CF_VOICE_IN_CALL_KEY = "_cf_voiceInCall";
262
- /**
263
- * Internal key used to remember the outer `/sub/...` URL for a
264
- * WebSocket accepted by the parent on behalf of a child facet.
265
- * Hibernated events then wake the parent, which forwards frames to
266
- * the child over serializable RPC while keeping native WebSocket I/O
267
- * parent-owned.
268
- */
269
- const CF_SUB_AGENT_OUTER_URL_KEY = "_cf_subAgentOuterUrl";
270
- const CF_SUB_AGENT_TAGS_KEY = "_cf_subAgentTags";
271
- const SUB_AGENT_OUTER_URL_HEADER = "x-cf-agents-subagent-url";
272
- /**
273
1270
  * The set of all internal keys stored in connection state that must be
274
1271
  * hidden from user code and preserved across setState calls.
275
1272
  */
276
1273
  const CF_INTERNAL_KEYS = /* @__PURE__ */ new Set([
277
1274
  CF_READONLY_KEY,
278
1275
  CF_NO_PROTOCOL_KEY,
279
- CF_VOICE_IN_CALL_KEY,
1276
+ "_cf_voiceInCall",
280
1277
  CF_SUB_AGENT_OUTER_URL_KEY,
281
1278
  CF_SUB_AGENT_TAGS_KEY
282
1279
  ]);
@@ -459,6 +1456,34 @@ var Agent = class Agent extends DurableObject {
459
1456
  async __unsafe_ensureInitialized(props) {
460
1457
  await this.lifecycle.start(props);
461
1458
  }
1459
+ /** @internal */
1460
+ get _dynamicAgents() {
1461
+ this._dynamicAgentsInstance ??= new DynamicAgentsInternal(this);
1462
+ return this._dynamicAgentsInstance;
1463
+ }
1464
+ /**
1465
+ * The dynamic-agents capability: facet-backed child agents that run
1466
+ * in their own isolate with their own SQLite database, colocated
1467
+ * with — and supervised by — this agent.
1468
+ *
1469
+ * Use dynamic agents for code whose class or lifecycle this agent
1470
+ * owns: dynamically-loaded or AI-generated code, per-run tool
1471
+ * agents, sandboxed components. For independent peers (for example
1472
+ * one Durable Object per chat), use `getAgentByName` instead.
1473
+ *
1474
+ * ```ts
1475
+ * const child = await this.dynamicAgents.get(Researcher, id);
1476
+ * await child.doWork();
1477
+ * this.dynamicAgents.abort(Researcher, id, reason);
1478
+ * await this.dynamicAgents.delete(Researcher, id);
1479
+ * ```
1480
+ *
1481
+ * @experimental The API surface may change before stabilizing.
1482
+ */
1483
+ get dynamicAgents() {
1484
+ this._dynamicAgentsApi ??= new DynamicAgents(this._dynamicAgents);
1485
+ return this._dynamicAgentsApi;
1486
+ }
462
1487
  /**
463
1488
  * Stable key for Workers AI session affinity (prefix-cache optimization).
464
1489
  *
@@ -643,7 +1668,6 @@ var Agent = class Agent extends DurableObject {
643
1668
  CREATE INDEX IF NOT EXISTS idx_workflows_name ON cf_agents_workflows(workflow_name)
644
1669
  `;
645
1670
  this.ctx.storage.sql.exec("DELETE FROM cf_agents_state WHERE id = ?", STATE_WAS_CHANGED);
646
- ensureScheduleTable(this.ctx.storage);
647
1671
  this.sql`
648
1672
  CREATE TABLE IF NOT EXISTS cf_agents_runs (
649
1673
  id TEXT PRIMARY KEY NOT NULL,
@@ -741,7 +1765,17 @@ var Agent = class Agent extends DurableObject {
741
1765
  }
742
1766
  constructor(ctx, env) {
743
1767
  super(ctx, env);
744
- this.lifecycle = Lifecycle.install(this);
1768
+ this.lifecycle = Lifecycle.install(this, { maxAlarmMemoryLimitStrikes: this._resolvedOptions.maxAlarmMemoryLimitStrikes });
1769
+ this._webSockets = new WebSockets({
1770
+ handlers: {
1771
+ onConnect: (connection, ctx) => this.onConnect(connection, ctx),
1772
+ onMessage: (connection, message) => this.onMessage(connection, message),
1773
+ onClose: (connection, code, reason, wasClean) => this.onClose(connection, code, reason, wasClean),
1774
+ onError: (connection, error) => this.onError(connection, error)
1775
+ },
1776
+ callables: callablesFromDecorated(this),
1777
+ getConnectionTags: (connection, ctx) => this.getConnectionTags(connection, ctx)
1778
+ });
745
1779
  this._state = DEFAULT_STATE;
746
1780
  this._disposables = new DisposableStore();
747
1781
  this._destroyed = false;
@@ -749,13 +1783,9 @@ var Agent = class Agent extends DurableObject {
749
1783
  this._persistenceHookMode = "none";
750
1784
  this._isFacet = false;
751
1785
  this._protocolBroadcastExcludeIds = /* @__PURE__ */ new Set();
752
- this._cf_subAgentBridgeContext = new AsyncLocalStorage();
753
- this._cf_virtualSubAgentConnections = /* @__PURE__ */ new Map();
754
- this._cf_subAgentConnectionOperationTails = /* @__PURE__ */ new Map();
755
1786
  this._parentPath = [];
756
1787
  this._warnedChatRecoveryInOnStart = false;
757
1788
  this._keepAliveRefs = 0;
758
- this._facetKeepAliveTokens = /* @__PURE__ */ new Set();
759
1789
  this._runFiberActiveFibers = /* @__PURE__ */ new Set();
760
1790
  this._managedFiberAbortControllers = /* @__PURE__ */ new Map();
761
1791
  this._managedFiberExecutions = /* @__PURE__ */ new Map();
@@ -769,7 +1799,6 @@ var Agent = class Agent extends DurableObject {
769
1799
  this.observability = genericObservability;
770
1800
  this._flushingQueue = false;
771
1801
  this.maxConcurrentAgentTools = Infinity;
772
- this._subAgentRegistryReady = false;
773
1802
  const routeHost = this;
774
1803
  setLifecycleRouteTransport(this.lifecycle, {
775
1804
  get source() {
@@ -782,10 +1811,10 @@ var Agent = class Agent extends DurableObject {
782
1811
  const payload = event.payload !== null && typeof event.payload === "object" && !Array.isArray(event.payload) ? event.payload : { value: event.payload };
783
1812
  this._emit(event.type, payload);
784
1813
  });
785
- setLifecycleHostInvoker(this.lifecycle, (run) => runInInvocation({
1814
+ setLifecycleHostInvoker(this.lifecycle, (run, scope) => runInInvocation({
786
1815
  agent: this,
787
- connection: void 0,
788
- request: void 0,
1816
+ connection: scope?.connection,
1817
+ request: scope?.request,
789
1818
  email: void 0
790
1819
  }, run));
791
1820
  this.scheduler = new Scheduler({
@@ -803,6 +1832,11 @@ var Agent = class Agent extends DurableObject {
803
1832
  if (typeof method !== "function") return void 0;
804
1833
  return (payload, schedule) => method.call(this, payload, schedule);
805
1834
  });
1835
+ this.tasks = new Tasks({ onError: (error) => this.onError(error) });
1836
+ setTaskRoutedMemoryLimitHandler(this.tasks, (context) => {
1837
+ return this.onAlarmMemoryLimit?.call(this, context);
1838
+ });
1839
+ setTaskDefinitionResolver(this.tasks, (name) => this.taskDefinitions?.[name]);
806
1840
  this.mcp = this._withAgentSpan("agent_initialization", "initialization", {}, (update) => {
807
1841
  if (!wrappedClasses.has(this.constructor)) {
808
1842
  this._autoWrapCustomMethods();
@@ -823,7 +1857,7 @@ var Agent = class Agent extends DurableObject {
823
1857
  createAuthProvider: (callbackUrl) => this.createMcpOAuthProvider(callbackUrl)
824
1858
  });
825
1859
  });
826
- this.lifecycle.use(this.scheduler).use(this.mcp);
1860
+ this.lifecycle.use(this.scheduler).use(this.mcp).use(this._webSockets, { fallback: true }).use(this.tasks).use(this._dynamicAgents);
827
1861
  let mcpBroadcastReady = false;
828
1862
  this._disposables.add(this.mcp.onServerStateChanged(() => {
829
1863
  if (mcpBroadcastReady) this.broadcastMcpServers();
@@ -857,6 +1891,8 @@ var Agent = class Agent extends DurableObject {
857
1891
  await _onAlarm();
858
1892
  if (this._destroyed) return;
859
1893
  await this._onAlarmHousekeeping();
1894
+ if (this._destroyed) return;
1895
+ await this._syncHostJobs();
860
1896
  };
861
1897
  const _onRequest = this.onRequest.bind(this);
862
1898
  this.onRequest = (request) => {
@@ -869,7 +1905,7 @@ var Agent = class Agent extends DurableObject {
869
1905
  };
870
1906
  const _onMessage = this.onMessage.bind(this);
871
1907
  this.onMessage = async (connection, message) => {
872
- const replyBridge = subAgentRpcReplyContext.getStore()?.bridge;
1908
+ const replyBridge = dynamicAgentRpcReplyContext.getStore()?.bridge;
873
1909
  if (await __DO_NOT_USE_WILL_BREAK__agentContext.exit(() => this._cf_forwardSubAgentWebSocketMessage(connection, message, replyBridge))) return;
874
1910
  this._ensureConnectionWrapped(connection);
875
1911
  return runInInvocation({
@@ -910,9 +1946,10 @@ var Agent = class Agent extends DurableObject {
910
1946
  const methodFn = this[method];
911
1947
  if (typeof methodFn !== "function") throw new Error(`Method ${method} does not exist`);
912
1948
  if (!this._isCallable(method)) throw new Error(`Method ${method} is not callable`);
913
- const metadata = callableMetadata.get(methodFn);
1949
+ const metadata = getCallableMetadata(methodFn);
914
1950
  if (metadata?.streaming) {
915
- const stream = createStreamingResponse(connection, id, replyBridge);
1951
+ const stream = new StreamingResponse(connection, id);
1952
+ if (replyBridge) registerFacetStreamingDelivery(stream, replyBridge);
916
1953
  this._emit("rpc", {
917
1954
  method,
918
1955
  streaming: true
@@ -1061,6 +2098,7 @@ var Agent = class Agent extends DurableObject {
1061
2098
  console.warn("[Agent] `chatRecovery` was assigned during onStart(). Chat recovery evaluates its budgets (and may seal an interrupted turn, firing onExhausted) on wake BEFORE onStart() runs, so a config set here is applied too late and the built-in defaults are used for the recovery that matters. Assign `chatRecovery` as a class field or in the constructor instead.");
1062
2099
  }
1063
2100
  this._scheduleAgentToolRunRecovery({ runIds: startupAgentToolRunIds });
2101
+ await this._syncHostJobs();
1064
2102
  return result;
1065
2103
  });
1066
2104
  });
@@ -1068,18 +2106,7 @@ var Agent = class Agent extends DurableObject {
1068
2106
  this.onStart = (props) => this._withAgentSpan("agent_start", "startup", {}, (update) => startAgent(props, update));
1069
2107
  }
1070
2108
  async _restoreAgentFacetContext() {
1071
- await this._withAgentSpan("restore_agent_state", "startup", {}, async () => {
1072
- if (await this.ctx.storage.get("cf_agents_is_facet")) this._isFacet = true;
1073
- const storedFacetName = await this.ctx.storage.get("cf_agents_facet_name");
1074
- if (typeof storedFacetName === "string") this._facetName = storedFacetName;
1075
- const storedParentPath = await this.ctx.storage.get("cf_agents_parent_path");
1076
- if (isValidParentPath(storedParentPath)) this._parentPath = storedParentPath;
1077
- try {
1078
- await this._cf_hydrateSubAgentConnectionsFromRoot();
1079
- } catch (error) {
1080
- console.warn("[Agent] Unable to hydrate sub-agent WebSocket connections:", error);
1081
- }
1082
- });
2109
+ await this._withAgentSpan("restore_agent_state", "startup", {}, () => this._dynamicAgents.restoreFacetContext());
1083
2110
  }
1084
2111
  /**
1085
2112
  * Check for workflows referencing unknown bindings and warn with migration suggestion.
@@ -1534,7 +2561,7 @@ var Agent = class Agent extends DurableObject {
1534
2561
  const descriptor = Object.getOwnPropertyDescriptor(proto, methodName);
1535
2562
  if (baseMethods.has(methodName) || methodName.startsWith("_") || !descriptor || !!descriptor.get || typeof descriptor.value !== "function") continue;
1536
2563
  const wrappedFunction = withAgentContext(this[methodName]);
1537
- if (this._isCallable(methodName)) callableMetadata.set(wrappedFunction, callableMetadata.get(this[methodName]));
2564
+ if (this._isCallable(methodName)) copyCallableMetadata(this[methodName], wrappedFunction);
1538
2565
  this.constructor.prototype[methodName] = wrappedFunction;
1539
2566
  }
1540
2567
  proto = Object.getPrototypeOf(proto);
@@ -1721,74 +2748,21 @@ var Agent = class Agent extends DurableObject {
1721
2748
  retry: parseRetryOptions(row)
1722
2749
  }));
1723
2750
  }
1724
- _facetRunRowsForPrefix(ownerPath) {
1725
- return this.sql`
1726
- SELECT owner_path, owner_path_key, run_id, created_at
1727
- FROM cf_agents_facet_runs
1728
- `.filter((row) => {
1729
- try {
1730
- const rowOwnerPath = JSON.parse(row.owner_path);
1731
- return this._isSameAgentPathPrefix(ownerPath, rowOwnerPath);
1732
- } catch {
1733
- return false;
1734
- }
1735
- });
1736
- }
1737
- _deleteFacetRunRowsForPrefix(ownerPath) {
1738
- for (const row of this._facetRunRowsForPrefix(ownerPath)) this.sql`
1739
- DELETE FROM cf_agents_facet_runs
1740
- WHERE owner_path_key = ${row.owner_path_key}
1741
- AND run_id = ${row.run_id}
1742
- `;
1743
- }
1744
2751
  _lifecycleRouteAddress() {
1745
- if (!this._isFacet) return void 0;
1746
- const key = agentPathKey(this.selfPath);
1747
- return key ? {
1748
- key,
1749
- data: JSON.stringify(this.selfPath)
1750
- } : void 0;
2752
+ return this._dynamicAgents.lifecycleRouteAddress();
1751
2753
  }
1752
- async _routeLifecycleToRoot(envelope) {
1753
- if (!this._isFacet) return this.lifecycle.route(envelope);
1754
- return (await this._rootAlarmOwner())._cf_routeLifecycle(void 0, envelope);
2754
+ _routeLifecycleToRoot(envelope) {
2755
+ return this._dynamicAgents.routeLifecycleToRoot(envelope);
1755
2756
  }
1756
- async _routeLifecycleToTarget(target, envelope) {
1757
- let targetPath;
1758
- try {
1759
- targetPath = JSON.parse(target.data);
1760
- } catch {
1761
- throw new Error("Lifecycle route target is not a valid Agent path");
1762
- }
1763
- const selfPath = this.selfPath;
1764
- if (!this._isSameAgentPathPrefix(selfPath, targetPath)) throw new Error(`Lifecycle route does not descend from ${JSON.stringify(selfPath)}.`);
1765
- if (selfPath.length === targetPath.length) return this.lifecycle.route(envelope);
1766
- const next = targetPath[selfPath.length];
1767
- if (!this.hasSubAgent(next.className, next.name)) {
1768
- const stalePath = targetPath.slice(0, selfPath.length + 1);
1769
- if (this._isFacet) await (await this._rootAlarmOwner())._cf_cleanupFacetPrefix(stalePath);
1770
- else await this._cf_cleanupFacetPrefix(stalePath);
1771
- return false;
1772
- }
1773
- return (await this._cf_resolveSubAgent(next.className, next.name))._cf_routeLifecycle(target, envelope);
2757
+ _routeLifecycleToTarget(target, envelope) {
2758
+ return this._dynamicAgents.routeLifecycleToTarget(target, envelope);
1774
2759
  }
1775
2760
  /** Single native-RPC aperture for routed Lifecycle capabilities. */
1776
2761
  _cf_routeLifecycle(target, envelope) {
1777
- return target ? this._routeLifecycleToTarget(target, envelope) : this.lifecycle.route(envelope);
1778
- }
1779
- async _rootAlarmOwner() {
1780
- const root = this._parentPath[0];
1781
- if (!root) throw new Error("Facet routing requires a root parent.");
1782
- const binding = this.ctx.exports?.[root.className];
1783
- if (!binding) throw new Error(`Unable to resolve root "${root.className}" for facet routing.`);
1784
- return await getAgentByName(binding, root.name);
2762
+ return this._dynamicAgents.routeLifecycle(target, envelope);
1785
2763
  }
1786
- _cf_rootResolvesToSelf() {
1787
- const root = this._parentPath[0];
1788
- if (!root) return false;
1789
- const binding = this.ctx.exports?.[root.className];
1790
- if (!binding?.idFromName) return false;
1791
- return binding.idFromName(root.name).equals(this.ctx.id);
2764
+ _rootAlarmOwner() {
2765
+ return this._dynamicAgents.rootAlarmOwner();
1792
2766
  }
1793
2767
  /**
1794
2768
  * Clean root-owned bookkeeping for a sub-tree of facets. This
@@ -1801,64 +2775,40 @@ var Agent = class Agent extends DurableObject {
1801
2775
  * @internal
1802
2776
  */
1803
2777
  async _cf_cleanupFacetPrefix(ownerPath) {
1804
- const prefix = agentPathKey(ownerPath);
1805
- if (prefix) this.scheduler.__DO_NOT_USE_WILL_BREAK__cleanupRoutePrefix(prefix);
1806
- this._deleteFacetRunRowsForPrefix(ownerPath);
1807
- await this._rearmAlarm();
2778
+ await this._dynamicAgents.cleanupPrefix(ownerPath);
1808
2779
  }
1809
2780
  /**
1810
2781
  * Acquire a root-owned keepAlive ref on behalf of a descendant facet.
1811
- * Facets share the root isolate but cannot set their own physical
1812
- * alarm, so this lets facet work use the root alarm heartbeat.
2782
+ * Facets run in separate colocated isolates but cannot set their own
2783
+ * physical alarm, so this lets facet work use the root alarm heartbeat.
1813
2784
  * @internal
1814
2785
  */
1815
- async _cf_acquireFacetKeepAlive(ownerPath) {
1816
- const token = `${agentPathKey(ownerPath) ?? "unknown"}:${nanoid(9)}`;
1817
- this._facetKeepAliveTokens.add(token);
1818
- this._keepAliveRefs++;
1819
- if (this._keepAliveRefs === 1) await this._rearmAlarm();
1820
- return token;
2786
+ _cf_acquireFacetKeepAlive(ownerPath) {
2787
+ return this._dynamicAgents.acquireKeepAlive(ownerPath);
1821
2788
  }
1822
2789
  /**
1823
2790
  * Release a root-owned keepAlive ref previously acquired for a facet.
1824
2791
  * Idempotent so disposer calls can safely race or run twice.
1825
2792
  * @internal
1826
2793
  */
1827
- async _cf_releaseFacetKeepAlive(token) {
1828
- if (!this._facetKeepAliveTokens.delete(token)) return;
1829
- this._keepAliveRefs = Math.max(0, this._keepAliveRefs - 1);
1830
- await this._rearmAlarm();
2794
+ _cf_releaseFacetKeepAlive(token) {
2795
+ return this._dynamicAgents.releaseKeepAlive(token);
1831
2796
  }
1832
2797
  /**
1833
2798
  * Register a facet's durable run row in the root-side index so root
1834
2799
  * alarm housekeeping can dispatch recovery checks into idle facets.
1835
2800
  * The facet remains authoritative for snapshots and recovery hooks.
1836
2801
  * @internal
1837
- */
1838
- async _cf_registerFacetRun(ownerPath, runId) {
1839
- const ownerPathJson = JSON.stringify(ownerPath);
1840
- const ownerPathKey = agentPathKey(ownerPath);
1841
- if (!ownerPathKey) throw new Error("_cf_registerFacetRun requires a non-empty owner path.");
1842
- this.sql`
1843
- INSERT OR REPLACE INTO cf_agents_facet_runs
1844
- (owner_path, owner_path_key, run_id, created_at)
1845
- VALUES
1846
- (${ownerPathJson}, ${ownerPathKey}, ${runId}, ${Date.now()})
1847
- `;
1848
- await this._rearmAlarm();
2802
+ */
2803
+ _cf_registerFacetRun(ownerPath, runId) {
2804
+ return this._dynamicAgents.registerRun(ownerPath, runId);
1849
2805
  }
1850
2806
  /**
1851
2807
  * Remove a completed facet fiber from the root-side index.
1852
2808
  * @internal
1853
2809
  */
1854
- async _cf_unregisterFacetRun(ownerPath, runId) {
1855
- const ownerPathKey = agentPathKey(ownerPath);
1856
- this.sql`
1857
- DELETE FROM cf_agents_facet_runs
1858
- WHERE owner_path_key IS ${ownerPathKey}
1859
- AND run_id = ${runId}
1860
- `;
1861
- await this._rearmAlarm();
2810
+ _cf_unregisterFacetRun(ownerPath, runId) {
2811
+ return this._dynamicAgents.unregisterRun(ownerPath, runId);
1862
2812
  }
1863
2813
  /**
1864
2814
  * Schedule a task to be executed in the future
@@ -2018,13 +2968,13 @@ var Agent = class Agent extends DurableObject {
2018
2968
  };
2019
2969
  }
2020
2970
  this._keepAliveRefs++;
2021
- if (this._keepAliveRefs === 1) await this._rearmAlarm();
2971
+ if (this._keepAliveRefs === 1) await this._syncHostJobs();
2022
2972
  let disposed = false;
2023
2973
  return () => {
2024
2974
  if (disposed) return;
2025
2975
  disposed = true;
2026
2976
  this._keepAliveRefs = Math.max(0, this._keepAliveRefs - 1);
2027
- if (this._keepAliveRefs === 0) this.ctx.waitUntil(this._rearmAlarm().catch((e) => {
2977
+ if (this._keepAliveRefs === 0) this.ctx.waitUntil(this._syncHostJobs().catch((e) => {
2028
2978
  console.error("[Agent] Failed to reschedule alarm after keepAlive dispose:", e);
2029
2979
  }));
2030
2980
  };
@@ -2617,6 +3567,15 @@ var Agent = class Agent extends DurableObject {
2617
3567
  ctx.stash(data);
2618
3568
  }
2619
3569
  /**
3570
+ * Run `fn` inside the fiber stash context so `this.stash()` keeps working
3571
+ * for turns executing on the `tasks` capability exactly as it does inside
3572
+ * legacy `runFiber()` closures.
3573
+ * @internal
3574
+ */
3575
+ _withFiberStash(context, fn) {
3576
+ return _fiberALS.run(context, fn);
3577
+ }
3578
+ /**
2620
3579
  * Called when an interrupted fiber is detected after restart.
2621
3580
  * Override to implement recovery (re-invoke work, notify clients, etc.).
2622
3581
  *
@@ -2787,36 +3746,8 @@ var Agent = class Agent extends DurableObject {
2787
3746
  * recovery hooks live in each facet's own `cf_agents_runs` table.
2788
3747
  * @internal
2789
3748
  */
2790
- async _checkFacetRunFibers() {
2791
- if (this._parentPath.length > 0) return;
2792
- const rows = this.sql`
2793
- SELECT owner_path, owner_path_key, run_id, created_at
2794
- FROM cf_agents_facet_runs
2795
- ORDER BY created_at ASC
2796
- `;
2797
- const firstRowByOwner = /* @__PURE__ */ new Map();
2798
- for (const row of rows) if (!firstRowByOwner.has(row.owner_path_key)) firstRowByOwner.set(row.owner_path_key, row);
2799
- for (const row of firstRowByOwner.values()) {
2800
- let ownerPath;
2801
- try {
2802
- ownerPath = JSON.parse(row.owner_path);
2803
- } catch (e) {
2804
- console.warn(`[Agent] Corrupted facet fiber owner path for ${row.owner_path_key}; pruning stale lease.`, e);
2805
- this.sql`
2806
- DELETE FROM cf_agents_facet_runs
2807
- WHERE owner_path_key = ${row.owner_path_key}
2808
- `;
2809
- continue;
2810
- }
2811
- try {
2812
- if (await this._cf_checkRunFibersForFacet(ownerPath) === 0) this.sql`
2813
- DELETE FROM cf_agents_facet_runs
2814
- WHERE owner_path_key = ${row.owner_path_key}
2815
- `;
2816
- } catch (e) {
2817
- console.error(`[Agent] Facet fiber recovery check failed for ${row.owner_path_key}:`, e);
2818
- }
2819
- }
3749
+ _checkFacetRunFibers() {
3750
+ return this._dynamicAgents.checkRunFibers();
2820
3751
  }
2821
3752
  /**
2822
3753
  * Dispatch a runFiber recovery check into the facet identified by
@@ -2824,18 +3755,8 @@ var Agent = class Agent extends DurableObject {
2824
3755
  * rows on the target facet after recovery.
2825
3756
  * @internal
2826
3757
  */
2827
- async _cf_checkRunFibersForFacet(ownerPath) {
2828
- const selfPath = this.selfPath;
2829
- if (!this._isSameAgentPathPrefix(selfPath, ownerPath)) throw new Error(`Facet fiber owner path does not descend from ${JSON.stringify(selfPath)}.`);
2830
- if (selfPath.length === ownerPath.length) {
2831
- await this._checkRunFibers();
2832
- return this.sql`
2833
- SELECT COUNT(*) as count FROM cf_agents_runs
2834
- `[0]?.count ?? 0;
2835
- }
2836
- const next = ownerPath[selfPath.length];
2837
- if (!this.hasSubAgent(next.className, next.name)) return 0;
2838
- return (await this._cf_resolveSubAgent(next.className, next.name))._cf_checkRunFibersForFacet(ownerPath);
3758
+ _cf_checkRunFibersForFacet(ownerPath) {
3759
+ return this._dynamicAgents.checkRunFibersAtPath(ownerPath);
2839
3760
  }
2840
3761
  /**
2841
3762
  * Invoke an RPC method on this Agent or a descendant facet identified
@@ -2843,18 +3764,8 @@ var Agent = class Agent extends DurableObject {
2843
3764
  * `this.agent` calls back to the exact sub-agent that started a workflow.
2844
3765
  * @internal
2845
3766
  */
2846
- async _cf_invokeAgentPath(targetPath, method, args) {
2847
- await this.__unsafe_ensureInitialized();
2848
- const selfPath = this.selfPath;
2849
- if (!this._isSameAgentPathPrefix(selfPath, targetPath)) throw new Error(`Workflow origin path does not descend from ${JSON.stringify(selfPath)}.`);
2850
- if (selfPath.length === targetPath.length) {
2851
- const fn = this[method];
2852
- if (isInternalJsStubProp(method) || method in Object.prototype || typeof fn !== "function") throw new Error(`Workflow origin method "${method}" is not callable on ${this.constructor.name}.`);
2853
- return await fn.apply(this, args);
2854
- }
2855
- const next = targetPath[selfPath.length];
2856
- if (!this.hasSubAgent(next.className, next.name)) throw new Error(`Workflow origin sub-agent ${next.className} "${next.name}" no longer exists.`);
2857
- return await (await this._cf_resolveSubAgent(next.className, next.name))._cf_invokeAgentPath(targetPath, method, args);
3767
+ _cf_invokeAgentPath(targetPath, method, args) {
3768
+ return this._dynamicAgents.invokeAgentPath(targetPath, method, args);
2858
3769
  }
2859
3770
  /**
2860
3771
  * Recursively destroy a descendant facet identified by
@@ -2869,25 +3780,8 @@ var Agent = class Agent extends DurableObject {
2869
3780
  * cleanup as `parent.deleteSubAgent(Cls, name)` from the parent.
2870
3781
  * @internal
2871
3782
  */
2872
- async _cf_destroyDescendantFacet(targetPath) {
2873
- const selfPath = this.selfPath;
2874
- if (targetPath.length === 0) throw new Error("_cf_destroyDescendantFacet: target path must not be empty.");
2875
- if (selfPath.length >= targetPath.length) throw new Error("_cf_destroyDescendantFacet: target must be a strict descendant.");
2876
- if (!this._isSameAgentPathPrefix(selfPath, targetPath)) throw new Error("_cf_destroyDescendantFacet: target path does not descend from this agent.");
2877
- if (this._parentPath.length === 0) await this._cf_cleanupFacetPrefix(targetPath);
2878
- if (selfPath.length === targetPath.length - 1) {
2879
- const target = targetPath[targetPath.length - 1];
2880
- const ctx = this.ctx;
2881
- if (!ctx.facets) throw new Error("destroy() (delegated from facet) is not supported in this runtime — `ctx.facets` is unavailable. Update to the latest `compatibility_date` in your wrangler.jsonc.");
2882
- try {
2883
- ctx.facets.delete(`${target.className}\0${target.name}`);
2884
- } catch {}
2885
- this._forgetSubAgent(target.className, target.name);
2886
- return;
2887
- }
2888
- const next = targetPath[selfPath.length];
2889
- if (!this.hasSubAgent(next.className, next.name)) return;
2890
- await (await this._cf_resolveSubAgent(next.className, next.name))._cf_destroyDescendantFacet(targetPath);
3783
+ _cf_destroyDescendantFacet(targetPath) {
3784
+ return this._dynamicAgents.destroyDescendant(targetPath);
2891
3785
  }
2892
3786
  /**
2893
3787
  * Whether any runFiber recovery work is still outstanding: orphaned
@@ -2895,7 +3789,7 @@ var Agent = class Agent extends DurableObject {
2895
3789
  * executing in memory, which already hold a keepAlive ref) or managed
2896
3790
  * ledger fibers stuck in a non-terminal state with no live run row.
2897
3791
  *
2898
- * Used by `_rearmAlarm` to arm a follow-up alarm so multi-pass
3792
+ * Used by `_syncHostJobs` to arm a follow-up alarm so multi-pass
2899
3793
  * recovery (e.g. after a scan-deadline yield, or while retrying a throwing
2900
3794
  * recovery hook) resumes instead of starving.
2901
3795
  * @internal
@@ -2913,28 +3807,57 @@ var Agent = class Agent extends DurableObject {
2913
3807
  AND r.id IS NULL
2914
3808
  `[0]?.count ?? 0) > 0;
2915
3809
  }
2916
- async _rearmAlarm() {
2917
- await this._withAgentSpan("schedule_agent_alarm", "alarm", {}, () => this.lifecycle.rearmAlarm());
2918
- }
2919
3810
  /**
2920
- * Contribute Agent-owned alarm work that has not yet been extracted into its
2921
- * own capability. Scheduler contributes independently.
3811
+ * Synchronize Agent-owned host jobs with current durable state.
3812
+ *
3813
+ * Replaces the old pull-based `getNextAlarm()` contribution: keep-alive
3814
+ * refs hold a `cf:keep-alive` job, and fiber-recovery / facet-run state
3815
+ * holds a `cf:housekeeping` job. Every state change that used to trigger
3816
+ * an alarm recalculation now re-pushes or cancels these jobs; queue
3817
+ * mutations re-arm the physical alarm automatically.
2922
3818
  * @internal
2923
3819
  */
2924
- async getNextAlarm() {
2925
- const pendingDestroy = await this._pendingDestroyAlarm();
2926
- if (pendingDestroy !== null) return {
2927
- time: pendingDestroy,
2928
- exclusive: true
2929
- };
2930
- const nowMs = Date.now();
3820
+ async _syncHostJobs() {
3821
+ if (this._destroyed) return;
3822
+ await this._withAgentSpan("schedule_agent_alarm", "alarm", {}, async () => {
3823
+ const work = this.lifecycle.jobs;
3824
+ const nowMs = Date.now();
3825
+ const pendingDestroy = await this._pendingDestroyAlarm();
3826
+ if (pendingDestroy !== null) {
3827
+ await work.push({
3828
+ id: HOST_JOB_DESTROY_ID,
3829
+ fn: "destroy",
3830
+ time: pendingDestroy,
3831
+ exclusive: true
3832
+ });
3833
+ return;
3834
+ }
3835
+ if (work.get(HOST_JOB_DESTROY_ID)) await work.cancel(HOST_JOB_DESTROY_ID);
3836
+ if (this._keepAliveRefs > 0) await work.push({
3837
+ id: HOST_JOB_KEEP_ALIVE_ID,
3838
+ fn: "keepAlive",
3839
+ time: nowMs + this._resolvedOptions.keepAliveIntervalMs
3840
+ });
3841
+ else if (work.get(HOST_JOB_KEEP_ALIVE_ID)) await work.cancel(HOST_JOB_KEEP_ALIVE_ID);
3842
+ const housekeepingAt = this._nextHousekeepingWakeMs(nowMs);
3843
+ if (housekeepingAt !== null) await work.push({
3844
+ id: HOST_JOB_HOUSEKEEPING_ID,
3845
+ fn: "housekeeping",
3846
+ time: housekeepingAt
3847
+ });
3848
+ else if (work.get(HOST_JOB_HOUSEKEEPING_ID)) await work.cancel(HOST_JOB_HOUSEKEEPING_ID);
3849
+ });
3850
+ }
3851
+ /**
3852
+ * The next wake fiber-recovery or facet-run housekeeping needs, or `null`
3853
+ * when neither has pending durable state.
3854
+ */
3855
+ _nextHousekeepingWakeMs(nowMs) {
2931
3856
  let nextTimeMs = null;
2932
- if (this._keepAliveRefs > 0) nextTimeMs = nowMs + this._resolvedOptions.keepAliveIntervalMs;
2933
3857
  if (this._hasPendingFiberRecovery()) {
2934
3858
  const base = this._resolvedOptions.keepAliveIntervalMs;
2935
3859
  const exp = Math.min(this._recoveryNoProgressScans, FIBER_RECOVERY_BACKOFF_MAX_EXP);
2936
- const recoveryMs = nowMs + Math.min(FIBER_RECOVERY_MAX_BACKOFF_MS, base * 2 ** exp);
2937
- nextTimeMs = nextTimeMs === null ? recoveryMs : Math.min(nextTimeMs, recoveryMs);
3860
+ nextTimeMs = nowMs + Math.min(FIBER_RECOVERY_MAX_BACKOFF_MS, base * 2 ** exp);
2938
3861
  }
2939
3862
  if ((this.sql`
2940
3863
  SELECT COUNT(*) as count FROM cf_agents_facet_runs
@@ -2942,132 +3865,68 @@ var Agent = class Agent extends DurableObject {
2942
3865
  const facetRecoveryMs = nowMs + this._resolvedOptions.keepAliveIntervalMs;
2943
3866
  nextTimeMs = nextTimeMs === null ? facetRecoveryMs : Math.min(nextTimeMs, facetRecoveryMs);
2944
3867
  }
2945
- const extensionAlarm = await this._getExtensionAlarm();
2946
- if (extensionAlarm !== null) nextTimeMs = nextTimeMs === null ? extensionAlarm : Math.min(nextTimeMs, extensionAlarm);
2947
3868
  return nextTimeMs;
2948
3869
  }
2949
- /** @internal Transitional alarm contribution for Agent subclasses. */
2950
- _getExtensionAlarm() {
2951
- return null;
2952
- }
2953
3870
  /** Lifecycle alarm callback; Agent housekeeping runs after user alarm work. */
2954
3871
  onAlarm() {}
2955
3872
  /**
2956
- * Run Lifecycle's alarm phase inside Agent's memory-limit circuit breaker.
2957
- *
2958
- * @remarks Use `this.schedule()` for named Agent callbacks. Reusable durable
2959
- * work belongs in a capability with `getNextAlarm()` and `onAlarm()`.
3873
+ * Drive one Agent-owned host job from the Lifecycle queue.
3874
+ * @internal Dispatched by Lifecycle's alarm event loop; extensions add
3875
+ * job fns through {@link _onHostJob}.
2960
3876
  */
2961
- async alarm() {
2962
- if (await this._hasPendingDestroy()) {
2963
- await this.destroy();
2964
- return;
2965
- }
2966
- try {
2967
- await this._cf_runAlarmBody();
2968
- await this._cf_clearAlarmMemoryLimitStrikes();
2969
- } catch (error) {
2970
- if (!isDurableObjectMemoryLimitReset(error)) throw error;
2971
- await this._cf_handleAlarmMemoryLimitReset(error);
2972
- }
2973
- }
2974
- /** Run the Lifecycle alarm phase inside Agent's OOM circuit breaker. */
2975
- async _cf_runAlarmBody() {
2976
- await this.lifecycle.alarm();
3877
+ onJob(context) {
3878
+ return this._onHostJob(context.job.fn, context);
2977
3879
  }
2978
3880
  /**
2979
- * The schedule-callback names whose alarm rows drive a recovery loop that can
2980
- * deterministically OOM. The base agent has none; chat hosts (`Think`,
2981
- * `AIChatAgent`) override this to return their recovery continuation callbacks
2982
- * so the circuit breaker can surgically back them off / purge them WITHOUT
2983
- * disturbing unrelated scheduled tasks. See {@link _cf_handleAlarmMemoryLimitReset}.
3881
+ * @internal Dispatch one host job fn. Agent extensions (Think) override
3882
+ * this to add fns and delegate unknown ones to `super`.
2984
3883
  */
2985
- _cf_recoveryAlarmCallbacks() {
2986
- return [];
3884
+ _onHostJob(fn, _context) {
3885
+ switch (fn) {
3886
+ case "keepAlive": return this._keepAliveRefs > 0 ? { rescheduleAt: Date.now() + this._resolvedOptions.keepAliveIntervalMs } : void 0;
3887
+ case "housekeeping": {
3888
+ const next = this._nextHousekeepingWakeMs(Date.now());
3889
+ return next === null ? void 0 : { rescheduleAt: next };
3890
+ }
3891
+ case "destroy": return;
3892
+ default:
3893
+ console.warn(`Unknown Agent host job fn ${JSON.stringify(fn)}`);
3894
+ return;
3895
+ }
2987
3896
  }
2988
3897
  /**
2989
- * Hook for a host to terminalize ("seal") any in-flight recovery work as an
2990
- * out-of-memory exhaustion when the alarm circuit breaker trips at its strike
2991
- * budget (#1825). Runs at the outermost alarm frame (post-unwind, so writes
2992
- * can land). Default: no-op. Chat hosts override to fire `onExhausted` + the
2993
- * terminal banner and persist the sealed incident.
2994
- */
2995
- async _cf_sealMemoryLimitedRecovery() {}
2996
- /**
2997
- * Clear the durable memory-limit strike counter after a clean alarm so the
2998
- * circuit breaker counts CONSECUTIVE resets rather than lifetime ones
2999
- * (#1825). Reads first (cheap, usually cached) and only writes when a strike
3000
- * is actually recorded, so the common no-strike path costs no write.
3001
- * Best-effort: a stale strike only costs one extra tolerated spike later.
3898
+ * Apply host policy after the alarm memory-limit breaker records a strike.
3899
+ *
3900
+ * New chat hosts override this hook directly. The sealed-only fallback keeps
3901
+ * `agents` 0.23 compatible with already-published chat packages whose peer
3902
+ * ranges accept it but which implement only the former
3903
+ * `_cf_sealMemoryLimitedRecovery` template method. Queue membership remains
3904
+ * job-row policy; this invokes terminalization only and can be removed once
3905
+ * old chat releases no longer accept the current `agents` range.
3906
+ *
3907
+ * @internal
3002
3908
  */
3003
- async _cf_clearAlarmMemoryLimitStrikes() {
3004
- try {
3005
- const prior = await this.ctx.storage.get(Agent._CF_OOM_ALARM_STRIKES_KEY);
3006
- if (typeof prior === "number" && prior > 0) await this.ctx.storage.delete(Agent._CF_OOM_ALARM_STRIKES_KEY);
3007
- } catch {}
3909
+ async onAlarmMemoryLimit(context) {
3910
+ if (!context.sealed) return;
3911
+ await this._cf_sealMemoryLimitedRecovery?.call(this);
3008
3912
  }
3009
3913
  /**
3010
- * Alarm-boundary circuit breaker for Durable Object memory-limit resets
3011
- * (#1825). The in-DO recovery budgets (`chatRecovery.maxOomRetries` /
3012
- * `maxRecoveryWork`) only engage if their code runs AND its writes land; a
3013
- * severe OOM can defeat both thrown before the budget runs (boot hydration),
3014
- * or its own small writes also OOM under memory pressure. In that case the
3015
- * error reaches {@link alarm} and, unhandled, the platform auto-retries the
3016
- * alarm indefinitely (re-running the doomed, billable turn each cycle).
3017
- *
3018
- * This runs at the OUTERMOST frame: the heavy turn has unwound and GC has
3019
- * reclaimed its footprint, so the small writes here can land where mid-turn
3020
- * ones (e.g. give-up's incident read) OOMed. A durable strike counter tolerates
3021
- * a few resets (a transient spike may clear), backing off the recovery rows so
3022
- * the retry is not a hot loop. At the `maxAlarmMemoryLimitStrikes` budget it
3023
- * seals the recovery work and purges the looping rows so the loop — and the
3024
- * bill — stops. Each step is best-effort: even these tiny writes can OOM, but
3025
- * swallowing (not re-throwing) still halts the platform's auto-retry, and a
3026
- * later wake re-arms legitimate schedules.
3027
- */
3028
- async _cf_handleAlarmMemoryLimitReset(error) {
3029
- const key = Agent._CF_OOM_ALARM_STRIKES_KEY;
3030
- let strikes = 1;
3031
- try {
3032
- const prior = await this.ctx.storage.get(key);
3033
- strikes = (typeof prior === "number" ? prior : 0) + 1;
3034
- await this.ctx.storage.put(key, strikes);
3035
- } catch {}
3036
- const limit = this._resolvedOptions.maxAlarmMemoryLimitStrikes;
3037
- const sealed = strikes >= limit;
3038
- const recoveryCallbacks = this._cf_recoveryAlarmCallbacks();
3039
- console.error(`Alarm hit a Durable Object memory-limit reset (strike ${strikes}/${limit}${sealed ? ", sealing recovery" : ", will retry with backoff"}). Breaking the platform alarm-retry loop (#1825).`, error instanceof Error ? error.message : String(error));
3040
- if (sealed) {
3041
- this.scheduler.__DO_NOT_USE_WILL_BREAK__handleAlarmMemoryLimit({
3042
- callbacks: recoveryCallbacks,
3043
- sealed: true
3044
- });
3045
- try {
3046
- await this._cf_sealMemoryLimitedRecovery();
3047
- } catch {}
3048
- try {
3049
- await this.ctx.storage.delete(key);
3050
- } catch {}
3051
- } else {
3052
- const backoffSeconds = Math.min(300, 30 * strikes);
3053
- const nextTime = Math.floor(Date.now() / 1e3) + backoffSeconds;
3054
- this.scheduler.__DO_NOT_USE_WILL_BREAK__handleAlarmMemoryLimit({
3055
- callbacks: recoveryCallbacks,
3056
- sealed: false,
3057
- nextTime
3058
- });
3914
+ * Run Lifecycle's alarm event loop after the pending-destroy preamble.
3915
+ *
3916
+ * The alarm memory-limit circuit breaker (#1825) lives inside
3917
+ * `Lifecycle.alarm()`; capabilities and hosts opt into extra domain
3918
+ * policy via their `onMemoryLimit` / `onAlarmMemoryLimit` hooks and the
3919
+ * `recoveryLoop` schedule option.
3920
+ *
3921
+ * @remarks Use `this.schedule()` for named Agent callbacks. Reusable durable
3922
+ * work belongs in a capability that pushes jobs and implements `onJob()`.
3923
+ */
3924
+ async alarm() {
3925
+ if (await this._hasPendingDestroy()) {
3926
+ await this.destroy();
3927
+ return;
3059
3928
  }
3060
- try {
3061
- this._emit("alarm:memory_limit_reset", {
3062
- strikes,
3063
- limit,
3064
- sealed,
3065
- error: error instanceof Error ? error.message : String(error)
3066
- });
3067
- } catch {}
3068
- try {
3069
- await this._rearmAlarm();
3070
- } catch {}
3929
+ await this.lifecycle.alarm();
3071
3930
  }
3072
3931
  /**
3073
3932
  * Intercept incoming HTTP/WS requests whose URL contains a
@@ -3077,9 +3936,9 @@ var Agent = class Agent extends DurableObject {
3077
3936
  * Response, the framework resolves the facet and hands the
3078
3937
  * request off.
3079
3938
  *
3080
- * After a WebSocket upgrade completes, subsequent frames route
3081
- * directly to the child the parent is only on the path for the
3082
- * initial request.
3939
+ * The parent owns an upgraded WebSocket for its lifetime. Subsequent
3940
+ * frames wake the root parent, which forwards them to the child over
3941
+ * RPC and routes replies back to the native socket.
3083
3942
  *
3084
3943
  * @experimental The API surface may change before stabilizing.
3085
3944
  */
@@ -3104,409 +3963,80 @@ var Agent = class Agent extends DurableObject {
3104
3963
  }
3105
3964
  broadcast(msg, without) {
3106
3965
  if (this._isFacet) {
3107
- this._cf_broadcastToParentSubAgent(msg, without);
3966
+ this._dynamicAgents.broadcastToParent(msg, without);
3108
3967
  return;
3109
3968
  }
3110
- for (const connection of this.lifecycle.getConnections()) {
3969
+ for (const connection of this._webSockets.getConnections()) {
3111
3970
  if (without?.includes(connection.id)) continue;
3112
- if (this._cf_connectionHasSubAgentTarget(connection)) continue;
3971
+ if (this._dynamicAgents.connectionHasChildTarget(connection)) continue;
3113
3972
  connection.send(msg);
3114
3973
  }
3115
3974
  }
3116
3975
  getConnection(id) {
3117
- if (this._isFacet) {
3118
- const stored = this._cf_virtualSubAgentConnections.get(id);
3119
- if (stored) return this._cf_createSubAgentBridgeConnection(stored.meta);
3120
- return;
3121
- }
3122
- const connection = this.lifecycle.getConnection(id);
3123
- if (!connection || this._cf_connectionHasSubAgentTarget(connection)) return;
3976
+ if (this._isFacet) return this._dynamicAgents.getVirtualConnection(id);
3977
+ const connection = this._webSockets.getConnection(id);
3978
+ if (!connection || this._dynamicAgents.connectionHasChildTarget(connection)) return;
3124
3979
  return connection;
3125
3980
  }
3126
3981
  *getConnections(tag) {
3127
3982
  if (this._isFacet) {
3128
- for (const stored of this._cf_virtualSubAgentConnections.values()) if (!tag || stored.meta.tags.includes(tag)) yield this._cf_createSubAgentBridgeConnection(stored.meta);
3129
- return;
3130
- }
3131
- for (const connection of this.lifecycle.getConnections(tag)) {
3132
- if (this._cf_connectionHasSubAgentTarget(connection)) continue;
3133
- yield connection;
3134
- }
3135
- }
3136
- _cf_activeSubAgentBridge(connectionId) {
3137
- const context = this._cf_subAgentBridgeContext.getStore();
3138
- if (connectionId !== void 0 && context?.connectionId !== connectionId) return;
3139
- return context?.bridge;
3140
- }
3141
- /**
3142
- * Route a virtual sub-agent connection operation through its live frame
3143
- * bridge, or through the durable root Agent after that frame completes.
3144
- * All operations share one per-connection queue. Facet broadcasts wait for
3145
- * older queued operations; failures do not block later work.
3146
- */
3147
- _cf_routeSubAgentConnectionOperation(connectionId, operationName, operation) {
3148
- const activeBridge = this._cf_activeSubAgentBridge(connectionId);
3149
- const previousConnectionOperation = this._cf_subAgentConnectionOperationTails.get(connectionId);
3150
- let pending;
3151
- if (activeBridge && !previousConnectionOperation) try {
3152
- pending = Promise.resolve(operation(activeBridge)).then(() => {});
3153
- } catch (error) {
3154
- pending = Promise.reject(error);
3155
- }
3156
- else pending = (previousConnectionOperation ?? Promise.resolve()).then(async () => {
3157
- await operation(new RootSubAgentConnectionBridge(await this._rootAlarmOwner(), connectionId));
3158
- });
3159
- const completion = pending.catch((error) => {
3160
- this._cf_reportSubAgentConnectionOperationFailure(connectionId, operationName, error);
3161
- });
3162
- this._cf_subAgentConnectionOperationTails.set(connectionId, completion);
3163
- this.ctx.waitUntil(completion);
3164
- completion.then(() => {
3165
- if (this._cf_subAgentConnectionOperationTails.get(connectionId) === completion) this._cf_subAgentConnectionOperationTails.delete(connectionId);
3166
- });
3167
- }
3168
- /**
3169
- * Route a facet broadcast after every older connection operation.
3170
- *
3171
- * This barrier is intentionally one-way: facet startup can broadcast before
3172
- * a child connection has finished initializing its tags and protocol flags.
3173
- * Making those later connection operations wait would let the next frame
3174
- * observe stale root-owned metadata.
3175
- */
3176
- async _cf_routeSubAgentBroadcast(ownerPath, message, without, upstreamBridge) {
3177
- const activeBridge = upstreamBridge ?? this._cf_activeSubAgentBridge();
3178
- const previousOperations = /* @__PURE__ */ new Set([...this._cf_subAgentBroadcastOperationTail ? [this._cf_subAgentBroadcastOperationTail] : [], ...this._cf_subAgentConnectionOperationTails.values()]);
3179
- let pending;
3180
- if (activeBridge && previousOperations.size === 0) try {
3181
- pending = Promise.resolve(activeBridge.broadcast(ownerPath, message, without));
3182
- } catch (error) {
3183
- pending = Promise.reject(error);
3184
- }
3185
- else pending = Promise.all(previousOperations).then(async () => {
3186
- await (await this._rootAlarmOwner())._cf_broadcastToSubAgent(ownerPath, message, without);
3187
- });
3188
- const completion = pending.catch((error) => {
3189
- console.error("[Agent] Sub-agent broadcast operation failed:", {
3190
- operation: "broadcast",
3191
- error
3192
- });
3193
- });
3194
- this._cf_subAgentBroadcastOperationTail = completion;
3195
- this.ctx.waitUntil(completion);
3196
- completion.then(() => {
3197
- if (this._cf_subAgentBroadcastOperationTail === completion) this._cf_subAgentBroadcastOperationTail = void 0;
3198
- });
3199
- await completion;
3200
- }
3201
- _cf_reportSubAgentConnectionOperationFailure(connectionId, operation, error) {
3202
- console.error("[Agent] Sub-agent connection operation failed:", {
3203
- connectionId,
3204
- operation,
3205
- error
3206
- });
3207
- }
3208
- async _cf_broadcastToParentSubAgent(message, without) {
3209
- await this._cf_routeSubAgentBroadcast(this.selfPath, message, without);
3210
- }
3211
- async _cf_broadcastToSubAgent(ownerPath, message, without) {
3212
- if (this._isFacet) {
3213
- await this._cf_routeSubAgentBroadcast(ownerPath, message, without);
3214
- return;
3215
- }
3216
- for (const connection of this.lifecycle.getConnections()) {
3217
- if (without?.includes(connection.id)) continue;
3218
- const targetPath = this._cf_subAgentTargetPath(connection);
3219
- if (!targetPath) continue;
3220
- if (!this._isSameAgentPath(targetPath, ownerPath)) continue;
3221
- connection.send(message);
3222
- }
3223
- }
3224
- async _cf_subAgentConnectionMetas(ownerPath) {
3225
- const metas = [];
3226
- for (const connection of this.lifecycle.getConnections()) {
3227
- const meta = this._cf_subAgentConnectionMetaForPath(connection, ownerPath);
3228
- if (meta) metas.push(meta);
3229
- }
3230
- return metas;
3231
- }
3232
- async _cf_sendToSubAgentConnection(connectionId, message) {
3233
- const connection = this.lifecycle.getConnection(connectionId);
3234
- if (!connection || !this._cf_connectionHasSubAgentTarget(connection)) return;
3235
- connection.send(message);
3236
- }
3237
- async _cf_closeSubAgentConnection(connectionId, code, reason) {
3238
- const connection = this.lifecycle.getConnection(connectionId);
3239
- if (!connection || !this._cf_connectionHasSubAgentTarget(connection)) return;
3240
- connection.close(code, reason);
3241
- }
3242
- async _cf_setSubAgentConnectionState(connectionId, state) {
3243
- const connection = this.lifecycle.getConnection(connectionId);
3244
- if (!connection || !this._cf_connectionHasSubAgentTarget(connection)) return null;
3245
- this._ensureConnectionWrapped(connection);
3246
- connection.setState(state);
3247
- return this._cf_getForwardedSubAgentState(connection);
3248
- }
3249
- _cf_subAgentConnectionMetaForPath(connection, ownerPath) {
3250
- this._ensureConnectionWrapped(connection);
3251
- const outerUri = this._unsafe_getConnectionFlag(connection, CF_SUB_AGENT_OUTER_URL_KEY);
3252
- if (typeof outerUri !== "string") return null;
3253
- const target = this._cf_subAgentPathFromOuterUri(outerUri, ownerPath);
3254
- if (!target) return null;
3255
- const raw = this._cf_getRawConnectionState(connection);
3256
- const rawTags = raw != null && typeof raw === "object" ? raw[CF_SUB_AGENT_TAGS_KEY] : void 0;
3257
- const tags = Array.isArray(rawTags) ? rawTags.filter((tag) => typeof tag === "string") : [...connection.tags];
3258
- return {
3259
- id: connection.id,
3260
- uri: target.uri,
3261
- tags,
3262
- state: this._cf_getForwardedSubAgentState(connection)
3263
- };
3264
- }
3265
- _cf_subAgentTargetPath(connection) {
3266
- this._ensureConnectionWrapped(connection);
3267
- const outerUri = this._unsafe_getConnectionFlag(connection, CF_SUB_AGENT_OUTER_URL_KEY);
3268
- if (typeof outerUri !== "string") return null;
3269
- return this._cf_subAgentPathFromOuterUri(outerUri)?.path ?? null;
3270
- }
3271
- _cf_subAgentPathFromOuterUri(outerUri, stopAt) {
3272
- const ctx = this.ctx;
3273
- const knownClasses = ctx.exports ? Object.keys(ctx.exports) : void 0;
3274
- const path = [...this.selfPath];
3275
- let currentUrl = outerUri;
3276
- while (true) {
3277
- const match = parseSubAgentPath(currentUrl, { knownClasses });
3278
- if (!match) break;
3279
- path.push({
3280
- className: match.childClass,
3281
- name: match.childName
3282
- });
3283
- const rewritten = new URL(currentUrl);
3284
- rewritten.pathname = match.remainingPath;
3285
- currentUrl = rewritten.toString();
3286
- if (stopAt && this._isSameAgentPath(path, stopAt)) return {
3287
- path,
3288
- uri: currentUrl
3289
- };
3290
- }
3291
- if (path.length === this.selfPath.length) return null;
3292
- if (stopAt) return null;
3293
- return {
3294
- path,
3295
- uri: currentUrl
3296
- };
3297
- }
3298
- _isSameAgentPath(a, b) {
3299
- if (a.length !== b.length) return false;
3300
- return a.every((step, index) => step.className === b[index]?.className && step.name === b[index]?.name);
3301
- }
3302
- _cf_connectionHasSubAgentTarget(connection) {
3303
- this._ensureConnectionWrapped(connection);
3304
- return typeof this._unsafe_getConnectionFlag(connection, CF_SUB_AGENT_OUTER_URL_KEY) === "string";
3305
- }
3306
- _cf_connectionTargetsSubAgent(connection) {
3307
- if (!connection.uri) return false;
3308
- const ctx = this.ctx;
3309
- return parseSubAgentPath(connection.uri, { knownClasses: ctx.exports ? Object.keys(ctx.exports) : void 0 }) !== null;
3310
- }
3311
- /**
3312
- * Returns true when the current request is addressed to a child facet of
3313
- * this agent rather than to this agent itself.
3314
- *
3315
- * Chat-style subclasses wrap `onConnect` before the base Agent forwarding
3316
- * wrapper runs, so they need a request-level check to avoid sending their
3317
- * own protocol frames on sockets that are about to be forwarded to a child.
3318
- */
3319
- _cf_requestTargetsSubAgent(request) {
3320
- const ctx = this.ctx;
3321
- return parseSubAgentPath(request.url, { knownClasses: ctx.exports ? Object.keys(ctx.exports) : void 0 }) !== null;
3322
- }
3323
- async _cf_forwardSubAgentWebSocketConnect(connection, request, options) {
3324
- const routed = await this._cf_resolveSubAgentConnection(connection, request, options);
3325
- if (!routed) return false;
3326
- await routed.child._cf_handleSubAgentWebSocketConnect(this._cf_createSubAgentConnectionBridge(connection), routed.meta);
3327
- return true;
3328
- }
3329
- _cf_createSubAgentConnectionBridge(connection) {
3330
- const upstreamBroadcastBridge = this._isFacet ? this._cf_activeSubAgentBridge(connection.id) : void 0;
3331
- return new SubAgentConnectionBridge(connection, (ownerPath, message, without) => {
3332
- if (upstreamBroadcastBridge) return this._cf_routeSubAgentBroadcast(ownerPath, message, without, upstreamBroadcastBridge);
3333
- return this._cf_broadcastToSubAgent(ownerPath, message, without);
3334
- });
3335
- }
3336
- async _cf_forwardSubAgentWebSocketMessage(connection, message, replyBridge) {
3337
- const routed = await this._cf_resolveSubAgentConnection(connection);
3338
- if (!routed) return false;
3339
- const bridge = this._cf_createSubAgentConnectionBridge(connection);
3340
- await routed.child._cf_handleSubAgentWebSocketMessage(message, bridge, routed.meta, replyBridge ?? bridge);
3341
- return true;
3342
- }
3343
- async _cf_forwardSubAgentWebSocketClose(connection, code, reason, wasClean) {
3344
- const routed = await this._cf_resolveSubAgentConnection(connection);
3345
- if (!routed) return false;
3346
- await routed.child._cf_handleSubAgentWebSocketClose(code, reason, wasClean, this._cf_createSubAgentConnectionBridge(connection), routed.meta);
3347
- return true;
3348
- }
3349
- async _cf_resolveSubAgentConnection(connection, request, options = { gate: false }) {
3350
- this._ensureConnectionWrapped(connection);
3351
- const outerUri = this._unsafe_getConnectionFlag(connection, CF_SUB_AGENT_OUTER_URL_KEY);
3352
- const uri = typeof outerUri === "string" ? outerUri : connection.uri;
3353
- if (!uri) return null;
3354
- const ctx = this.ctx;
3355
- let match = parseSubAgentPath(uri, { knownClasses: ctx.exports ? Object.keys(ctx.exports) : void 0 });
3356
- if (!match) return null;
3357
- if (this._ParentClass.name === match.childClass && this.name === match.childName) {
3358
- const tailUri = new URL(uri);
3359
- tailUri.pathname = match.remainingPath;
3360
- match = parseSubAgentPath(tailUri.toString(), { knownClasses: ctx.exports ? Object.keys(ctx.exports) : void 0 });
3361
- if (!match) return null;
3983
+ yield* this._dynamicAgents.getVirtualConnections(tag);
3984
+ return;
3362
3985
  }
3363
- let forwardReq = request;
3364
- if (request && options.gate) {
3365
- const decision = await this.onBeforeSubAgent(request, {
3366
- className: match.childClass,
3367
- name: match.childName
3368
- });
3369
- if (decision instanceof Response) {
3370
- connection.close(1008, "Sub-agent connection rejected");
3371
- return null;
3372
- }
3373
- forwardReq = decision instanceof Request ? decision : request;
3986
+ for (const connection of this._webSockets.getConnections(tag)) {
3987
+ if (this._dynamicAgents.connectionHasChildTarget(connection)) continue;
3988
+ yield connection;
3374
3989
  }
3375
- const child = await this._cf_resolveSubAgent(match.childClass, match.childName);
3376
- const childUri = new URL(forwardReq?.url ?? uri);
3377
- childUri.pathname = match.remainingPath;
3378
- const raw = this._cf_getRawConnectionState(connection);
3379
- const rawTags = raw != null && typeof raw === "object" ? raw[CF_SUB_AGENT_TAGS_KEY] : void 0;
3380
- const tags = Array.isArray(rawTags) ? rawTags.filter((tag) => typeof tag === "string") : [...connection.tags];
3381
- return {
3382
- child,
3383
- meta: {
3384
- id: connection.id,
3385
- uri: childUri.toString(),
3386
- tags,
3387
- state: this._cf_getForwardedSubAgentState(connection),
3388
- requestHeaders: forwardReq ? [...forwardReq.headers] : void 0
3389
- }
3390
- };
3391
3990
  }
3392
- async _cf_handleSubAgentWebSocketConnect(bridge, meta) {
3393
- await this._cf_runWithSubAgentBridge(bridge, meta.id, async () => {
3394
- const connection = this._cf_createSubAgentBridgeConnection(meta);
3395
- const request = new Request(meta.uri ?? "http://placeholder/", { headers: meta.requestHeaders });
3396
- if (await this._cf_forwardSubAgentWebSocketConnect(connection, request, { gate: true })) return;
3397
- if (this.shouldConnectionBeReadonly(connection, { request })) this.setConnectionReadonly(connection, true);
3398
- if (!this.shouldSendProtocolMessages(connection, { request })) this._setConnectionNoProtocol(connection);
3399
- const childTags = await this.getConnectionTags(connection, { request });
3400
- connection.tags = [connection.id, ...childTags.filter((tag) => tag !== connection.id)];
3401
- this._cf_storeVirtualSubAgentConnection(connection);
3402
- await this.onConnect(connection, { request });
3403
- this._cf_storeVirtualSubAgentConnection(connection);
3404
- });
3991
+ async _cf_broadcastToSubAgent(ownerPath, message, without) {
3992
+ await this._dynamicAgents.broadcastToPath(ownerPath, message, without);
3405
3993
  }
3406
- async _cf_handleSubAgentWebSocketMessage(message, bridge, meta, replyBridge = bridge) {
3407
- const connection = this._cf_createSubAgentBridgeConnection(meta);
3408
- this._cf_storeVirtualSubAgentConnection(connection);
3409
- const replyContext = { bridge: replyBridge };
3410
- try {
3411
- await subAgentRpcReplyContext.run(replyContext, () => this._cf_runWithSubAgentBridge(bridge, meta.id, () => this.onMessage(connection, message)));
3412
- } finally {
3413
- replyContext.bridge = void 0;
3414
- }
3994
+ _cf_subAgentConnectionMetas(ownerPath) {
3995
+ return this._dynamicAgents.connectionMetas(ownerPath);
3415
3996
  }
3416
- async _cf_handleSubAgentWebSocketClose(code, reason, wasClean, bridge, meta) {
3417
- const connection = this._cf_createSubAgentBridgeConnection(meta);
3418
- this._cf_storeVirtualSubAgentConnection(connection);
3419
- await this._cf_runWithSubAgentBridge(bridge, meta.id, () => this.onClose(connection, code, reason, wasClean));
3420
- this._cf_virtualSubAgentConnections.delete(meta.id);
3997
+ _cf_sendToSubAgentConnection(connectionId, message) {
3998
+ return this._dynamicAgents.sendToConnection(connectionId, message);
3421
3999
  }
3422
- async _cf_runWithSubAgentBridge(bridge, connectionId, fn) {
3423
- const context = {
3424
- bridge,
3425
- connectionId
3426
- };
3427
- try {
3428
- return await this._cf_subAgentBridgeContext.run(context, fn);
3429
- } finally {
3430
- context.bridge = void 0;
3431
- }
4000
+ _cf_closeSubAgentConnection(connectionId, code, reason) {
4001
+ return this._dynamicAgents.closeConnection(connectionId, code, reason);
3432
4002
  }
3433
- _cf_createSubAgentBridgeConnection(meta) {
3434
- let stored = this._cf_virtualSubAgentConnections.get(meta.id);
3435
- if (stored) {
3436
- stored.meta = meta;
3437
- if (stored.connection) {
3438
- stored.connection.uri = meta.uri;
3439
- stored.connection.tags = meta.tags;
3440
- return stored.connection;
3441
- }
3442
- } else {
3443
- stored = { meta };
3444
- this._cf_virtualSubAgentConnections.set(meta.id, stored);
3445
- }
3446
- const owner = this;
3447
- const getStored = () => this._cf_virtualSubAgentConnections.get(meta.id) ?? stored;
3448
- const updateStoredState = (nextState) => {
3449
- const current = this._cf_virtualSubAgentConnections.get(meta.id);
3450
- if (current) current.meta = {
3451
- ...current.meta,
3452
- state: nextState
3453
- };
3454
- };
3455
- const connection = {
3456
- id: meta.id,
3457
- uri: meta.uri,
3458
- tags: meta.tags,
3459
- get state() {
3460
- return getStored().meta.state;
3461
- },
3462
- setState(next) {
3463
- const currentState = getStored().meta.state;
3464
- const state = typeof next === "function" ? next(currentState) : next;
3465
- updateStoredState(state);
3466
- owner._cf_routeSubAgentConnectionOperation(meta.id, "setState", (bridge) => bridge.setState(state));
3467
- return state;
3468
- },
3469
- send(message) {
3470
- owner._cf_routeSubAgentConnectionOperation(meta.id, "send", (bridge) => bridge.send(message));
3471
- },
3472
- close(code, reason) {
3473
- owner._cf_routeSubAgentConnectionOperation(meta.id, "close", (bridge) => bridge.close(code, reason));
3474
- },
3475
- addEventListener() {},
3476
- removeEventListener() {}
3477
- };
3478
- stored.connection = connection;
3479
- this._ensureConnectionWrapped(connection);
3480
- return connection;
4003
+ _cf_setSubAgentConnectionState(connectionId, state) {
4004
+ return this._dynamicAgents.setConnectionState(connectionId, state);
3481
4005
  }
3482
- _cf_storeVirtualSubAgentConnection(connection) {
3483
- this._unsafe_setConnectionFlag(connection, CF_SUB_AGENT_TAGS_KEY, [...connection.tags]);
3484
- const stored = this._cf_virtualSubAgentConnections.get(connection.id);
3485
- this._cf_virtualSubAgentConnections.set(connection.id, {
3486
- meta: {
3487
- id: connection.id,
3488
- uri: connection.uri,
3489
- tags: [...connection.tags],
3490
- state: this._cf_getRawConnectionState(connection)
3491
- },
3492
- connection: stored?.connection ?? connection
3493
- });
4006
+ _cf_connectionTargetsSubAgent(connection) {
4007
+ return this._dynamicAgents.connectionTargetsChild(connection);
4008
+ }
4009
+ /**
4010
+ * Returns true when the current request is addressed to a child facet of
4011
+ * this agent rather than to this agent itself.
4012
+ *
4013
+ * Chat-style subclasses wrap `onConnect` before the base Agent forwarding
4014
+ * wrapper runs, so they need a request-level check to avoid sending their
4015
+ * own protocol frames on sockets that are about to be forwarded to a child.
4016
+ */
4017
+ _cf_requestTargetsSubAgent(request) {
4018
+ return this._dynamicAgents.requestTargetsChild(request);
3494
4019
  }
3495
- async _cf_hydrateSubAgentConnectionsFromRoot() {
3496
- if (!this._isFacet || this._parentPath.length === 0) return;
3497
- if (this._cf_rootResolvesToSelf()) return;
3498
- const metas = await (await this._rootAlarmOwner())._cf_subAgentConnectionMetas(this.selfPath);
3499
- for (const meta of metas) this._cf_virtualSubAgentConnections.set(meta.id, { meta });
4020
+ _cf_forwardSubAgentWebSocketConnect(connection, request, options) {
4021
+ return this._dynamicAgents.forwardWebSocketConnect(connection, request, options);
3500
4022
  }
3501
- _cf_getRawConnectionState(connection) {
3502
- this._ensureConnectionWrapped(connection);
3503
- return this._rawStateAccessors.get(connection)?.getRaw() ?? null;
4023
+ _cf_forwardSubAgentWebSocketMessage(connection, message, replyBridge) {
4024
+ return this._dynamicAgents.forwardWebSocketMessage(connection, message, replyBridge);
3504
4025
  }
3505
- _cf_getForwardedSubAgentState(connection) {
3506
- const raw = this._cf_getRawConnectionState(connection);
3507
- if (raw == null || typeof raw !== "object") return raw;
3508
- const { [CF_SUB_AGENT_OUTER_URL_KEY]: _, ...rest } = raw;
3509
- return Object.keys(rest).length > 0 ? rest : null;
4026
+ _cf_forwardSubAgentWebSocketClose(connection, code, reason, wasClean) {
4027
+ return this._dynamicAgents.forwardWebSocketClose(connection, code, reason, wasClean);
4028
+ }
4029
+ _cf_handleSubAgentWebSocketConnect(bridge, meta) {
4030
+ return this._dynamicAgents.handleWebSocketConnect(bridge, meta);
4031
+ }
4032
+ _cf_handleSubAgentWebSocketMessage(message, bridge, meta, replyBridge = bridge) {
4033
+ return this._dynamicAgents.handleWebSocketMessage(message, bridge, meta, replyBridge);
4034
+ }
4035
+ _cf_handleSubAgentWebSocketClose(code, reason, wasClean, bridge, meta) {
4036
+ return this._dynamicAgents.handleWebSocketClose(code, reason, wasClean, bridge, meta);
4037
+ }
4038
+ _cf_hydrateSubAgentConnectionsFromRoot() {
4039
+ return this._dynamicAgents.hydrateConnectionsFromRoot();
3510
4040
  }
3511
4041
  /**
3512
4042
  * Parent-side middleware hook. Fires before a request is
@@ -3543,7 +4073,7 @@ var Agent = class Agent extends DurableObject {
3543
4073
  * class Inbox extends Agent {
3544
4074
  * override async onBeforeSubAgent(req, { className, name }) {
3545
4075
  * // Strict registry gate
3546
- * if (!this.hasSubAgent(className, name)) {
4076
+ * if (!this.dynamicAgents.has(className, name)) {
3547
4077
  * return new Response("Not found", { status: 404 });
3548
4078
  * }
3549
4079
  * }
@@ -3557,27 +4087,8 @@ var Agent = class Agent extends DurableObject {
3557
4087
  *
3558
4088
  * @internal
3559
4089
  */
3560
- async _cf_forwardToFacet(req, match) {
3561
- let fetcher;
3562
- try {
3563
- fetcher = await this._cf_resolveSubAgent(match.childClass, match.childName);
3564
- } catch (err) {
3565
- const message = err instanceof Error ? err.message : String(err);
3566
- console.error("[agents] sub-agent route failed:", message);
3567
- if (/null character/i.test(message) || /reserved/i.test(message)) return new Response("Bad Request", { status: 400 });
3568
- return new Response("Not Found", { status: 404 });
3569
- }
3570
- const rewritten = new URL(req.url);
3571
- rewritten.pathname = match.remainingPath;
3572
- const forwardedHeaders = new Headers(req.headers);
3573
- const forwardedInit = {
3574
- method: req.method,
3575
- headers: forwardedHeaders
3576
- };
3577
- if (req.headers.get("Upgrade")?.toLowerCase() === "websocket") forwardedHeaders.set(SUB_AGENT_OUTER_URL_HEADER, req.url);
3578
- if (req.body && req.method !== "GET" && req.method !== "HEAD") forwardedInit.body = req.body;
3579
- const forwarded = new Request(rewritten, forwardedInit);
3580
- return fetcher.fetch(forwarded);
4090
+ _cf_forwardToFacet(req, match) {
4091
+ return this._dynamicAgents.forward(req, match);
3581
4092
  }
3582
4093
  /**
3583
4094
  * Bridge method used by `getSubAgentByName`. Resolves the facet
@@ -3586,9 +4097,8 @@ var Agent = class Agent extends DurableObject {
3586
4097
  *
3587
4098
  * @internal
3588
4099
  */
3589
- async _cf_invokeSubAgent(className, name, method, args) {
3590
- const stub = await this._cf_resolveSubAgent(className, name);
3591
- return await this._cf_invokeStubMethod(stub, className, method, args);
4100
+ _cf_invokeSubAgent(className, name, method, args) {
4101
+ return this._dynamicAgents.invoke(className, name, method, args);
3592
4102
  }
3593
4103
  /**
3594
4104
  * Bridge method used by `parentAgent()` when the requested parent is
@@ -3598,20 +4108,8 @@ var Agent = class Agent extends DurableObject {
3598
4108
  *
3599
4109
  * @internal
3600
4110
  */
3601
- async _cf_invokeSubAgentPath(path, method, args) {
3602
- const [self, next, ...rest] = path;
3603
- if (!self) throw new Error(`Sub-agent path invocation requires a non-empty path.`);
3604
- const ownClassName = this.constructor.name;
3605
- if (self.className !== ownClassName || self.name !== this.name) throw new Error(`Sub-agent path invocation reached ${ownClassName}("${this.name}") but expected ${self.className}("${self.name}").`);
3606
- if (!next) return await this._cf_invokeStubMethod(this, this.constructor.name, method, args);
3607
- const child = await this._cf_resolveSubAgent(next.className, next.name);
3608
- if (rest.length === 0) return await this._cf_invokeStubMethod(child, next.className, method, args);
3609
- return await child._cf_invokeSubAgentPath([next, ...rest], method, args);
3610
- }
3611
- async _cf_invokeStubMethod(stub, className, method, args) {
3612
- const handle = stub;
3613
- if (typeof handle[method] !== "function") throw new Error(`Method "${method}" not found on ${className}.`);
3614
- return await handle[method](...args);
4111
+ _cf_invokeSubAgentPath(path, method, args) {
4112
+ return this._dynamicAgents.invokePath(path, method, args);
3615
4113
  }
3616
4114
  /**
3617
4115
  * Initialize this agent as a facet in a single RPC.
@@ -3637,18 +4135,8 @@ var Agent = class Agent extends DurableObject {
3637
4135
  *
3638
4136
  * @internal Called by {@link subAgent}.
3639
4137
  */
3640
- async _cf_initAsFacet(name, parentPath = [], identityName = name) {
3641
- const routedName = this.lifecycle.name;
3642
- if (routedName !== identityName) throw new Error(`Facet bootstrap mismatch: expected routed identity "${identityName}" but got "${routedName}". This usually means the parent passed the wrong id to ctx.facets.get(). See _cf_resolveSubAgent.`);
3643
- this._isFacet = true;
3644
- this._facetName = name;
3645
- this._parentPath = parentPath;
3646
- await Promise.all([
3647
- this.ctx.storage.put("cf_agents_is_facet", true),
3648
- this.ctx.storage.put("cf_agents_facet_name", name),
3649
- this.ctx.storage.put("cf_agents_parent_path", parentPath)
3650
- ]);
3651
- await this.__unsafe_ensureInitialized();
4138
+ _cf_initAsFacet(name, parentPath = [], identityName = name) {
4139
+ return this._dynamicAgents.init(name, parentPath, identityName);
3652
4140
  }
3653
4141
  get name() {
3654
4142
  const routedName = this.lifecycle.name;
@@ -3796,9 +4284,11 @@ var Agent = class Agent extends DurableObject {
3796
4284
  * const searcher = await this.subAgent(SearchAgent, "main-search");
3797
4285
  * const results = await searcher.search("cloudflare agents");
3798
4286
  * ```
4287
+ *
4288
+ * @deprecated Use {@link Agent.dynamicAgents | this.dynamicAgents.get()} instead.
3799
4289
  */
3800
4290
  async subAgent(cls, name) {
3801
- return await this._cf_resolveSubAgent(cls.name, name);
4291
+ return this.dynamicAgents.get(cls, name);
3802
4292
  }
3803
4293
  async onAgentToolStart(_run) {}
3804
4294
  async onAgentToolFinish(_run, _result) {}
@@ -5185,46 +5675,22 @@ var Agent = class Agent extends DurableObject {
5185
5675
  *
5186
5676
  * @internal
5187
5677
  */
5188
- async _cf_resolveSubAgent(className, name) {
5189
- const ctx = this.ctx;
5190
- if (!ctx.facets || !ctx.exports) throw new Error("subAgent() is not supported in this runtime — `ctx.facets` / `ctx.exports` are unavailable. Update to the latest `compatibility_date` in your wrangler.jsonc.");
5191
- if (camelCaseToKebabCase(className) === "sub") throw new Error(`Sub-agent class name "${className}" kebab-cases to "sub", which collides with the reserved URL separator — rename the class (e.g. "SubThing" or "Subtask").`);
5192
- const Cls = ctx.exports[className];
5193
- if (!Cls) throw new Error(`Sub-agent class "${className}" not found in worker exports. Make sure the class is exported from your worker entry point and that the export name matches the class name.`);
5194
- if (name.includes("\0")) throw new Error(`Sub-agent name contains null character (\\0), which is reserved.`);
5195
- const facetKey = `${className}\0${name}`;
5196
- const childParentPath = this.selfPath;
5197
- const childPath = [...childParentPath, {
5198
- className,
5199
- name
5200
- }];
5201
- const rootClassName = this._parentPath[0]?.className ?? this.constructor.name;
5202
- const rootNs = ctx.exports[rootClassName];
5203
- if (!rootNs?.idFromName) {
5204
- const minificationHint = /^_*[a-z][a-z0-9]{0,2}$/.test(rootClassName) ? ` The class name "${rootClassName}" looks minified — make sure your bundler preserves class names (e.g. esbuild's \`keepNames: true\`).` : "";
5205
- throw new Error(`Sub-agent bootstrap requires the root agent class "${rootClassName}" to be available as a Durable Object namespace, but ctx.exports["${rootClassName}"] is missing or doesn't expose idFromName.${minificationHint} Make sure the root agent class is exported under that class name and registered in your wrangler.jsonc durable_objects.bindings.`);
5206
- }
5207
- const identity = await this._cf_subAgentIdentity(className, name, childPath);
5208
- const facetId = rootNs.idFromName(identity.name);
5209
- const stub = ctx.facets.get(facetKey, () => ({
5210
- class: Cls,
5211
- id: facetId
5212
- }));
5213
- this._recordSubAgent(className, name, identity);
5214
- try {
5215
- await runInInvocation({
5216
- agent: this,
5217
- connection: void 0,
5218
- request: void 0,
5219
- email: void 0
5220
- }, async () => {
5221
- await stub._cf_initAsFacet(name, childParentPath, identity.name);
5222
- });
5223
- } catch (error) {
5224
- if (!identity.existing) this._forgetSubAgent(className, name);
5225
- throw error;
5226
- }
5227
- return stub;
5678
+ _cf_resolveSubAgent(className, name) {
5679
+ return this._dynamicAgents.resolve(className, name);
5680
+ }
5681
+ /**
5682
+ * Run `body` in a fresh invocation scope with no native request/
5683
+ * connection context attached, so a child-facet RPC never sees
5684
+ * parent-owned I/O handles.
5685
+ * @internal
5686
+ */
5687
+ _runFacetInitInvocation(body) {
5688
+ return runInInvocation({
5689
+ agent: this,
5690
+ connection: void 0,
5691
+ request: void 0,
5692
+ email: void 0
5693
+ }, body);
5228
5694
  }
5229
5695
  /**
5230
5696
  * Forcefully abort a running sub-agent. The child stops executing
@@ -5237,12 +5703,11 @@ var Agent = class Agent extends DurableObject {
5237
5703
  * @param cls The Agent subclass used when creating the child
5238
5704
  * @param name Name of the child to abort
5239
5705
  * @param reason Error thrown to pending/future RPC callers
5706
+ *
5707
+ * @deprecated Use {@link Agent.dynamicAgents | this.dynamicAgents.abort()} instead.
5240
5708
  */
5241
5709
  abortSubAgent(cls, name, reason) {
5242
- const ctx = this.ctx;
5243
- if (!ctx.facets) throw new Error("abortSubAgent() is not supported in this runtime — `ctx.facets` is unavailable. Update to the latest `compatibility_date` in your wrangler.jsonc.");
5244
- const facetKey = `${cls.name}\0${name}`;
5245
- ctx.facets.abort(facetKey, reason);
5710
+ this.dynamicAgents.abort(cls, name, reason);
5246
5711
  }
5247
5712
  /**
5248
5713
  * Delete a sub-agent: abort it if running, then permanently wipe its
@@ -5252,117 +5717,18 @@ var Agent = class Agent extends DurableObject {
5252
5717
  *
5253
5718
  * @param cls The Agent subclass used when creating the child
5254
5719
  * @param name Name of the child to delete
5720
+ *
5721
+ * @deprecated Use {@link Agent.dynamicAgents | this.dynamicAgents.delete()} instead.
5255
5722
  */
5256
- async deleteSubAgent(cls, name) {
5257
- const ctx = this.ctx;
5258
- if (!ctx.facets) throw new Error("deleteSubAgent() is not supported in this runtime — `ctx.facets` is unavailable. Update to the latest `compatibility_date` in your wrangler.jsonc.");
5259
- const facetKey = `${cls.name}\0${name}`;
5260
- const childPath = [...this.selfPath, {
5261
- className: cls.name,
5262
- name
5263
- }];
5264
- if (this._isFacet) await (await this._rootAlarmOwner())._cf_cleanupFacetPrefix(childPath);
5265
- else await this._cf_cleanupFacetPrefix(childPath);
5266
- try {
5267
- ctx.facets.delete(facetKey);
5268
- } catch {}
5269
- this._forgetSubAgent(cls.name, name);
5270
- }
5271
- _addColumnIfNotExists(sql) {
5272
- try {
5273
- this.ctx.storage.sql.exec(sql);
5274
- } catch (e) {
5275
- if (!(e instanceof Error ? e.message : String(e)).toLowerCase().includes("duplicate column")) throw e;
5276
- }
5277
- }
5278
- /** @internal */
5279
- _ensureSubAgentRegistry() {
5280
- if (this._subAgentRegistryReady) return;
5281
- this.sql`
5282
- CREATE TABLE IF NOT EXISTS cf_agents_sub_agents (
5283
- class TEXT NOT NULL,
5284
- name TEXT NOT NULL,
5285
- created_at INTEGER NOT NULL,
5286
- identity_version TEXT,
5287
- identity_name TEXT,
5288
- PRIMARY KEY (class, name)
5289
- )
5290
- `;
5291
- this._addColumnIfNotExists("ALTER TABLE cf_agents_sub_agents ADD COLUMN identity_version TEXT");
5292
- this._addColumnIfNotExists("ALTER TABLE cf_agents_sub_agents ADD COLUMN identity_name TEXT");
5293
- this._subAgentRegistryReady = true;
5294
- }
5295
- /** @internal */
5296
- _recordSubAgent(className, name, identity) {
5297
- this._ensureSubAgentRegistry();
5298
- this.sql`
5299
- INSERT OR IGNORE INTO cf_agents_sub_agents
5300
- (class, name, created_at, identity_version, identity_name)
5301
- VALUES
5302
- (${className}, ${name}, ${Date.now()}, ${identity.version}, ${identity.name})
5303
- `;
5304
- }
5305
- /** @internal */
5306
- _subAgentRegistryRow(className, name) {
5307
- this._ensureSubAgentRegistry();
5308
- return this.sql`
5309
- SELECT identity_version, identity_name
5310
- FROM cf_agents_sub_agents
5311
- WHERE class = ${className} AND name = ${name}
5312
- LIMIT 1
5313
- `[0] ?? null;
5314
- }
5315
- async _cf_subAgentIdentity(className, name, childPath) {
5316
- const row = this._subAgentRegistryRow(className, name);
5317
- if (row) {
5318
- if (row.identity_version === SUB_AGENT_IDENTITY_VERSION_PATH_V2 && typeof row.identity_name === "string") return {
5319
- version: SUB_AGENT_IDENTITY_VERSION_PATH_V2,
5320
- name: row.identity_name,
5321
- existing: true
5322
- };
5323
- return {
5324
- version: SUB_AGENT_IDENTITY_VERSION_LEGACY,
5325
- name,
5326
- existing: true
5327
- };
5328
- }
5329
- return {
5330
- version: SUB_AGENT_IDENTITY_VERSION_PATH_V2,
5331
- name: pathV2IdentityName(name, await sha256Hex(JSON.stringify(childPath))),
5332
- existing: false
5333
- };
5334
- }
5335
- /** @internal */
5336
- _forgetSubAgent(className, name) {
5337
- this._ensureSubAgentRegistry();
5338
- this.sql`
5339
- DELETE FROM cf_agents_sub_agents
5340
- WHERE class = ${className} AND name = ${name}
5341
- `;
5723
+ deleteSubAgent(cls, name) {
5724
+ return this.dynamicAgents.delete(cls, name);
5342
5725
  }
5343
5726
  hasSubAgent(classOrName, name) {
5344
- const className = typeof classOrName === "string" ? classOrName : classOrName.name;
5345
- this._ensureSubAgentRegistry();
5346
- return (this.sql`
5347
- SELECT COUNT(*) AS n FROM cf_agents_sub_agents
5348
- WHERE class = ${className} AND name = ${name}
5349
- `[0]?.n ?? 0) > 0;
5727
+ return typeof classOrName === "string" ? this.dynamicAgents.has(classOrName, name) : this.dynamicAgents.has(classOrName, name);
5350
5728
  }
5351
5729
  listSubAgents(classOrName) {
5352
- const className = typeof classOrName === "string" ? classOrName : classOrName?.name;
5353
- this._ensureSubAgentRegistry();
5354
- return (className ? this.sql`
5355
- SELECT class, name, created_at FROM cf_agents_sub_agents
5356
- WHERE class = ${className}
5357
- ORDER BY created_at ASC
5358
- ` : this.sql`
5359
- SELECT class, name, created_at FROM cf_agents_sub_agents
5360
- ORDER BY created_at ASC
5361
- `).map((r) => ({
5362
- className: r.class,
5363
- name: r.name,
5364
- createdAt: r.created_at
5365
- }));
5730
+ if (typeof classOrName === "string" || classOrName === void 0) return this.dynamicAgents.list(classOrName);
5731
+ return this.dynamicAgents.list(classOrName);
5366
5732
  }
5367
5733
  /**
5368
5734
  * Destroy the Agent, removing all state and scheduled tasks.
@@ -5392,7 +5758,7 @@ var Agent = class Agent extends DurableObject {
5392
5758
  await this.ctx.storage.deleteAll();
5393
5759
  this._destroyed = true;
5394
5760
  setTimeout(() => {
5395
- this.ctx.abort("destroyed");
5761
+ abortWithoutAlarmRetry(this.ctx, "destroyed");
5396
5762
  }, 0);
5397
5763
  this._emit("destroy");
5398
5764
  }
@@ -5423,7 +5789,12 @@ var Agent = class Agent extends DurableObject {
5423
5789
  }
5424
5790
  const destroyAt = Date.now() + DESTROY_ALARM_DELAY_MS;
5425
5791
  await this.ctx.storage.put(DESTROY_PENDING_KEY, destroyAt);
5426
- await this.lifecycle.rearmAlarm();
5792
+ await this.lifecycle.jobs.push({
5793
+ id: HOST_JOB_DESTROY_ID,
5794
+ fn: "destroy",
5795
+ time: destroyAt,
5796
+ exclusive: true
5797
+ });
5427
5798
  }
5428
5799
  /**
5429
5800
  * Whether a (deferred or interrupted) destroy is pending. Reads the
@@ -5444,32 +5815,14 @@ var Agent = class Agent extends DurableObject {
5444
5815
  * @returns True if the method is marked as callable
5445
5816
  */
5446
5817
  _isCallable(method) {
5447
- return callableMetadata.has(this[method]);
5818
+ return isCallableMethod(this[method]);
5448
5819
  }
5449
5820
  /**
5450
5821
  * Get all methods marked as callable on this Agent
5451
5822
  * @returns A map of method names to their metadata
5452
5823
  */
5453
5824
  getCallableMethods() {
5454
- const result = /* @__PURE__ */ new Map();
5455
- let prototype = Object.getPrototypeOf(this);
5456
- while (prototype && prototype !== Object.prototype) {
5457
- for (const name of Object.getOwnPropertyNames(prototype)) {
5458
- if (name === "constructor") continue;
5459
- if (result.has(name)) continue;
5460
- try {
5461
- const fn = prototype[name];
5462
- if (typeof fn === "function") {
5463
- const meta = callableMetadata.get(fn);
5464
- if (meta) result.set(name, meta);
5465
- }
5466
- } catch (e) {
5467
- if (!(e instanceof TypeError)) throw e;
5468
- }
5469
- }
5470
- prototype = Object.getPrototypeOf(prototype);
5471
- }
5472
- return result;
5825
+ return new Map(decoratedMethods(this));
5473
5826
  }
5474
5827
  /**
5475
5828
  * Start a workflow and track it in this Agent's database.
@@ -6447,7 +6800,6 @@ var Agent = class Agent extends DurableObject {
6447
6800
  }
6448
6801
  };
6449
6802
  Agent.options = {};
6450
- Agent._CF_OOM_ALARM_STRIKES_KEY = "cf_agents:oom_alarm_strikes";
6451
6803
  const wrappedClasses = /* @__PURE__ */ new Set();
6452
6804
  var _email = /* @__PURE__ */ new WeakMap();
6453
6805
  _Symbol$dispose = Symbol.dispose;
@@ -6542,11 +6894,9 @@ var StreamingResponse = class {
6542
6894
  this._id = id;
6543
6895
  }
6544
6896
  _send(response) {
6545
- const state = facetStreamingResponseDeliveryStates.get(this);
6546
- if (!state) return sendRpcResponseIfOpen(this._connection, response);
6547
- const delivery = sendFacetRpcResponseIfOpen(state.replyTarget, response);
6548
- trackFacetStreamingResponseDelivery(this, delivery.completion);
6549
- return delivery.sent;
6897
+ const facetSent = sendFacetStreamingResponse(this, response);
6898
+ if (facetSent !== null) return facetSent;
6899
+ return sendRpcResponseIfOpen(this._connection, response);
6550
6900
  }
6551
6901
  /**
6552
6902
  * Whether the stream has been closed (via end() or error())
@@ -6608,6 +6958,6 @@ var StreamingResponse = class {
6608
6958
  }
6609
6959
  };
6610
6960
  //#endregion
6611
- export { getCurrentAgent as a, callable as i, DEFAULT_AGENT_STATIC_OPTIONS as n, routeAgentEmail as o, StreamingResponse as r, unstable_callable as s, Agent as t };
6961
+ export { routeAgentEmail as a, getCurrentAgent as i, DEFAULT_AGENT_STATIC_OPTIONS as n, StreamingResponse as r, Agent as t };
6612
6962
 
6613
- //# sourceMappingURL=src-5W6JNKVb.js.map
6963
+ //# sourceMappingURL=src-DlSHshb2.js.map