agents 0.23.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-routing-DE5zmCQ8.d.ts → agent-routing-0DR0Tk2v.d.ts} +270 -190
- package/dist/agent-routing.d.ts +1 -1
- package/dist/agent-routing.js.map +1 -1
- package/dist/agent-tool-types.d.ts +13 -7
- package/dist/agent-tool-types.js +23 -1
- package/dist/agent-tool-types.js.map +1 -1
- package/dist/{agent-tools-y7zLfw4Q.js → agent-tools-CdJC-8Dc.js} +72 -9
- package/dist/agent-tools-CdJC-8Dc.js.map +1 -0
- package/dist/{agent-tools-DtXMTDGM.d.ts → agent-tools-YrATZJu2.d.ts} +29 -15
- package/dist/agent-tools.d.ts +6 -4
- package/dist/agent-tools.js.map +1 -1
- package/dist/ai-fm7xx1BK.js +312 -0
- package/dist/ai-fm7xx1BK.js.map +1 -0
- package/dist/base64-redaction-C1Uu29t5.js +59 -0
- package/dist/base64-redaction-C1Uu29t5.js.map +1 -0
- package/dist/browser/ai-sdk.d.ts +90 -0
- package/dist/browser/ai-sdk.js +51 -0
- package/dist/browser/ai-sdk.js.map +1 -0
- package/dist/browser/ai.d.ts +4 -4
- package/dist/browser/ai.js +1 -347
- package/dist/browser/index.d.ts +81 -79
- package/dist/browser/index.js +361 -2
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/tanstack-ai.d.ts +96 -1
- package/dist/browser/tanstack-ai.js +62 -2
- package/dist/browser/tanstack-ai.js.map +1 -1
- package/dist/browser-DE3d-RLk.d.ts +275 -0
- package/dist/browser-tool-CQ10e6Kj.js +480 -0
- package/dist/browser-tool-CQ10e6Kj.js.map +1 -0
- package/dist/browser-tool-DuNdlwZ9.d.ts +73 -0
- package/dist/{capability-BjSKYpzg.js → capability-B4WbF81e.js} +2 -1
- package/dist/capability-B4WbF81e.js.map +1 -0
- package/dist/{capability-runner-Be_-PLR1.d.ts → capability-runner-DBAnGr6L.d.ts} +45 -15
- package/dist/chat/index.d.ts +341 -232
- package/dist/chat/index.js +602 -72
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/react.d.ts +42 -2
- package/dist/chat/react.js +191 -35
- package/dist/chat/react.js.map +1 -1
- package/dist/chat/transport.d.ts +1 -1
- package/dist/chat/transport.js +1 -1
- package/dist/chat-sdk/index.d.ts +17 -3
- package/dist/chat-sdk/index.js +28 -2
- package/dist/chat-sdk/index.js.map +1 -1
- package/dist/client-4OlMIJx-.js +591 -0
- package/dist/client-4OlMIJx-.js.map +1 -0
- package/dist/{client-jagG8a9_.js → client-fyQrLGkD.js} +2 -2
- package/dist/{client-jagG8a9_.js.map → client-fyQrLGkD.js.map} +1 -1
- package/dist/client-je3koD9Z.d.ts +446 -0
- package/dist/client.d.ts +29 -283
- package/dist/client.js +2 -274
- package/dist/{connector-CptFKzRh.js → connector-58XDGvc3.js} +251 -120
- package/dist/connector-58XDGvc3.js.map +1 -0
- package/dist/{connector-CkQD4MK3.d.ts → connector-Cl-VqPmA.d.ts} +244 -146
- package/dist/context/index.d.ts +29 -0
- package/dist/context/index.js +64 -17
- package/dist/context/index.js.map +1 -1
- package/dist/current-agent-DhoDkSnH.js.map +1 -1
- package/dist/current-agent-vAk_5JMt.d.ts +93 -0
- package/dist/{current-agent-Da_C9a3b.d.ts → durable-object-lifecycle-HhgOMWq9.d.ts} +26 -127
- package/dist/harnesses/pi/index.d.ts +348 -0
- package/dist/harnesses/pi/index.js +754 -0
- package/dist/harnesses/pi/index.js.map +1 -0
- package/dist/images-iiTDSyRu.js +1631 -0
- package/dist/images-iiTDSyRu.js.map +1 -0
- package/dist/index-BreL0YrQ.d.ts +78 -0
- package/dist/index-kbSUIer3.d.ts +343 -0
- package/dist/index.d.ts +43 -41
- package/dist/index.js +2 -2
- package/dist/internal_context.d.ts +1 -1
- package/dist/lifecycle/index.d.ts +19 -17
- package/dist/lifecycle/index.js +2 -2
- package/dist/{lifecycle-CMRGjZdw.js → lifecycle-CFu6OV03.js} +73 -29
- package/dist/lifecycle-CFu6OV03.js.map +1 -0
- package/dist/mcp/client/index.d.ts +20 -20
- package/dist/mcp/client/index.js +1 -1
- package/dist/mcp/client/x402.js +21 -19
- package/dist/mcp/client/x402.js.map +1 -1
- package/dist/mcp/index.d.ts +35 -35
- package/dist/mcp/index.js +2 -2
- package/dist/{message-builder-BymO4N_D.js → message-builder-DAl9AhHH.js} +89 -4
- package/dist/message-builder-DAl9AhHH.js.map +1 -0
- package/dist/models/ai-sdk/index.d.ts +353 -0
- package/dist/models/ai-sdk/index.js +2443 -0
- package/dist/models/ai-sdk/index.js.map +1 -0
- package/dist/models/pi-ai/index.d.ts +313 -0
- package/dist/models/pi-ai/index.js +1795 -0
- package/dist/models/pi-ai/index.js.map +1 -0
- package/dist/observability/ai/index.js +20 -4
- package/dist/observability/ai/index.js.map +1 -1
- package/dist/{protocol-B0nh6KNf.js → protocol-kHsr-f0X.js} +26 -12
- package/dist/{protocol-B0nh6KNf.js.map → protocol-kHsr-f0X.js.map} +1 -1
- package/dist/queue/index.d.ts +123 -0
- package/dist/queue/index.js +2 -0
- package/dist/queue-BB-zfIYF.js +406 -0
- package/dist/queue-BB-zfIYF.js.map +1 -0
- package/dist/react.d.ts +31 -13
- package/dist/react.js +190 -71
- package/dist/react.js.map +1 -1
- package/dist/{retries-D9Ds-1lz.d.ts → retries-CV6AkXmQ.d.ts} +23 -8
- package/dist/retries.d.ts +12 -8
- package/dist/retries.js +19 -1
- package/dist/retries.js.map +1 -1
- package/dist/routing/index.d.ts +2 -2
- package/dist/routing/index.js +1 -1
- package/dist/{scheduler-DD9NdYbF.js → scheduler-DZ3xQfeK.js} +4 -11
- package/dist/scheduler-DZ3xQfeK.js.map +1 -0
- package/dist/{scheduler-Dwh85ZGl.d.ts → scheduler-RdACNXDP.d.ts} +6 -6
- package/dist/schedules/index.d.ts +1 -1
- package/dist/schedules/index.js +1 -1
- package/dist/serializable.d.ts +1 -1
- package/dist/sessions/index.d.ts +70 -3
- package/dist/sessions/index.js +276 -67
- package/dist/sessions/index.js.map +1 -1
- package/dist/skills/index.d.ts +29 -314
- package/dist/socket-address.d.ts +16 -0
- package/dist/socket-address.js +19 -0
- package/dist/socket-address.js.map +1 -0
- package/dist/{src-DlSHshb2.js → src-BZeu-SCD.js} +541 -500
- package/dist/src-BZeu-SCD.js.map +1 -0
- package/dist/state/index.d.ts +6 -0
- package/dist/state/index.js +137 -0
- package/dist/state/index.js.map +1 -0
- package/dist/streams/index.d.ts +1 -1
- package/dist/streams/index.js +1 -1
- package/dist/{streams-D6tJ0NN9.d.ts → streams-Cmkj9QfW.d.ts} +4 -2
- package/dist/{streams-DZKgAj9b.js → streams-DW-UCOUc.js} +9 -2
- package/dist/streams-DW-UCOUc.js.map +1 -0
- package/dist/sub-routing.d.ts +12 -12
- package/dist/tasks/index.d.ts +1 -1
- package/dist/tasks/index.js +1 -1
- package/dist/{tasks-ylZgBjhj.js → tasks-68DESxf0.js} +3 -3
- package/dist/{tasks-ylZgBjhj.js.map → tasks-68DESxf0.js.map} +1 -1
- package/dist/{tasks-BRJ5zgya.d.ts → tasks-9YxYtByh.d.ts} +5 -5
- package/dist/transport-PfFnt-Ld.d.ts +525 -0
- package/dist/transport-protocol-BxyngQ_R.js +25 -0
- package/dist/transport-protocol-BxyngQ_R.js.map +1 -0
- package/dist/transport-protocol-ZTew5Wso.d.ts +27 -0
- package/dist/types-BA4anrjI.d.ts +70 -0
- package/dist/voice/index.d.ts +2 -2
- package/dist/websockets/index.d.ts +195 -69
- package/dist/websockets/index.js +3 -2
- package/dist/websockets-Ci-LZFHH.js +1030 -0
- package/dist/websockets-Ci-LZFHH.js.map +1 -0
- package/dist/wire-types-CnMt6_HR.js.map +1 -1
- package/dist/wire-types-CrjKvDyi.d.ts +300 -0
- package/dist/workflow-types.d.ts +25 -25
- package/dist/workflows.d.ts +21 -21
- package/dist/workflows.js +1 -1
- package/dist/{ws-chat-transport-UNRIS2xl.d.ts → ws-chat-transport-BbT1A5Lc.d.ts} +51 -2
- package/dist/{ws-chat-transport-rWwta645.js → ws-chat-transport-cqMKq3IY.js} +143 -16
- package/dist/ws-chat-transport-cqMKq3IY.js.map +1 -0
- package/docs/agent-class.md +2 -2
- package/docs/agent-tools.md +29 -4
- package/docs/browse-the-web.md +87 -5
- package/docs/chat-agents.md +124 -16
- package/docs/client-sdk.md +30 -0
- package/docs/context.md +27 -0
- package/docs/durable-execution.md +2 -0
- package/docs/harnesses/pi.md +91 -0
- package/docs/human-in-the-loop.md +4 -0
- package/docs/index.md +4 -1
- package/docs/lifecycle.md +114 -23
- package/docs/models-pi-ai.md +325 -0
- package/docs/models.md +504 -0
- package/docs/queue.md +135 -241
- package/docs/resumable-streaming.md +2 -0
- package/docs/retries.md +1 -1
- package/docs/routing.md +13 -13
- package/docs/sessions.md +36 -1
- package/docs/state.md +1 -0
- package/docs/sub-agents.md +12 -3
- package/package.json +51 -3
- package/dist/agent-tools-y7zLfw4Q.js.map +0 -1
- package/dist/browser/ai.js.map +0 -1
- package/dist/capability-BjSKYpzg.js.map +0 -1
- package/dist/client-tools-aIBO0Fk7.d.ts +0 -53
- package/dist/client.js.map +0 -1
- package/dist/connector-CptFKzRh.js.map +0 -1
- package/dist/lifecycle-CMRGjZdw.js.map +0 -1
- package/dist/message-builder-BymO4N_D.js.map +0 -1
- package/dist/scheduler-DD9NdYbF.js.map +0 -1
- package/dist/src-DlSHshb2.js.map +0 -1
- package/dist/streams-DZKgAj9b.js.map +0 -1
- package/dist/websockets-DUfRHPRq.js +0 -502
- package/dist/websockets-DUfRHPRq.js.map +0 -1
- package/dist/ws-chat-transport-rWwta645.js.map +0 -1
|
@@ -1,26 +1,28 @@
|
|
|
1
1
|
import { camelCaseToKebabCase, isInternalJsStubProp } from "./utils.js";
|
|
2
2
|
import { getAgentByName } from "./agent-routing.js";
|
|
3
|
-
import { AGENT_TOOL_MILESTONE_PART } from "./agent-tool-types.js";
|
|
3
|
+
import { AGENT_TOOL_MILESTONE_PART, isAgentToolLifecycleChunk } 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
6
|
import { copyCallableMetadata, decoratedMethods, getCallableMetadata, isCallableMethod } from "./callable-decorator.js";
|
|
7
7
|
import "./types.js";
|
|
8
|
+
import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, r as _assertClassBrand, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-DZBYAB34.js";
|
|
9
|
+
import { t as _classPrivateMethodInitSpec } from "./classPrivateMethodInitSpec-qMjJ6sHQ.js";
|
|
8
10
|
import { signAgentHeaders } from "./email.js";
|
|
9
11
|
import { sendAgentEmail } from "./email-send.js";
|
|
10
|
-
import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, r as _assertClassBrand, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-DZBYAB34.js";
|
|
11
12
|
import { n as withInvocationScope, r as writeSpanAttributes, t as tracer } from "./cloudflare-Dzvc7V2N.js";
|
|
12
13
|
import { parseSubAgentPath } from "./sub-routing.js";
|
|
13
|
-
import { t as
|
|
14
|
-
import { t as
|
|
15
|
-
import { a as abortWithoutAlarmRetry, i as setLifecycleRouteTransport, n as setLifecycleEventSink, r as setLifecycleHostInvoker, t as Lifecycle } from "./lifecycle-
|
|
14
|
+
import { a as getConnectionFlag, c as isConnectionReadonly, d as setConnectionProtocolEnabled, f as setConnectionReadonly, i as ensureConnectionWrapped, l as registerInternalConnectionKeys, o as getConnectionRawState, s as isConnectionProtocolEnabled, t as WebSockets, u as setConnectionFlag } from "./websockets-Ci-LZFHH.js";
|
|
15
|
+
import { t as LifecycleCapability } from "./capability-B4WbF81e.js";
|
|
16
|
+
import { a as abortWithoutAlarmRetry, i as setLifecycleRouteTransport, n as setLifecycleEventSink, r as setLifecycleHostInvoker, t as Lifecycle } from "./lifecycle-CFu6OV03.js";
|
|
16
17
|
import { isErrorRetryable, tryN, validateRetryOptions } from "./retries.js";
|
|
17
18
|
import { SqlError } from "./sql-error.js";
|
|
18
|
-
import { o as
|
|
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";
|
|
19
|
+
import { a as ensureMcpServerTable, d as DisposableStore, i as normalizeServerId, l as RPC_DO_PREFIX, o as MCPConnectionState, t as MCPClientManager } from "./client-fyQrLGkD.js";
|
|
20
20
|
import { DurableObjectOAuthClientProvider } from "./mcp/client/do-oauth-client-provider.js";
|
|
21
21
|
import { genericObservability } from "./observability/index.js";
|
|
22
|
-
import { n as setSchedulerCallbackResolver, t as Scheduler } from "./scheduler-
|
|
23
|
-
import { n as
|
|
22
|
+
import { n as setSchedulerCallbackResolver, t as Scheduler } from "./scheduler-DZ3xQfeK.js";
|
|
23
|
+
import { n as setQueueCallbackResolver, t as Queue } from "./queue-BB-zfIYF.js";
|
|
24
|
+
import { n as setTaskDefinitionResolver, r as setTaskRoutedMemoryLimitHandler, t as Tasks } from "./tasks-68DESxf0.js";
|
|
25
|
+
import { State } from "./state/index.js";
|
|
24
26
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
25
27
|
import { DurableObject, RpcTarget, exports } from "cloudflare:workers";
|
|
26
28
|
import { nanoid } from "nanoid";
|
|
@@ -294,12 +296,47 @@ function _addColumnIfNotExists(sql) {
|
|
|
294
296
|
*/
|
|
295
297
|
const CF_SUB_AGENT_OUTER_URL_KEY = "_cf_subAgentOuterUrl";
|
|
296
298
|
const CF_SUB_AGENT_TAGS_KEY = "_cf_subAgentTags";
|
|
299
|
+
/**
|
|
300
|
+
* Set on a socket closed because its sub-agent was deleted. Its late
|
|
301
|
+
* message/close events are dropped, so they can't reach a same-name
|
|
302
|
+
* replacement created after the delete. Storage-frozen — never rename.
|
|
303
|
+
*/
|
|
304
|
+
const CF_SUB_AGENT_DELETED_KEY = "_cf_subAgentDeleted";
|
|
297
305
|
/** Wire-frozen internal header carrying the outer URL on WS upgrades. */
|
|
298
306
|
const SUB_AGENT_OUTER_URL_HEADER = "x-cf-agents-subagent-url";
|
|
307
|
+
/**
|
|
308
|
+
* The close frame a rejected sub-agent WebSocket receives in place of the
|
|
309
|
+
* `onBeforeSubAgent` response, which a browser cannot read from a failed
|
|
310
|
+
* handshake. A 4xx closes with `4000 + status`, which clients treat as
|
|
311
|
+
* final; anything else closes with 1011 so the client retries.
|
|
312
|
+
*/
|
|
313
|
+
function subAgentRejectionClose(response) {
|
|
314
|
+
const { status } = response;
|
|
315
|
+
return {
|
|
316
|
+
code: status >= 400 && status < 500 ? 4e3 + status : 1011,
|
|
317
|
+
reason: `Sub-agent connection rejected (${status})`
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Upgrade a rejected sub-agent WebSocket only to close it, so the client
|
|
322
|
+
* sees a close frame instead of a failed handshake that reconnects forever.
|
|
323
|
+
*/
|
|
324
|
+
function rejectSubAgentWebSocket(response) {
|
|
325
|
+
const { code, reason } = subAgentRejectionClose(response);
|
|
326
|
+
const [client, server] = Object.values(new WebSocketPair());
|
|
327
|
+
server.accept();
|
|
328
|
+
server.close(code, reason);
|
|
329
|
+
return new Response(null, {
|
|
330
|
+
status: 101,
|
|
331
|
+
webSocket: client
|
|
332
|
+
});
|
|
333
|
+
}
|
|
299
334
|
var _host = /* @__PURE__ */ new WeakMap();
|
|
300
335
|
var _facetKeepAliveTokens = /* @__PURE__ */ new WeakMap();
|
|
301
336
|
var _bridgeContext = /* @__PURE__ */ new WeakMap();
|
|
302
337
|
var _virtualConnections = /* @__PURE__ */ new WeakMap();
|
|
338
|
+
var _virtualConnectionsHydrated = /* @__PURE__ */ new WeakMap();
|
|
339
|
+
var _virtualConnectionsHydration = /* @__PURE__ */ new WeakMap();
|
|
303
340
|
var _connectionOperationTails = /* @__PURE__ */ new WeakMap();
|
|
304
341
|
var _broadcastOperationTail = /* @__PURE__ */ new WeakMap();
|
|
305
342
|
var _DynamicAgentsInternal_brand = /* @__PURE__ */ new WeakSet();
|
|
@@ -333,6 +370,8 @@ var DynamicAgentsInternal = class extends LifecycleCapability {
|
|
|
333
370
|
_classPrivateFieldInitSpec(this, _facetKeepAliveTokens, /* @__PURE__ */ new Set());
|
|
334
371
|
_classPrivateFieldInitSpec(this, _bridgeContext, new AsyncLocalStorage());
|
|
335
372
|
_classPrivateFieldInitSpec(this, _virtualConnections, /* @__PURE__ */ new Map());
|
|
373
|
+
_classPrivateFieldInitSpec(this, _virtualConnectionsHydrated, false);
|
|
374
|
+
_classPrivateFieldInitSpec(this, _virtualConnectionsHydration, void 0);
|
|
336
375
|
_classPrivateFieldInitSpec(this, _connectionOperationTails, /* @__PURE__ */ new Map());
|
|
337
376
|
_classPrivateFieldInitSpec(this, _broadcastOperationTail, void 0);
|
|
338
377
|
_classPrivateFieldSet2(_host, this, host);
|
|
@@ -387,7 +426,7 @@ var DynamicAgentsInternal = class extends LifecycleCapability {
|
|
|
387
426
|
if (!_classPrivateFieldGet2(_host, this).hasSubAgent(next.className, next.name)) {
|
|
388
427
|
const stalePath = targetPath.slice(0, selfPath.length + 1);
|
|
389
428
|
if (_classPrivateFieldGet2(_host, this)._isFacet) await (await this.rootAlarmOwner())._cf_cleanupFacetPrefix(stalePath);
|
|
390
|
-
else await
|
|
429
|
+
else await this.cleanupPrefix(stalePath);
|
|
391
430
|
return false;
|
|
392
431
|
}
|
|
393
432
|
return (await _classPrivateFieldGet2(_host, this)._cf_resolveSubAgent(next.className, next.name))._cf_routeLifecycle(target, envelope);
|
|
@@ -401,7 +440,7 @@ var DynamicAgentsInternal = class extends LifecycleCapability {
|
|
|
401
440
|
if (!root) throw new Error("Facet routing requires a root parent.");
|
|
402
441
|
const binding = _classPrivateFieldGet2(_host, this).ctx.exports?.[root.className];
|
|
403
442
|
if (!binding) throw new Error(`Unable to resolve root "${root.className}" for facet routing.`);
|
|
404
|
-
return
|
|
443
|
+
return binding.get(binding.idFromName(root.name));
|
|
405
444
|
}
|
|
406
445
|
rootResolvesToSelf() {
|
|
407
446
|
const root = _classPrivateFieldGet2(_host, this)._parentPath[0];
|
|
@@ -412,14 +451,16 @@ var DynamicAgentsInternal = class extends LifecycleCapability {
|
|
|
412
451
|
}
|
|
413
452
|
/**
|
|
414
453
|
* Clean root-owned bookkeeping for a sub-tree of facets: bulk-cancel
|
|
415
|
-
* schedules and routed Task wake mirrors under the
|
|
416
|
-
* and delete root-side facet fiber recovery leases for
|
|
454
|
+
* schedules, queue items, and routed Task wake mirrors under the
|
|
455
|
+
* owner-path prefix, and delete root-side facet fiber recovery leases for
|
|
456
|
+
* the same sub-tree.
|
|
417
457
|
*/
|
|
418
458
|
async cleanupPrefix(ownerPath) {
|
|
419
459
|
const prefix = agentPathKey(ownerPath);
|
|
420
460
|
if (prefix) {
|
|
421
461
|
await _classPrivateFieldGet2(_host, this).scheduler.__DO_NOT_USE_WILL_BREAK__cleanupRoutePrefix(prefix);
|
|
422
462
|
await _classPrivateFieldGet2(_host, this).tasks.__DO_NOT_USE_WILL_BREAK__cleanupRoutePrefix(prefix);
|
|
463
|
+
await _classPrivateFieldGet2(_host, this)._queue.__DO_NOT_USE_WILL_BREAK__cleanupRoutePrefix(prefix);
|
|
423
464
|
}
|
|
424
465
|
this.deleteRunRowsForPrefix(ownerPath);
|
|
425
466
|
await _classPrivateFieldGet2(_host, this)._syncHostJobs();
|
|
@@ -501,6 +542,7 @@ var DynamicAgentsInternal = class extends LifecycleCapability {
|
|
|
501
542
|
* rows on the target facet after recovery.
|
|
502
543
|
*/
|
|
503
544
|
async checkRunFibersAtPath(ownerPath) {
|
|
545
|
+
await _classPrivateFieldGet2(_host, this).__unsafe_ensureInitialized();
|
|
504
546
|
const selfPath = _classPrivateFieldGet2(_host, this).selfPath;
|
|
505
547
|
if (!_classPrivateFieldGet2(_host, this)._isSameAgentPathPrefix(selfPath, ownerPath)) throw new Error(`Facet fiber owner path does not descend from ${JSON.stringify(selfPath)}.`);
|
|
506
548
|
if (selfPath.length === ownerPath.length) {
|
|
@@ -544,11 +586,12 @@ var DynamicAgentsInternal = class extends LifecycleCapability {
|
|
|
544
586
|
if (targetPath.length === 0) throw new Error("_cf_destroyDescendantFacet: target path must not be empty.");
|
|
545
587
|
if (selfPath.length >= targetPath.length) throw new Error("_cf_destroyDescendantFacet: target must be a strict descendant.");
|
|
546
588
|
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
|
|
589
|
+
if (_classPrivateFieldGet2(_host, this)._parentPath.length === 0) await this.cleanupPrefix(targetPath);
|
|
548
590
|
if (selfPath.length === targetPath.length - 1) {
|
|
549
591
|
const target = targetPath[targetPath.length - 1];
|
|
550
592
|
const ctx = _classPrivateFieldGet2(_host, this).ctx;
|
|
551
593
|
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.");
|
|
594
|
+
await _assertClassBrand(_DynamicAgentsInternal_brand, this, _closeConnectionsForDeletedPath).call(this, targetPath);
|
|
552
595
|
try {
|
|
553
596
|
ctx.facets.delete(`${target.className}\0${target.name}`);
|
|
554
597
|
} catch {}
|
|
@@ -624,16 +667,31 @@ var DynamicAgentsInternal = class extends LifecycleCapability {
|
|
|
624
667
|
className,
|
|
625
668
|
name
|
|
626
669
|
}];
|
|
627
|
-
|
|
628
|
-
|
|
670
|
+
const root = _classPrivateFieldGet2(_host, this)._isFacet ? await this.rootAlarmOwner() : void 0;
|
|
671
|
+
await _assertClassBrand(_DynamicAgentsInternal_brand, this, _closeConnectionsForDeletedPath).call(this, childPath, root);
|
|
629
672
|
try {
|
|
630
673
|
ctx.facets.delete(facetKey);
|
|
631
674
|
} catch {}
|
|
632
675
|
this.registry.forget(className, name);
|
|
676
|
+
if (root) await root._cf_cleanupFacetPrefix(childPath);
|
|
677
|
+
else await this.cleanupPrefix(childPath);
|
|
678
|
+
}
|
|
679
|
+
/** Root-side: close sockets whose `/sub/...` target is under `prefix`. */
|
|
680
|
+
closeConnectionsForPrefix(prefix, code, reason) {
|
|
681
|
+
for (const connection of _classPrivateFieldGet2(_host, this)._webSockets.getConnections()) {
|
|
682
|
+
const targetPath = this.connectionTargetPath(connection);
|
|
683
|
+
if (!targetPath) continue;
|
|
684
|
+
if (!_classPrivateFieldGet2(_host, this)._isSameAgentPathPrefix(prefix, targetPath)) continue;
|
|
685
|
+
_classPrivateFieldGet2(_host, this)._unsafe_setConnectionFlag(connection, CF_SUB_AGENT_DELETED_KEY, true);
|
|
686
|
+
try {
|
|
687
|
+
connection.close(code, reason);
|
|
688
|
+
} catch {}
|
|
689
|
+
}
|
|
633
690
|
}
|
|
634
691
|
/** Drop all facet-side virtual connections (test/rehydration hook). */
|
|
635
692
|
clearVirtualConnections() {
|
|
636
693
|
_classPrivateFieldGet2(_virtualConnections, this).clear();
|
|
694
|
+
_classPrivateFieldSet2(_virtualConnectionsHydrated, this, false);
|
|
637
695
|
}
|
|
638
696
|
/** Facet-side lookup of a virtual connection by id. */
|
|
639
697
|
getVirtualConnection(id) {
|
|
@@ -711,6 +769,7 @@ var DynamicAgentsInternal = class extends LifecycleCapability {
|
|
|
711
769
|
await completion;
|
|
712
770
|
}
|
|
713
771
|
async broadcastToParent(message, without) {
|
|
772
|
+
if (_assertClassBrand(_DynamicAgentsInternal_brand, this, _broadcastHasNoRecipients).call(this, without)) return;
|
|
714
773
|
await this.routeBroadcast(_classPrivateFieldGet2(_host, this).selfPath, message, without);
|
|
715
774
|
}
|
|
716
775
|
async broadcastToPath(ownerPath, message, without) {
|
|
@@ -747,12 +806,12 @@ var DynamicAgentsInternal = class extends LifecycleCapability {
|
|
|
747
806
|
async setConnectionState(connectionId, state) {
|
|
748
807
|
const connection = _classPrivateFieldGet2(_host, this)._webSockets.getConnection(connectionId);
|
|
749
808
|
if (!connection || !this.connectionHasChildTarget(connection)) return null;
|
|
750
|
-
|
|
809
|
+
ensureConnectionWrapped(connection);
|
|
751
810
|
connection.setState(state);
|
|
752
811
|
return this.getForwardedState(connection);
|
|
753
812
|
}
|
|
754
813
|
connectionTargetPath(connection) {
|
|
755
|
-
|
|
814
|
+
ensureConnectionWrapped(connection);
|
|
756
815
|
const outerUri = _classPrivateFieldGet2(_host, this)._unsafe_getConnectionFlag(connection, CF_SUB_AGENT_OUTER_URL_KEY);
|
|
757
816
|
if (typeof outerUri !== "string") return null;
|
|
758
817
|
return _assertClassBrand(_DynamicAgentsInternal_brand, this, _pathFromOuterUri).call(this, outerUri)?.path ?? null;
|
|
@@ -762,7 +821,7 @@ var DynamicAgentsInternal = class extends LifecycleCapability {
|
|
|
762
821
|
return a.every((step, index) => step.className === b[index]?.className && step.name === b[index]?.name);
|
|
763
822
|
}
|
|
764
823
|
connectionHasChildTarget(connection) {
|
|
765
|
-
|
|
824
|
+
ensureConnectionWrapped(connection);
|
|
766
825
|
return typeof _classPrivateFieldGet2(_host, this)._unsafe_getConnectionFlag(connection, CF_SUB_AGENT_OUTER_URL_KEY) === "string";
|
|
767
826
|
}
|
|
768
827
|
connectionTargetsChild(connection) {
|
|
@@ -775,21 +834,27 @@ var DynamicAgentsInternal = class extends LifecycleCapability {
|
|
|
775
834
|
return parseSubAgentPath(request.url, { knownClasses: ctx.exports ? Object.keys(ctx.exports) : void 0 }) !== null;
|
|
776
835
|
}
|
|
777
836
|
async forwardWebSocketConnect(connection, request, options) {
|
|
778
|
-
const routed = await _assertClassBrand(_DynamicAgentsInternal_brand, this, _resolveConnection).call(this, connection,
|
|
779
|
-
|
|
837
|
+
const routed = await _assertClassBrand(_DynamicAgentsInternal_brand, this, _resolveConnection).call(this, connection, {
|
|
838
|
+
create: true,
|
|
839
|
+
request,
|
|
840
|
+
gate: options.gate
|
|
841
|
+
});
|
|
842
|
+
if (routed.status !== "ok") return false;
|
|
780
843
|
await routed.child._cf_handleSubAgentWebSocketConnect(_assertClassBrand(_DynamicAgentsInternal_brand, this, _createConnectionBridge).call(this, connection), routed.meta);
|
|
781
844
|
return true;
|
|
782
845
|
}
|
|
783
846
|
async forwardWebSocketMessage(connection, message, replyBridge) {
|
|
784
|
-
const routed = await _assertClassBrand(_DynamicAgentsInternal_brand, this, _resolveConnection).call(this, connection);
|
|
785
|
-
if (
|
|
847
|
+
const routed = await _assertClassBrand(_DynamicAgentsInternal_brand, this, _resolveConnection).call(this, connection, { create: false });
|
|
848
|
+
if (routed.status === "no-match") return false;
|
|
849
|
+
if (routed.status === "dropped") return true;
|
|
786
850
|
const bridge = _assertClassBrand(_DynamicAgentsInternal_brand, this, _createConnectionBridge).call(this, connection);
|
|
787
851
|
await routed.child._cf_handleSubAgentWebSocketMessage(message, bridge, routed.meta, replyBridge ?? bridge);
|
|
788
852
|
return true;
|
|
789
853
|
}
|
|
790
854
|
async forwardWebSocketClose(connection, code, reason, wasClean) {
|
|
791
|
-
const routed = await _assertClassBrand(_DynamicAgentsInternal_brand, this, _resolveConnection).call(this, connection);
|
|
792
|
-
if (
|
|
855
|
+
const routed = await _assertClassBrand(_DynamicAgentsInternal_brand, this, _resolveConnection).call(this, connection, { create: false });
|
|
856
|
+
if (routed.status === "no-match") return false;
|
|
857
|
+
if (routed.status === "dropped") return true;
|
|
793
858
|
await routed.child._cf_handleSubAgentWebSocketClose(code, reason, wasClean, _assertClassBrand(_DynamicAgentsInternal_brand, this, _createConnectionBridge).call(this, connection), routed.meta);
|
|
794
859
|
return true;
|
|
795
860
|
}
|
|
@@ -799,7 +864,7 @@ var DynamicAgentsInternal = class extends LifecycleCapability {
|
|
|
799
864
|
const request = new Request(meta.uri ?? "http://placeholder/", { headers: meta.requestHeaders });
|
|
800
865
|
if (await this.forwardWebSocketConnect(connection, request, { gate: true })) return;
|
|
801
866
|
if (_classPrivateFieldGet2(_host, this).shouldConnectionBeReadonly(connection, { request })) _classPrivateFieldGet2(_host, this).setConnectionReadonly(connection, true);
|
|
802
|
-
if (!_classPrivateFieldGet2(_host, this).shouldSendProtocolMessages(connection, { request }))
|
|
867
|
+
if (!_classPrivateFieldGet2(_host, this).shouldSendProtocolMessages(connection, { request })) setConnectionProtocolEnabled(connection, false);
|
|
803
868
|
const childTags = await _classPrivateFieldGet2(_host, this).getConnectionTags(connection, { request });
|
|
804
869
|
connection.tags = [connection.id, ...childTags.filter((tag) => tag !== connection.id)];
|
|
805
870
|
this.storeVirtualConnection(connection);
|
|
@@ -881,7 +946,7 @@ var DynamicAgentsInternal = class extends LifecycleCapability {
|
|
|
881
946
|
removeEventListener() {}
|
|
882
947
|
};
|
|
883
948
|
stored.connection = connection;
|
|
884
|
-
|
|
949
|
+
ensureConnectionWrapped(connection);
|
|
885
950
|
return connection;
|
|
886
951
|
}
|
|
887
952
|
storeVirtualConnection(connection) {
|
|
@@ -918,11 +983,11 @@ var DynamicAgentsInternal = class extends LifecycleCapability {
|
|
|
918
983
|
if (!_classPrivateFieldGet2(_host, this)._isFacet || _classPrivateFieldGet2(_host, this)._parentPath.length === 0) return;
|
|
919
984
|
if (this.rootResolvesToSelf()) return;
|
|
920
985
|
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 });
|
|
986
|
+
for (const meta of metas) if (!_classPrivateFieldGet2(_virtualConnections, this).has(meta.id)) _classPrivateFieldGet2(_virtualConnections, this).set(meta.id, { meta });
|
|
987
|
+
_classPrivateFieldSet2(_virtualConnectionsHydrated, this, true);
|
|
922
988
|
}
|
|
923
989
|
getRawConnectionState(connection) {
|
|
924
|
-
|
|
925
|
-
return _classPrivateFieldGet2(_host, this)._rawStateAccessors.get(connection)?.getRaw() ?? null;
|
|
990
|
+
return getConnectionRawState(connection);
|
|
926
991
|
}
|
|
927
992
|
getForwardedState(connection) {
|
|
928
993
|
const raw = this.getRawConnectionState(connection);
|
|
@@ -961,6 +1026,7 @@ var DynamicAgentsInternal = class extends LifecycleCapability {
|
|
|
961
1026
|
* one RPC method. Stateless — no cached references.
|
|
962
1027
|
*/
|
|
963
1028
|
async invoke(className, name, method, args) {
|
|
1029
|
+
await _classPrivateFieldGet2(_host, this).__unsafe_ensureInitialized();
|
|
964
1030
|
const stub = await this.resolve(className, name);
|
|
965
1031
|
return await this.invokeStubMethod(stub, className, method, args);
|
|
966
1032
|
}
|
|
@@ -971,6 +1037,7 @@ var DynamicAgentsInternal = class extends LifecycleCapability {
|
|
|
971
1037
|
* to the next facet using that facet's own `ctx.facets`.
|
|
972
1038
|
*/
|
|
973
1039
|
async invokePath(path, method, args) {
|
|
1040
|
+
await _classPrivateFieldGet2(_host, this).__unsafe_ensureInitialized();
|
|
974
1041
|
const [self, next, ...rest] = path;
|
|
975
1042
|
if (!self) throw new Error(`Sub-agent path invocation requires a non-empty path.`);
|
|
976
1043
|
const ownClassName = _classPrivateFieldGet2(_host, this).constructor.name;
|
|
@@ -1014,6 +1081,46 @@ var DynamicAgentsInternal = class extends LifecycleCapability {
|
|
|
1014
1081
|
await _classPrivateFieldGet2(_host, this)._syncHostJobs();
|
|
1015
1082
|
}
|
|
1016
1083
|
};
|
|
1084
|
+
/**
|
|
1085
|
+
* Non-creating counterpart to {@link resolve} for WebSocket message/close
|
|
1086
|
+
* forwarding. Returns `null` when the child has no registry row, and never
|
|
1087
|
+
* writes one. The connection's initial connect already went through
|
|
1088
|
+
* {@link resolve} on this hop, so the runtime/export checks there hold.
|
|
1089
|
+
*/
|
|
1090
|
+
async function _resolveExisting(className, name) {
|
|
1091
|
+
const row = this.registry.row(className, name);
|
|
1092
|
+
if (!row) return null;
|
|
1093
|
+
const ctx = _classPrivateFieldGet2(_host, this).ctx;
|
|
1094
|
+
const identityName = row.identity_version === "path-v2" && typeof row.identity_name === "string" ? row.identity_name : name;
|
|
1095
|
+
const rootClassName = _classPrivateFieldGet2(_host, this)._parentPath[0]?.className ?? _classPrivateFieldGet2(_host, this).constructor.name;
|
|
1096
|
+
const rootNs = ctx.exports[rootClassName];
|
|
1097
|
+
const stub = ctx.facets.get(`${className}\0${name}`, () => ({
|
|
1098
|
+
class: ctx.exports[className],
|
|
1099
|
+
id: rootNs.idFromName(identityName)
|
|
1100
|
+
}));
|
|
1101
|
+
const childParentPath = _classPrivateFieldGet2(_host, this).selfPath;
|
|
1102
|
+
try {
|
|
1103
|
+
await _classPrivateFieldGet2(_host, this)._runFacetInitInvocation(async () => {
|
|
1104
|
+
await stub._cf_initAsFacet(name, childParentPath, identityName);
|
|
1105
|
+
});
|
|
1106
|
+
} catch (error) {
|
|
1107
|
+
if (!this.registry.row(className, name)) return null;
|
|
1108
|
+
throw error;
|
|
1109
|
+
}
|
|
1110
|
+
return stub;
|
|
1111
|
+
}
|
|
1112
|
+
/**
|
|
1113
|
+
* Close every client socket targeting `path` or a descendant, so a client
|
|
1114
|
+
* attached to a deleted facet gets an explicit close instead of a socket
|
|
1115
|
+
* that goes nowhere (#2003). Only the root owns real sockets.
|
|
1116
|
+
*/
|
|
1117
|
+
async function _closeConnectionsForDeletedPath(path, root) {
|
|
1118
|
+
if (!_classPrivateFieldGet2(_host, this)._isFacet) {
|
|
1119
|
+
this.closeConnectionsForPrefix(path, 1001, "Sub-agent deleted");
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
await (root ?? await this.rootAlarmOwner())._cf_closeSubAgentConnectionsForPrefix(path, 1001, "Sub-agent deleted");
|
|
1123
|
+
}
|
|
1017
1124
|
function _reportConnectionOperationFailure(connectionId, operation, error) {
|
|
1018
1125
|
console.error("[Agent] Sub-agent connection operation failed:", {
|
|
1019
1126
|
connectionId,
|
|
@@ -1021,8 +1128,31 @@ function _reportConnectionOperationFailure(connectionId, operation, error) {
|
|
|
1021
1128
|
error
|
|
1022
1129
|
});
|
|
1023
1130
|
}
|
|
1131
|
+
/**
|
|
1132
|
+
* Whether a hydrated mirror proves no root socket targets this facet
|
|
1133
|
+
* outside `without`. Stale mirror entries only cost a root call; a socket
|
|
1134
|
+
* missing from a hydrated mirror has not had its connect forwarded here
|
|
1135
|
+
* yet, so it has not been through `onConnect`. Synchronous so a skipped
|
|
1136
|
+
* broadcast never reorders later operations.
|
|
1137
|
+
*/
|
|
1138
|
+
function _broadcastHasNoRecipients(without) {
|
|
1139
|
+
if (!_classPrivateFieldGet2(_virtualConnectionsHydrated, this)) {
|
|
1140
|
+
_assertClassBrand(_DynamicAgentsInternal_brand, this, _hydrateVirtualConnectionsInBackground).call(this);
|
|
1141
|
+
return false;
|
|
1142
|
+
}
|
|
1143
|
+
for (const id of _classPrivateFieldGet2(_virtualConnections, this).keys()) if (!without?.includes(id)) return false;
|
|
1144
|
+
return true;
|
|
1145
|
+
}
|
|
1146
|
+
function _hydrateVirtualConnectionsInBackground() {
|
|
1147
|
+
if (_classPrivateFieldGet2(_virtualConnectionsHydration, this)) return;
|
|
1148
|
+
const hydration = this.hydrateConnectionsFromRoot().catch(() => {}).finally(() => {
|
|
1149
|
+
_classPrivateFieldSet2(_virtualConnectionsHydration, this, void 0);
|
|
1150
|
+
});
|
|
1151
|
+
_classPrivateFieldSet2(_virtualConnectionsHydration, this, hydration);
|
|
1152
|
+
_classPrivateFieldGet2(_host, this).ctx.waitUntil(hydration);
|
|
1153
|
+
}
|
|
1024
1154
|
function _connectionMetaForPath(connection, ownerPath) {
|
|
1025
|
-
|
|
1155
|
+
ensureConnectionWrapped(connection);
|
|
1026
1156
|
const outerUri = _classPrivateFieldGet2(_host, this)._unsafe_getConnectionFlag(connection, CF_SUB_AGENT_OUTER_URL_KEY);
|
|
1027
1157
|
if (typeof outerUri !== "string") return null;
|
|
1028
1158
|
const target = _assertClassBrand(_DynamicAgentsInternal_brand, this, _pathFromOuterUri).call(this, outerUri, ownerPath);
|
|
@@ -1071,20 +1201,31 @@ function _createConnectionBridge(connection) {
|
|
|
1071
1201
|
return _classPrivateFieldGet2(_host, this)._cf_broadcastToSubAgent(ownerPath, message, without);
|
|
1072
1202
|
});
|
|
1073
1203
|
}
|
|
1074
|
-
|
|
1075
|
-
|
|
1204
|
+
/**
|
|
1205
|
+
* Resolve a connection's `/sub/{class}/{name}` target.
|
|
1206
|
+
*
|
|
1207
|
+
* `create: true` (the initial connect) may wake or create the child, like
|
|
1208
|
+
* `subAgent()`. `create: false` (message/close on an already-forwarded
|
|
1209
|
+
* connection) only attaches to a child that still has a registry row; a
|
|
1210
|
+
* deleted child resolves to `"dropped"`, which the caller must treat as
|
|
1211
|
+
* consumed so a stale frame can't recreate it (#2003).
|
|
1212
|
+
*/
|
|
1213
|
+
async function _resolveConnection(connection, options) {
|
|
1214
|
+
ensureConnectionWrapped(connection);
|
|
1076
1215
|
const outerUri = _classPrivateFieldGet2(_host, this)._unsafe_getConnectionFlag(connection, CF_SUB_AGENT_OUTER_URL_KEY);
|
|
1077
1216
|
const uri = typeof outerUri === "string" ? outerUri : connection.uri;
|
|
1078
|
-
if (!uri) return
|
|
1217
|
+
if (!uri) return { status: "no-match" };
|
|
1218
|
+
if (_classPrivateFieldGet2(_host, this)._unsafe_getConnectionFlag(connection, "_cf_subAgentDeleted")) return { status: "dropped" };
|
|
1079
1219
|
const ctx = _classPrivateFieldGet2(_host, this).ctx;
|
|
1080
1220
|
let match = parseSubAgentPath(uri, { knownClasses: ctx.exports ? Object.keys(ctx.exports) : void 0 });
|
|
1081
|
-
if (!match) return
|
|
1221
|
+
if (!match) return { status: "no-match" };
|
|
1082
1222
|
if (_classPrivateFieldGet2(_host, this)._ParentClass.name === match.childClass && _classPrivateFieldGet2(_host, this).name === match.childName) {
|
|
1083
1223
|
const tailUri = new URL(uri);
|
|
1084
1224
|
tailUri.pathname = match.remainingPath;
|
|
1085
1225
|
match = parseSubAgentPath(tailUri.toString(), { knownClasses: ctx.exports ? Object.keys(ctx.exports) : void 0 });
|
|
1086
|
-
if (!match) return
|
|
1226
|
+
if (!match) return { status: "no-match" };
|
|
1087
1227
|
}
|
|
1228
|
+
const { request } = options;
|
|
1088
1229
|
let forwardReq = request;
|
|
1089
1230
|
if (request && options.gate) {
|
|
1090
1231
|
const decision = await _classPrivateFieldGet2(_host, this).onBeforeSubAgent(request, {
|
|
@@ -1092,25 +1233,28 @@ async function _resolveConnection(connection, request, options = { gate: false }
|
|
|
1092
1233
|
name: match.childName
|
|
1093
1234
|
});
|
|
1094
1235
|
if (decision instanceof Response) {
|
|
1095
|
-
|
|
1096
|
-
|
|
1236
|
+
const { code, reason } = subAgentRejectionClose(decision);
|
|
1237
|
+
connection.close(code, reason);
|
|
1238
|
+
return { status: "dropped" };
|
|
1097
1239
|
}
|
|
1098
1240
|
forwardReq = decision instanceof Request ? decision : request;
|
|
1099
1241
|
}
|
|
1100
|
-
const child = await this.resolve(match.childClass, match.childName);
|
|
1242
|
+
const child = options.create ? await this.resolve(match.childClass, match.childName) : await _assertClassBrand(_DynamicAgentsInternal_brand, this, _resolveExisting).call(this, match.childClass, match.childName);
|
|
1243
|
+
if (!child) return { status: "dropped" };
|
|
1101
1244
|
const childUri = new URL(forwardReq?.url ?? uri);
|
|
1102
1245
|
childUri.pathname = match.remainingPath;
|
|
1103
1246
|
const raw = this.getRawConnectionState(connection);
|
|
1104
1247
|
const rawTags = raw != null && typeof raw === "object" ? raw[CF_SUB_AGENT_TAGS_KEY] : void 0;
|
|
1105
1248
|
const tags = Array.isArray(rawTags) ? rawTags.filter((tag) => typeof tag === "string") : [...connection.tags];
|
|
1106
1249
|
return {
|
|
1250
|
+
status: "ok",
|
|
1107
1251
|
child,
|
|
1108
1252
|
meta: {
|
|
1109
1253
|
id: connection.id,
|
|
1110
1254
|
uri: childUri.toString(),
|
|
1111
1255
|
tags,
|
|
1112
1256
|
state: this.getForwardedState(connection),
|
|
1113
|
-
requestHeaders: forwardReq ? [...forwardReq.headers] : void 0
|
|
1257
|
+
requestHeaders: forwardReq ? [...forwardReq.headers].filter(([name]) => name !== SUB_AGENT_OUTER_URL_HEADER) : void 0
|
|
1114
1258
|
}
|
|
1115
1259
|
};
|
|
1116
1260
|
}
|
|
@@ -1213,12 +1357,6 @@ function sendRpcResponseIfOpen(connection, response) {
|
|
|
1213
1357
|
function isRPCRequest(msg) {
|
|
1214
1358
|
return typeof msg === "object" && msg !== null && "type" in msg && msg.type === "rpc" && "id" in msg && typeof msg.id === "string" && "method" in msg && typeof msg.method === "string" && "args" in msg && Array.isArray(msg.args);
|
|
1215
1359
|
}
|
|
1216
|
-
/**
|
|
1217
|
-
* Type guard for state update messages
|
|
1218
|
-
*/
|
|
1219
|
-
function isStateUpdateMessage(msg) {
|
|
1220
|
-
return typeof msg === "object" && msg !== null && "type" in msg && msg.type === "cf_agent_state" && "state" in msg;
|
|
1221
|
-
}
|
|
1222
1360
|
const _fiberALS = new AsyncLocalStorage();
|
|
1223
1361
|
const DEFAULT_KEEP_ALIVE_INTERVAL_MS = 3e4;
|
|
1224
1362
|
const DEFAULT_AGENT_TOOL_RECOVERY_TIMEOUT_MS = 2e3;
|
|
@@ -1247,57 +1385,15 @@ const DETACHED_NOTIFY_CALLBACK = "_cfDetachedNotifyFinish";
|
|
|
1247
1385
|
/**
|
|
1248
1386
|
* Schema version for the Agent's internal SQLite tables.
|
|
1249
1387
|
* Bump this when adding new tables, columns, or migrations.
|
|
1250
|
-
* The constructor stores this
|
|
1251
|
-
*
|
|
1388
|
+
* The constructor stores this under a namespaced KV key (the same convention
|
|
1389
|
+
* every capability uses for its own schema version) and checks it on wake to
|
|
1390
|
+
* skip DDL on established DOs.
|
|
1252
1391
|
*/
|
|
1253
|
-
const CURRENT_SCHEMA_VERSION =
|
|
1254
|
-
const
|
|
1255
|
-
const
|
|
1256
|
-
const STATE_WAS_CHANGED = "cf_state_was_changed";
|
|
1392
|
+
const CURRENT_SCHEMA_VERSION = 14;
|
|
1393
|
+
const SCHEMA_VERSION_KEY = "cf_agents:schema_version";
|
|
1394
|
+
const LEGACY_SCHEMA_VERSION_ROW_ID = "cf_schema_version";
|
|
1257
1395
|
const DEFAULT_STATE = {};
|
|
1258
|
-
|
|
1259
|
-
* Internal key used to store the readonly flag in connection state.
|
|
1260
|
-
* Prefixed with _cf_ to avoid collision with user state keys.
|
|
1261
|
-
*/
|
|
1262
|
-
const CF_READONLY_KEY = "_cf_readonly";
|
|
1263
|
-
/**
|
|
1264
|
-
* Internal key used to store the no-protocol flag in connection state.
|
|
1265
|
-
* When set, protocol messages (identity, state sync, MCP servers) are not
|
|
1266
|
-
* sent to this connection — neither on connect nor via broadcasts.
|
|
1267
|
-
*/
|
|
1268
|
-
const CF_NO_PROTOCOL_KEY = "_cf_no_protocol";
|
|
1269
|
-
/**
|
|
1270
|
-
* The set of all internal keys stored in connection state that must be
|
|
1271
|
-
* hidden from user code and preserved across setState calls.
|
|
1272
|
-
*/
|
|
1273
|
-
const CF_INTERNAL_KEYS = /* @__PURE__ */ new Set([
|
|
1274
|
-
CF_READONLY_KEY,
|
|
1275
|
-
CF_NO_PROTOCOL_KEY,
|
|
1276
|
-
"_cf_voiceInCall",
|
|
1277
|
-
CF_SUB_AGENT_OUTER_URL_KEY,
|
|
1278
|
-
CF_SUB_AGENT_TAGS_KEY
|
|
1279
|
-
]);
|
|
1280
|
-
/** Check if a raw connection state object contains any internal keys. */
|
|
1281
|
-
function rawHasInternalKeys(raw) {
|
|
1282
|
-
for (const key of Object.keys(raw)) if (CF_INTERNAL_KEYS.has(key)) return true;
|
|
1283
|
-
return false;
|
|
1284
|
-
}
|
|
1285
|
-
/** Return a copy of `raw` with all internal keys removed, or null if no user keys remain. */
|
|
1286
|
-
function stripInternalKeys(raw) {
|
|
1287
|
-
const result = {};
|
|
1288
|
-
let hasUserKeys = false;
|
|
1289
|
-
for (const key of Object.keys(raw)) if (!CF_INTERNAL_KEYS.has(key)) {
|
|
1290
|
-
result[key] = raw[key];
|
|
1291
|
-
hasUserKeys = true;
|
|
1292
|
-
}
|
|
1293
|
-
return hasUserKeys ? result : null;
|
|
1294
|
-
}
|
|
1295
|
-
/** Return a copy containing only the internal keys present in `raw`. */
|
|
1296
|
-
function extractInternalFlags(raw) {
|
|
1297
|
-
const result = {};
|
|
1298
|
-
for (const key of Object.keys(raw)) if (CF_INTERNAL_KEYS.has(key)) result[key] = raw[key];
|
|
1299
|
-
return result;
|
|
1300
|
-
}
|
|
1396
|
+
registerInternalConnectionKeys("_cf_voiceInCall", CF_SUB_AGENT_OUTER_URL_KEY, CF_SUB_AGENT_TAGS_KEY);
|
|
1301
1397
|
/** Max length for error strings broadcast to clients. */
|
|
1302
1398
|
const MAX_ERROR_STRING_LENGTH = 500;
|
|
1303
1399
|
/**
|
|
@@ -1368,9 +1464,9 @@ const DEFAULT_AGENT_STATIC_OPTIONS = {
|
|
|
1368
1464
|
/**
|
|
1369
1465
|
* Optional hard wall-clock ceiling (ms) on a single agent-tool re-attach
|
|
1370
1466
|
* (#1630). Caps the total wait even as the no-progress budget re-arms across
|
|
1371
|
-
* stream-closes. Defaults to `Infinity` (no implicit cap),
|
|
1372
|
-
*
|
|
1373
|
-
*
|
|
1467
|
+
* stream-closes. Defaults to `Infinity` (no implicit cap, #1672): a healthy,
|
|
1468
|
+
* still-advancing child is followed for as long as it makes progress — a
|
|
1469
|
+
* hung child is bounded by
|
|
1374
1470
|
* the no-progress budget, and a content-runaway by the child's own
|
|
1375
1471
|
* `maxRecoveryWork` / `shouldKeepRecovering`. Set a finite value to impose a
|
|
1376
1472
|
* wall-clock cap (which also tears the child down on `window-exceeded`).
|
|
@@ -1388,36 +1484,23 @@ const DEFAULT_AGENT_STATIC_OPTIONS = {
|
|
|
1388
1484
|
*/
|
|
1389
1485
|
maxAlarmMemoryLimitStrikes: 3
|
|
1390
1486
|
};
|
|
1391
|
-
/**
|
|
1392
|
-
* Parse the raw `retry_options` TEXT column from a SQLite row into a
|
|
1393
|
-
* typed `RetryOptions` object, or `undefined` if not set.
|
|
1394
|
-
*/
|
|
1395
|
-
function parseRetryOptions(row) {
|
|
1396
|
-
const raw = row.retry_options;
|
|
1397
|
-
if (typeof raw !== "string") return void 0;
|
|
1398
|
-
return JSON.parse(raw);
|
|
1399
|
-
}
|
|
1400
|
-
/**
|
|
1401
|
-
* Resolve per-task retry options against class-level defaults and call
|
|
1402
|
-
* `tryN`. This is the retry-execution path for queue flush; Scheduler owns
|
|
1403
|
-
* its own copy for schedule callbacks.
|
|
1404
|
-
*/
|
|
1405
|
-
function resolveRetryConfig(taskRetry, defaults) {
|
|
1406
|
-
return {
|
|
1407
|
-
maxAttempts: taskRetry?.maxAttempts ?? defaults.maxAttempts,
|
|
1408
|
-
baseDelayMs: taskRetry?.baseDelayMs ?? defaults.baseDelayMs,
|
|
1409
|
-
maxDelayMs: taskRetry?.maxDelayMs ?? defaults.maxDelayMs
|
|
1410
|
-
};
|
|
1411
|
-
}
|
|
1412
1487
|
/** Compatibility alias for the lifecycle-owned current Agent accessor. */
|
|
1413
1488
|
const getCurrentAgent = getCurrentAgent$1;
|
|
1489
|
+
const AsyncFunction = Object.getPrototypeOf(async () => {}).constructor;
|
|
1490
|
+
/** Functions produced by {@link withAgentContext}, so they are wrapped once. */
|
|
1491
|
+
const agentContextWrappers = /* @__PURE__ */ new WeakSet();
|
|
1414
1492
|
/**
|
|
1415
1493
|
* Restore Agent context when a public method is entered outside a Lifecycle
|
|
1416
1494
|
* hook, notably through native Durable Object RPC or cross-Agent re-entry.
|
|
1417
1495
|
* Lifecycle already owns context for its capability and semantic user hooks.
|
|
1496
|
+
*
|
|
1497
|
+
* Native RPC bypasses the lifecycle's runtime handlers, so an `async` method
|
|
1498
|
+
* entered from outside this Agent's context first starts the lifecycle when
|
|
1499
|
+
* the call is the one that wakes a cold instance. Synchronous methods are
|
|
1500
|
+
* never deferred, so they keep their synchronous return type.
|
|
1418
1501
|
*/
|
|
1419
1502
|
function withAgentContext(method) {
|
|
1420
|
-
|
|
1503
|
+
const enter = function(...args) {
|
|
1421
1504
|
const { agent } = getCurrentAgent();
|
|
1422
1505
|
if (agent === this) return method.apply(this, args);
|
|
1423
1506
|
return runInInvocation({
|
|
@@ -1429,11 +1512,21 @@ function withAgentContext(method) {
|
|
|
1429
1512
|
return method.apply(this, args);
|
|
1430
1513
|
});
|
|
1431
1514
|
};
|
|
1515
|
+
if (!(method instanceof AsyncFunction)) {
|
|
1516
|
+
agentContextWrappers.add(enter);
|
|
1517
|
+
return enter;
|
|
1518
|
+
}
|
|
1519
|
+
const enterStarted = async function(...args) {
|
|
1520
|
+
if (getCurrentAgent().agent !== this && !this.lifecycle.isStarted()) await this.lifecycle.start();
|
|
1521
|
+
return enter.apply(this, args);
|
|
1522
|
+
};
|
|
1523
|
+
agentContextWrappers.add(enterStarted);
|
|
1524
|
+
return enterStarted;
|
|
1432
1525
|
}
|
|
1433
1526
|
/**
|
|
1434
1527
|
* Base class for creating Agent implementations
|
|
1435
1528
|
* @template Env Environment type containing bindings
|
|
1436
|
-
* @template
|
|
1529
|
+
* @template TState State type to store within the Agent
|
|
1437
1530
|
*/
|
|
1438
1531
|
var Agent = class Agent extends DurableObject {
|
|
1439
1532
|
/** Run user initialization after lifecycle components have started. */
|
|
@@ -1505,31 +1598,16 @@ var Agent = class Agent extends DurableObject {
|
|
|
1505
1598
|
return this.ctx.id.toString();
|
|
1506
1599
|
}
|
|
1507
1600
|
/**
|
|
1508
|
-
* Current state of the Agent
|
|
1601
|
+
* Current state of the Agent.
|
|
1602
|
+
*
|
|
1603
|
+
* Delegates to the State capability, which owns lazy load and the
|
|
1604
|
+
* in-memory cache; Agent seeds `initialState` on first access.
|
|
1509
1605
|
*/
|
|
1510
1606
|
get state() {
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
if (result.length > 0) {
|
|
1516
|
-
const state = result[0].state;
|
|
1517
|
-
try {
|
|
1518
|
-
this._state = JSON.parse(state);
|
|
1519
|
-
} catch (e) {
|
|
1520
|
-
console.error("Failed to parse stored state, falling back to initialState:", e);
|
|
1521
|
-
if (this.initialState !== DEFAULT_STATE) {
|
|
1522
|
-
this._state = this.initialState;
|
|
1523
|
-
this._setStateInternal(this.initialState);
|
|
1524
|
-
} else {
|
|
1525
|
-
this.sql`DELETE FROM cf_agents_state WHERE id = ${STATE_ROW_ID}`;
|
|
1526
|
-
return;
|
|
1527
|
-
}
|
|
1528
|
-
}
|
|
1529
|
-
return this._state;
|
|
1530
|
-
}
|
|
1531
|
-
if (this.initialState === DEFAULT_STATE) return;
|
|
1532
|
-
this._setStateInternal(this.initialState);
|
|
1607
|
+
const stored = this._state.get();
|
|
1608
|
+
if (stored !== void 0) return stored;
|
|
1609
|
+
if (this.initialState === DEFAULT_STATE) return void 0;
|
|
1610
|
+
this._state.set(this.initialState, "server");
|
|
1533
1611
|
return this.initialState;
|
|
1534
1612
|
}
|
|
1535
1613
|
get _resolvedOptions() {
|
|
@@ -1615,26 +1693,9 @@ var Agent = class Agent extends DurableObject {
|
|
|
1615
1693
|
* local dev and the constructor only runs once per DO instance).
|
|
1616
1694
|
*/
|
|
1617
1695
|
_ensureSchema() {
|
|
1618
|
-
this.
|
|
1619
|
-
CREATE TABLE IF NOT EXISTS cf_agents_state (
|
|
1620
|
-
id TEXT PRIMARY KEY NOT NULL,
|
|
1621
|
-
state TEXT
|
|
1622
|
-
)
|
|
1623
|
-
`;
|
|
1624
|
-
const versionRow = this.sql`
|
|
1625
|
-
SELECT state FROM cf_agents_state WHERE id = ${SCHEMA_VERSION_ROW_ID}
|
|
1626
|
-
`;
|
|
1627
|
-
const schemaVersion = versionRow.length > 0 ? Number(versionRow[0].state) : 0;
|
|
1696
|
+
const schemaVersion = this._readSchemaVersion();
|
|
1628
1697
|
if (schemaVersion < CURRENT_SCHEMA_VERSION) {
|
|
1629
1698
|
ensureMcpServerTable(this.ctx.storage);
|
|
1630
|
-
this.sql`
|
|
1631
|
-
CREATE TABLE IF NOT EXISTS cf_agents_queues (
|
|
1632
|
-
id TEXT PRIMARY KEY NOT NULL,
|
|
1633
|
-
payload TEXT,
|
|
1634
|
-
callback TEXT,
|
|
1635
|
-
created_at INTEGER DEFAULT (unixepoch())
|
|
1636
|
-
)
|
|
1637
|
-
`;
|
|
1638
1699
|
const addColumnIfNotExists = (sql) => {
|
|
1639
1700
|
try {
|
|
1640
1701
|
this.ctx.storage.sql.exec(sql);
|
|
@@ -1642,7 +1703,6 @@ var Agent = class Agent extends DurableObject {
|
|
|
1642
1703
|
if (!(error instanceof Error ? error.message : String(error)).toLowerCase().includes("duplicate column")) throw error;
|
|
1643
1704
|
}
|
|
1644
1705
|
};
|
|
1645
|
-
addColumnIfNotExists("ALTER TABLE cf_agents_queues ADD COLUMN retry_options TEXT");
|
|
1646
1706
|
this.sql`
|
|
1647
1707
|
CREATE TABLE IF NOT EXISTS cf_agents_workflows (
|
|
1648
1708
|
id TEXT PRIMARY KEY NOT NULL,
|
|
@@ -1667,13 +1727,15 @@ var Agent = class Agent extends DurableObject {
|
|
|
1667
1727
|
this.sql`
|
|
1668
1728
|
CREATE INDEX IF NOT EXISTS idx_workflows_name ON cf_agents_workflows(workflow_name)
|
|
1669
1729
|
`;
|
|
1670
|
-
this.ctx.storage.sql.exec("DELETE FROM cf_agents_state WHERE id = ?", STATE_WAS_CHANGED);
|
|
1671
1730
|
this.sql`
|
|
1672
1731
|
CREATE TABLE IF NOT EXISTS cf_agents_runs (
|
|
1673
1732
|
id TEXT PRIMARY KEY NOT NULL,
|
|
1674
1733
|
name TEXT NOT NULL,
|
|
1675
1734
|
snapshot TEXT,
|
|
1676
|
-
created_at INTEGER NOT NULL
|
|
1735
|
+
created_at INTEGER NOT NULL,
|
|
1736
|
+
completed_at INTEGER,
|
|
1737
|
+
outcome TEXT,
|
|
1738
|
+
error_message TEXT
|
|
1677
1739
|
)
|
|
1678
1740
|
`;
|
|
1679
1741
|
this.sql`
|
|
@@ -1752,10 +1814,11 @@ var Agent = class Agent extends DurableObject {
|
|
|
1752
1814
|
addColumnIfNotExists("ALTER TABLE cf_agent_tool_runs ADD COLUMN detached_no_progress_budget_ms INTEGER");
|
|
1753
1815
|
addColumnIfNotExists("ALTER TABLE cf_agent_tool_runs ADD COLUMN last_progress_at INTEGER");
|
|
1754
1816
|
addColumnIfNotExists("ALTER TABLE cf_agent_tool_runs ADD COLUMN detached_on_milestones TEXT");
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1817
|
+
addColumnIfNotExists("ALTER TABLE cf_agents_runs ADD COLUMN completed_at INTEGER");
|
|
1818
|
+
addColumnIfNotExists("ALTER TABLE cf_agents_runs ADD COLUMN outcome TEXT");
|
|
1819
|
+
addColumnIfNotExists("ALTER TABLE cf_agents_runs ADD COLUMN error_message TEXT");
|
|
1820
|
+
addColumnIfNotExists("ALTER TABLE cf_agent_tool_runs ADD COLUMN event_delivery TEXT");
|
|
1821
|
+
this.ctx.storage.kv.put(SCHEMA_VERSION_KEY, CURRENT_SCHEMA_VERSION);
|
|
1759
1822
|
}
|
|
1760
1823
|
this._schemaInitialization = {
|
|
1761
1824
|
previousVersion: schemaVersion,
|
|
@@ -1763,9 +1826,31 @@ var Agent = class Agent extends DurableObject {
|
|
|
1763
1826
|
migrated: schemaVersion < CURRENT_SCHEMA_VERSION
|
|
1764
1827
|
};
|
|
1765
1828
|
}
|
|
1829
|
+
/**
|
|
1830
|
+
* Read the Agent's schema version from its KV key. A DO created before the
|
|
1831
|
+
* State capability owned `cf_agents_state` has the version as a row in that
|
|
1832
|
+
* table instead: read it once, move it to the key, and delete the row so the
|
|
1833
|
+
* table is left with a single owner. Synchronous (`storage.kv`) because the
|
|
1834
|
+
* constructor gates DDL on it.
|
|
1835
|
+
*/
|
|
1836
|
+
_readSchemaVersion() {
|
|
1837
|
+
const stored = this.ctx.storage.kv.get(SCHEMA_VERSION_KEY);
|
|
1838
|
+
if (stored !== void 0) return stored;
|
|
1839
|
+
if (!(this.ctx.storage.sql.exec("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'cf_agents_state'").toArray().length > 0)) return 0;
|
|
1840
|
+
const rows = this.ctx.storage.sql.exec("SELECT state FROM cf_agents_state WHERE id = ?", LEGACY_SCHEMA_VERSION_ROW_ID).toArray();
|
|
1841
|
+
if (rows.length === 0) return 0;
|
|
1842
|
+
const version = Number(rows[0].state) || 0;
|
|
1843
|
+
this.ctx.storage.kv.put(SCHEMA_VERSION_KEY, version);
|
|
1844
|
+
this.ctx.storage.sql.exec("DELETE FROM cf_agents_state WHERE id = ?", LEGACY_SCHEMA_VERSION_ROW_ID);
|
|
1845
|
+
return version;
|
|
1846
|
+
}
|
|
1766
1847
|
constructor(ctx, env) {
|
|
1767
1848
|
super(ctx, env);
|
|
1768
1849
|
this.lifecycle = Lifecycle.install(this, { maxAlarmMemoryLimitStrikes: this._resolvedOptions.maxAlarmMemoryLimitStrikes });
|
|
1850
|
+
this._state = new State({
|
|
1851
|
+
validateStateChange: (nextState, source) => this.validateStateChange(nextState, source),
|
|
1852
|
+
onChanged: (nextState, source) => this._handleStateChanged(nextState, source)
|
|
1853
|
+
});
|
|
1769
1854
|
this._webSockets = new WebSockets({
|
|
1770
1855
|
handlers: {
|
|
1771
1856
|
onConnect: (connection, ctx) => this.onConnect(connection, ctx),
|
|
@@ -1773,13 +1858,12 @@ var Agent = class Agent extends DurableObject {
|
|
|
1773
1858
|
onClose: (connection, code, reason, wasClean) => this.onClose(connection, code, reason, wasClean),
|
|
1774
1859
|
onError: (connection, error) => this.onError(connection, error)
|
|
1775
1860
|
},
|
|
1776
|
-
|
|
1861
|
+
protocol: false,
|
|
1862
|
+
state: this._state,
|
|
1777
1863
|
getConnectionTags: (connection, ctx) => this.getConnectionTags(connection, ctx)
|
|
1778
1864
|
});
|
|
1779
|
-
this._state = DEFAULT_STATE;
|
|
1780
1865
|
this._disposables = new DisposableStore();
|
|
1781
1866
|
this._destroyed = false;
|
|
1782
|
-
this._rawStateAccessors = /* @__PURE__ */ new WeakMap();
|
|
1783
1867
|
this._persistenceHookMode = "none";
|
|
1784
1868
|
this._isFacet = false;
|
|
1785
1869
|
this._protocolBroadcastExcludeIds = /* @__PURE__ */ new Set();
|
|
@@ -1797,7 +1881,6 @@ var Agent = class Agent extends DurableObject {
|
|
|
1797
1881
|
this._ParentClass = Object.getPrototypeOf(this).constructor;
|
|
1798
1882
|
this.initialState = DEFAULT_STATE;
|
|
1799
1883
|
this.observability = genericObservability;
|
|
1800
|
-
this._flushingQueue = false;
|
|
1801
1884
|
this.maxConcurrentAgentTools = Infinity;
|
|
1802
1885
|
const routeHost = this;
|
|
1803
1886
|
setLifecycleRouteTransport(this.lifecycle, {
|
|
@@ -1832,6 +1915,20 @@ var Agent = class Agent extends DurableObject {
|
|
|
1832
1915
|
if (typeof method !== "function") return void 0;
|
|
1833
1916
|
return (payload, schedule) => method.call(this, payload, schedule);
|
|
1834
1917
|
});
|
|
1918
|
+
this._queue = new Queue({
|
|
1919
|
+
retry: this._resolvedOptions.retry,
|
|
1920
|
+
onError: (error) => runInInvocation({
|
|
1921
|
+
agent: this,
|
|
1922
|
+
connection: void 0,
|
|
1923
|
+
request: void 0,
|
|
1924
|
+
email: void 0
|
|
1925
|
+
}, () => this.onError(error))
|
|
1926
|
+
});
|
|
1927
|
+
setQueueCallbackResolver(this._queue, (name) => {
|
|
1928
|
+
const method = this[name];
|
|
1929
|
+
if (typeof method !== "function") return void 0;
|
|
1930
|
+
return (payload, item) => method.call(this, payload, item);
|
|
1931
|
+
});
|
|
1835
1932
|
this.tasks = new Tasks({ onError: (error) => this.onError(error) });
|
|
1836
1933
|
setTaskRoutedMemoryLimitHandler(this.tasks, (context) => {
|
|
1837
1934
|
return this.onAlarmMemoryLimit?.call(this, context);
|
|
@@ -1857,7 +1954,7 @@ var Agent = class Agent extends DurableObject {
|
|
|
1857
1954
|
createAuthProvider: (callbackUrl) => this.createMcpOAuthProvider(callbackUrl)
|
|
1858
1955
|
});
|
|
1859
1956
|
});
|
|
1860
|
-
this.lifecycle.use(this.scheduler).use(this.mcp).use(this._webSockets
|
|
1957
|
+
this.lifecycle.use(this.scheduler).use(this._queue).use(this.mcp).use(this._state).use(this._webSockets).use(this.tasks).use(this._dynamicAgents);
|
|
1861
1958
|
let mcpBroadcastReady = false;
|
|
1862
1959
|
this._disposables.add(this.mcp.onServerStateChanged(() => {
|
|
1863
1960
|
if (mcpBroadcastReady) this.broadcastMcpServers();
|
|
@@ -1907,7 +2004,7 @@ var Agent = class Agent extends DurableObject {
|
|
|
1907
2004
|
this.onMessage = async (connection, message) => {
|
|
1908
2005
|
const replyBridge = dynamicAgentRpcReplyContext.getStore()?.bridge;
|
|
1909
2006
|
if (await __DO_NOT_USE_WILL_BREAK__agentContext.exit(() => this._cf_forwardSubAgentWebSocketMessage(connection, message, replyBridge))) return;
|
|
1910
|
-
|
|
2007
|
+
ensureConnectionWrapped(connection);
|
|
1911
2008
|
return runInInvocation({
|
|
1912
2009
|
agent: this,
|
|
1913
2010
|
connection,
|
|
@@ -1921,25 +2018,7 @@ var Agent = class Agent extends DurableObject {
|
|
|
1921
2018
|
} catch (_e) {
|
|
1922
2019
|
return this._tryCatch(() => _onMessage(connection, message));
|
|
1923
2020
|
}
|
|
1924
|
-
if (
|
|
1925
|
-
if (this.isConnectionReadonly(connection)) {
|
|
1926
|
-
connection.send(JSON.stringify({
|
|
1927
|
-
type: "cf_agent_state_error",
|
|
1928
|
-
error: "Connection is readonly"
|
|
1929
|
-
}));
|
|
1930
|
-
return;
|
|
1931
|
-
}
|
|
1932
|
-
try {
|
|
1933
|
-
this._setStateInternal(parsed.state, connection);
|
|
1934
|
-
} catch (e) {
|
|
1935
|
-
console.error("[Agent] State update rejected:", e);
|
|
1936
|
-
connection.send(JSON.stringify({
|
|
1937
|
-
type: "cf_agent_state_error",
|
|
1938
|
-
error: "State update rejected"
|
|
1939
|
-
}));
|
|
1940
|
-
}
|
|
1941
|
-
return;
|
|
1942
|
-
}
|
|
2021
|
+
if (this._webSockets.applyStateFrame(connection, parsed)) return;
|
|
1943
2022
|
if (isRPCRequest(parsed)) {
|
|
1944
2023
|
try {
|
|
1945
2024
|
const { id, method, args } = parsed;
|
|
@@ -2003,7 +2082,7 @@ var Agent = class Agent extends DurableObject {
|
|
|
2003
2082
|
};
|
|
2004
2083
|
const _onConnect = this.onConnect.bind(this);
|
|
2005
2084
|
this.onConnect = async (connection, ctx) => {
|
|
2006
|
-
|
|
2085
|
+
ensureConnectionWrapped(connection);
|
|
2007
2086
|
const subAgentOuterUrl = ctx.request.headers.get(SUB_AGENT_OUTER_URL_HEADER);
|
|
2008
2087
|
if (subAgentOuterUrl) this._unsafe_setConnectionFlag(connection, CF_SUB_AGENT_OUTER_URL_KEY, subAgentOuterUrl);
|
|
2009
2088
|
if (await __DO_NOT_USE_WILL_BREAK__agentContext.exit(() => this._cf_forwardSubAgentWebSocketConnect(connection, ctx.request, { gate: false }))) return;
|
|
@@ -2015,6 +2094,14 @@ var Agent = class Agent extends DurableObject {
|
|
|
2015
2094
|
}, async () => {
|
|
2016
2095
|
if (this.shouldConnectionBeReadonly(connection, ctx)) this.setConnectionReadonly(connection, true);
|
|
2017
2096
|
if (this.shouldSendProtocolMessages(connection, ctx)) {
|
|
2097
|
+
const wasExcludedFromStateInitBroadcast = this._protocolBroadcastExcludeIds.has(connection.id);
|
|
2098
|
+
let currentState;
|
|
2099
|
+
this._protocolBroadcastExcludeIds.add(connection.id);
|
|
2100
|
+
try {
|
|
2101
|
+
currentState = this.state;
|
|
2102
|
+
} finally {
|
|
2103
|
+
if (!wasExcludedFromStateInitBroadcast) this._protocolBroadcastExcludeIds.delete(connection.id);
|
|
2104
|
+
}
|
|
2018
2105
|
if (this._resolvedOptions.sendIdentityOnConnect) {
|
|
2019
2106
|
const ctor = this.constructor;
|
|
2020
2107
|
if (ctor.options?.sendIdentityOnConnect === void 0 && !_sendIdentityWarnedClasses.has(ctor) && !this._isFacet) {
|
|
@@ -2023,29 +2110,16 @@ var Agent = class Agent extends DurableObject {
|
|
|
2023
2110
|
console.warn(`[Agent] ${ctor.name}: sending instance name "${this.name}" to clients via sendIdentityOnConnect (the name is not visible in the URL with custom routing). If this name is sensitive, add \`static options = { sendIdentityOnConnect: false }\` to opt out. Set it to true to silence this message.`);
|
|
2024
2111
|
}
|
|
2025
2112
|
}
|
|
2026
|
-
|
|
2113
|
+
this._webSockets.sendConnectFrames(connection, {
|
|
2027
2114
|
name: this.name,
|
|
2028
|
-
agent: camelCaseToKebabCase(this._ParentClass.name)
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
}
|
|
2032
|
-
const wasExcludedFromStateInitBroadcast = this._protocolBroadcastExcludeIds.has(connection.id);
|
|
2033
|
-
let currentState;
|
|
2034
|
-
this._protocolBroadcastExcludeIds.add(connection.id);
|
|
2035
|
-
try {
|
|
2036
|
-
currentState = this.state;
|
|
2037
|
-
} finally {
|
|
2038
|
-
if (!wasExcludedFromStateInitBroadcast) this._protocolBroadcastExcludeIds.delete(connection.id);
|
|
2039
|
-
}
|
|
2040
|
-
if (currentState !== void 0) connection.send(JSON.stringify({
|
|
2041
|
-
state: currentState,
|
|
2042
|
-
type: "cf_agent_state"
|
|
2043
|
-
}));
|
|
2115
|
+
agent: camelCaseToKebabCase(this._ParentClass.name)
|
|
2116
|
+
});
|
|
2117
|
+
} else if (currentState !== void 0) this._webSockets.sendState(connection);
|
|
2044
2118
|
connection.send(JSON.stringify({
|
|
2045
2119
|
mcp: this.getMcpServers(),
|
|
2046
2120
|
type: "cf_agent_mcp_servers"
|
|
2047
2121
|
}));
|
|
2048
|
-
} else this.
|
|
2122
|
+
} else this._webSockets.setProtocolEnabled(connection, false);
|
|
2049
2123
|
this._emit("connect", { connectionId: connection.id });
|
|
2050
2124
|
await this._replayAgentToolRuns(connection);
|
|
2051
2125
|
return this._tryCatch(() => _onConnect(connection, ctx));
|
|
@@ -2142,13 +2216,14 @@ var Agent = class Agent extends DurableObject {
|
|
|
2142
2216
|
for (const conn of this.getConnections()) if (!this.isConnectionProtocolEnabled(conn)) exclude.push(conn.id);
|
|
2143
2217
|
this.broadcast(msg, exclude);
|
|
2144
2218
|
}
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2219
|
+
/**
|
|
2220
|
+
* React to a persisted state change from the State capability.
|
|
2221
|
+
*
|
|
2222
|
+
* Reproduces the pre-migration steps 3-4: broadcast the new state to
|
|
2223
|
+
* protocol-enabled connections (excluding the originating connection) and
|
|
2224
|
+
* run the notification hook off the invocation tail.
|
|
2225
|
+
*/
|
|
2226
|
+
_handleStateChanged(nextState, source) {
|
|
2152
2227
|
this._broadcastProtocol(JSON.stringify({
|
|
2153
2228
|
state: nextState,
|
|
2154
2229
|
type: "cf_agent_state"
|
|
@@ -2180,69 +2255,7 @@ var Agent = class Agent extends DurableObject {
|
|
|
2180
2255
|
setState(state) {
|
|
2181
2256
|
const store = __DO_NOT_USE_WILL_BREAK__agentContext.getStore();
|
|
2182
2257
|
if (store?.connection && this.isConnectionReadonly(store.connection)) throw new Error("Connection is readonly");
|
|
2183
|
-
this.
|
|
2184
|
-
}
|
|
2185
|
-
/**
|
|
2186
|
-
* Wraps connection.state and connection.setState so that internal
|
|
2187
|
-
* _cf_-prefixed flags (readonly, no-protocol) are hidden from user code
|
|
2188
|
-
* and cannot be accidentally overwritten.
|
|
2189
|
-
*
|
|
2190
|
-
* Idempotent — safe to call multiple times on the same connection.
|
|
2191
|
-
* After hibernation, the _rawStateAccessors WeakMap is empty but the
|
|
2192
|
-
* connection's state getter still reads from the persisted WebSocket
|
|
2193
|
-
* attachment. Calling this method re-captures the raw getter so that
|
|
2194
|
-
* predicate methods (isConnectionReadonly, isConnectionProtocolEnabled)
|
|
2195
|
-
* work correctly post-hibernation.
|
|
2196
|
-
*/
|
|
2197
|
-
_ensureConnectionWrapped(connection) {
|
|
2198
|
-
if (this._rawStateAccessors.has(connection)) return;
|
|
2199
|
-
const descriptor = Object.getOwnPropertyDescriptor(connection, "state");
|
|
2200
|
-
let getRaw;
|
|
2201
|
-
let setRaw;
|
|
2202
|
-
if (descriptor?.get) {
|
|
2203
|
-
getRaw = descriptor.get.bind(connection);
|
|
2204
|
-
setRaw = connection.setState.bind(connection);
|
|
2205
|
-
} else {
|
|
2206
|
-
let rawState = connection.state ?? null;
|
|
2207
|
-
getRaw = () => rawState;
|
|
2208
|
-
setRaw = (state) => {
|
|
2209
|
-
rawState = state;
|
|
2210
|
-
return rawState;
|
|
2211
|
-
};
|
|
2212
|
-
}
|
|
2213
|
-
this._rawStateAccessors.set(connection, {
|
|
2214
|
-
getRaw,
|
|
2215
|
-
setRaw
|
|
2216
|
-
});
|
|
2217
|
-
Object.defineProperty(connection, "state", {
|
|
2218
|
-
configurable: true,
|
|
2219
|
-
enumerable: true,
|
|
2220
|
-
get() {
|
|
2221
|
-
const raw = getRaw();
|
|
2222
|
-
if (raw != null && typeof raw === "object" && rawHasInternalKeys(raw)) return stripInternalKeys(raw);
|
|
2223
|
-
return raw;
|
|
2224
|
-
}
|
|
2225
|
-
});
|
|
2226
|
-
Object.defineProperty(connection, "setState", {
|
|
2227
|
-
configurable: true,
|
|
2228
|
-
writable: true,
|
|
2229
|
-
value(stateOrFn) {
|
|
2230
|
-
const raw = getRaw();
|
|
2231
|
-
const flags = raw != null && typeof raw === "object" ? extractInternalFlags(raw) : {};
|
|
2232
|
-
const hasFlags = Object.keys(flags).length > 0;
|
|
2233
|
-
let newUserState;
|
|
2234
|
-
if (typeof stateOrFn === "function") newUserState = stateOrFn(hasFlags ? stripInternalKeys(raw) : raw);
|
|
2235
|
-
else newUserState = stateOrFn;
|
|
2236
|
-
if (hasFlags) {
|
|
2237
|
-
if (newUserState != null && typeof newUserState === "object") return setRaw({
|
|
2238
|
-
...newUserState,
|
|
2239
|
-
...flags
|
|
2240
|
-
});
|
|
2241
|
-
return setRaw(flags);
|
|
2242
|
-
}
|
|
2243
|
-
return setRaw(newUserState);
|
|
2244
|
-
}
|
|
2245
|
-
});
|
|
2258
|
+
this._state.set(state, "server");
|
|
2246
2259
|
}
|
|
2247
2260
|
/**
|
|
2248
2261
|
* Mark a connection as readonly or readwrite
|
|
@@ -2250,17 +2263,7 @@ var Agent = class Agent extends DurableObject {
|
|
|
2250
2263
|
* @param readonly Whether the connection should be readonly (default: true)
|
|
2251
2264
|
*/
|
|
2252
2265
|
setConnectionReadonly(connection, readonly = true) {
|
|
2253
|
-
|
|
2254
|
-
const accessors = this._rawStateAccessors.get(connection);
|
|
2255
|
-
const raw = accessors.getRaw() ?? {};
|
|
2256
|
-
if (readonly) accessors.setRaw({
|
|
2257
|
-
...raw,
|
|
2258
|
-
[CF_READONLY_KEY]: true
|
|
2259
|
-
});
|
|
2260
|
-
else {
|
|
2261
|
-
const { [CF_READONLY_KEY]: _, ...rest } = raw;
|
|
2262
|
-
accessors.setRaw(Object.keys(rest).length > 0 ? rest : null);
|
|
2263
|
-
}
|
|
2266
|
+
setConnectionReadonly(connection, readonly);
|
|
2264
2267
|
}
|
|
2265
2268
|
/**
|
|
2266
2269
|
* Check if a connection is marked as readonly.
|
|
@@ -2271,8 +2274,7 @@ var Agent = class Agent extends DurableObject {
|
|
|
2271
2274
|
* @returns True if the connection is readonly
|
|
2272
2275
|
*/
|
|
2273
2276
|
isConnectionReadonly(connection) {
|
|
2274
|
-
|
|
2275
|
-
return !!this._rawStateAccessors.get(connection).getRaw()?.[CF_READONLY_KEY];
|
|
2277
|
+
return isConnectionReadonly(connection);
|
|
2276
2278
|
}
|
|
2277
2279
|
/**
|
|
2278
2280
|
* ⚠️ INTERNAL — DO NOT USE IN APPLICATION CODE. ⚠️
|
|
@@ -2287,30 +2289,20 @@ var Agent = class Agent extends DurableObject {
|
|
|
2287
2289
|
* @internal
|
|
2288
2290
|
*/
|
|
2289
2291
|
_unsafe_getConnectionFlag(connection, key) {
|
|
2290
|
-
|
|
2291
|
-
return this._rawStateAccessors.get(connection).getRaw()?.[key];
|
|
2292
|
+
return getConnectionFlag(connection, key);
|
|
2292
2293
|
}
|
|
2293
2294
|
/**
|
|
2294
2295
|
* ⚠️ INTERNAL — DO NOT USE IN APPLICATION CODE. ⚠️
|
|
2295
2296
|
*
|
|
2296
2297
|
* Write an internal `_cf_`-prefixed flag to the raw connection state,
|
|
2297
2298
|
* bypassing the user-facing state wrapper. The key must be registered
|
|
2298
|
-
*
|
|
2299
|
-
* and hidden from `connection.state`.
|
|
2299
|
+
* with `registerInternalConnectionKeys` so it is preserved across user
|
|
2300
|
+
* `setState` calls and hidden from `connection.state`.
|
|
2300
2301
|
*
|
|
2301
2302
|
* @internal
|
|
2302
2303
|
*/
|
|
2303
2304
|
_unsafe_setConnectionFlag(connection, key, value) {
|
|
2304
|
-
|
|
2305
|
-
const accessors = this._rawStateAccessors.get(connection);
|
|
2306
|
-
const raw = accessors.getRaw() ?? {};
|
|
2307
|
-
if (value === void 0) {
|
|
2308
|
-
const { [key]: _, ...rest } = raw;
|
|
2309
|
-
accessors.setRaw(Object.keys(rest).length > 0 ? rest : null);
|
|
2310
|
-
} else accessors.setRaw({
|
|
2311
|
-
...raw,
|
|
2312
|
-
[key]: value
|
|
2313
|
-
});
|
|
2305
|
+
setConnectionFlag(connection, key, value);
|
|
2314
2306
|
}
|
|
2315
2307
|
/**
|
|
2316
2308
|
* Override this method to determine if a connection should be readonly on connect
|
|
@@ -2351,21 +2343,7 @@ var Agent = class Agent extends DurableObject {
|
|
|
2351
2343
|
* @returns True if the connection receives protocol messages
|
|
2352
2344
|
*/
|
|
2353
2345
|
isConnectionProtocolEnabled(connection) {
|
|
2354
|
-
|
|
2355
|
-
return !this._rawStateAccessors.get(connection).getRaw()?.[CF_NO_PROTOCOL_KEY];
|
|
2356
|
-
}
|
|
2357
|
-
/**
|
|
2358
|
-
* Mark a connection as having protocol messages disabled.
|
|
2359
|
-
* Called internally when shouldSendProtocolMessages returns false.
|
|
2360
|
-
*/
|
|
2361
|
-
_setConnectionNoProtocol(connection) {
|
|
2362
|
-
this._ensureConnectionWrapped(connection);
|
|
2363
|
-
const accessors = this._rawStateAccessors.get(connection);
|
|
2364
|
-
const raw = accessors.getRaw() ?? {};
|
|
2365
|
-
accessors.setRaw({
|
|
2366
|
-
...raw,
|
|
2367
|
-
[CF_NO_PROTOCOL_KEY]: true
|
|
2368
|
-
});
|
|
2346
|
+
return isConnectionProtocolEnabled(connection);
|
|
2369
2347
|
}
|
|
2370
2348
|
/**
|
|
2371
2349
|
* Called before the Agent's state is persisted and broadcast.
|
|
@@ -2553,19 +2531,21 @@ var Agent = class Agent extends DurableObject {
|
|
|
2553
2531
|
proto = Object.getPrototypeOf(proto);
|
|
2554
2532
|
}
|
|
2555
2533
|
}
|
|
2534
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2556
2535
|
let proto = Object.getPrototypeOf(this);
|
|
2557
|
-
|
|
2558
|
-
while (proto && proto !== Object.prototype && depth < 10) {
|
|
2536
|
+
while (proto && proto !== Agent.prototype && proto !== Object.prototype) {
|
|
2559
2537
|
const methodNames = Object.getOwnPropertyNames(proto);
|
|
2560
2538
|
for (const methodName of methodNames) {
|
|
2539
|
+
if (seen.has(methodName)) continue;
|
|
2540
|
+
seen.add(methodName);
|
|
2561
2541
|
const descriptor = Object.getOwnPropertyDescriptor(proto, methodName);
|
|
2562
|
-
if (baseMethods.has(methodName) || methodName.startsWith("_") || !descriptor || !!descriptor.get || typeof descriptor.value !== "function") continue;
|
|
2563
|
-
const
|
|
2564
|
-
|
|
2542
|
+
if (baseMethods.has(methodName) || methodName.startsWith("_") || !descriptor || !!descriptor.get || typeof descriptor.value !== "function" || agentContextWrappers.has(descriptor.value)) continue;
|
|
2543
|
+
const method = descriptor.value;
|
|
2544
|
+
const wrappedFunction = withAgentContext(method);
|
|
2545
|
+
if (this._isCallable(methodName)) copyCallableMetadata(method, wrappedFunction);
|
|
2565
2546
|
this.constructor.prototype[methodName] = wrappedFunction;
|
|
2566
2547
|
}
|
|
2567
2548
|
proto = Object.getPrototypeOf(proto);
|
|
2568
|
-
depth++;
|
|
2569
2549
|
}
|
|
2570
2550
|
}
|
|
2571
2551
|
onError(connectionOrError, error) {
|
|
@@ -2610,111 +2590,42 @@ var Agent = class Agent extends DurableObject {
|
|
|
2610
2590
|
});
|
|
2611
2591
|
}
|
|
2612
2592
|
/**
|
|
2613
|
-
* Queue a task to
|
|
2593
|
+
* Queue a task to run in the background.
|
|
2594
|
+
*
|
|
2595
|
+
* The item is durable: it runs from the Lifecycle alarm event loop after
|
|
2596
|
+
* this call returns, in push order, one at a time, with retries per
|
|
2597
|
+
* `options.retry`, and survives the Durable Object leaving memory.
|
|
2614
2598
|
* @param callback Name of the method to call
|
|
2615
2599
|
* @param payload Payload to pass to the callback
|
|
2616
2600
|
* @param options Options for the queued task
|
|
2617
2601
|
* @param options.retry Retry options for the callback execution
|
|
2602
|
+
* @param options.id Stable id; a push with an existing id replaces that item
|
|
2618
2603
|
* @returns The ID of the queued task
|
|
2619
2604
|
*/
|
|
2620
2605
|
async queue(callback, payload, options) {
|
|
2621
|
-
const id = nanoid(9);
|
|
2622
2606
|
if (typeof callback !== "string") throw new Error("Callback must be a string");
|
|
2623
2607
|
if (typeof this[callback] !== "function") throw new Error(`this.${callback} is not a function`);
|
|
2624
|
-
|
|
2625
|
-
const retryJson = options?.retry ? JSON.stringify(options.retry) : null;
|
|
2626
|
-
this.sql`
|
|
2627
|
-
INSERT OR REPLACE INTO cf_agents_queues (id, payload, callback, retry_options)
|
|
2628
|
-
VALUES (${id}, ${JSON.stringify(payload)}, ${callback}, ${retryJson})
|
|
2629
|
-
`;
|
|
2630
|
-
this._emit("queue:create", {
|
|
2631
|
-
callback,
|
|
2632
|
-
id
|
|
2633
|
-
});
|
|
2634
|
-
this._flushQueue().catch((e) => {
|
|
2635
|
-
console.error("Error flushing queue:", e);
|
|
2636
|
-
});
|
|
2637
|
-
return id;
|
|
2638
|
-
}
|
|
2639
|
-
async _flushQueue() {
|
|
2640
|
-
if (this._flushingQueue) return;
|
|
2641
|
-
this._flushingQueue = true;
|
|
2642
|
-
try {
|
|
2643
|
-
while (true) {
|
|
2644
|
-
const result = this.sql`
|
|
2645
|
-
SELECT * FROM cf_agents_queues
|
|
2646
|
-
ORDER BY created_at ASC
|
|
2647
|
-
`;
|
|
2648
|
-
if (!result || result.length === 0) break;
|
|
2649
|
-
for (const row of result || []) {
|
|
2650
|
-
const callback = this[row.callback];
|
|
2651
|
-
if (!callback) {
|
|
2652
|
-
console.error(`callback ${row.callback} not found`);
|
|
2653
|
-
await this.dequeue(row.id);
|
|
2654
|
-
continue;
|
|
2655
|
-
}
|
|
2656
|
-
const { connection, request, email } = __DO_NOT_USE_WILL_BREAK__agentContext.getStore() || {};
|
|
2657
|
-
await runInInvocation({
|
|
2658
|
-
agent: this,
|
|
2659
|
-
connection,
|
|
2660
|
-
request,
|
|
2661
|
-
email
|
|
2662
|
-
}, async () => {
|
|
2663
|
-
const { maxAttempts, baseDelayMs, maxDelayMs } = resolveRetryConfig(parseRetryOptions(row), this._resolvedOptions.retry);
|
|
2664
|
-
const parsedPayload = JSON.parse(row.payload);
|
|
2665
|
-
try {
|
|
2666
|
-
await tryN(maxAttempts, async (attempt) => {
|
|
2667
|
-
if (attempt > 1) this._emit("queue:retry", {
|
|
2668
|
-
callback: row.callback,
|
|
2669
|
-
id: row.id,
|
|
2670
|
-
attempt,
|
|
2671
|
-
maxAttempts
|
|
2672
|
-
});
|
|
2673
|
-
await callback.bind(this)(parsedPayload, row);
|
|
2674
|
-
}, {
|
|
2675
|
-
baseDelayMs,
|
|
2676
|
-
maxDelayMs
|
|
2677
|
-
});
|
|
2678
|
-
} catch (e) {
|
|
2679
|
-
console.error(`queue callback "${row.callback}" failed after ${maxAttempts} attempts`, e);
|
|
2680
|
-
this._emit("queue:error", {
|
|
2681
|
-
callback: row.callback,
|
|
2682
|
-
id: row.id,
|
|
2683
|
-
error: e instanceof Error ? e.message : String(e),
|
|
2684
|
-
attempts: maxAttempts
|
|
2685
|
-
});
|
|
2686
|
-
try {
|
|
2687
|
-
await this.onError(e);
|
|
2688
|
-
} catch {}
|
|
2689
|
-
} finally {
|
|
2690
|
-
this.dequeue(row.id);
|
|
2691
|
-
}
|
|
2692
|
-
}, { detached: true });
|
|
2693
|
-
}
|
|
2694
|
-
}
|
|
2695
|
-
} finally {
|
|
2696
|
-
this._flushingQueue = false;
|
|
2697
|
-
}
|
|
2608
|
+
return (await this._queue.push(callback, payload, options)).id;
|
|
2698
2609
|
}
|
|
2699
2610
|
/**
|
|
2700
2611
|
* Dequeue a task by ID
|
|
2701
2612
|
* @param id ID of the task to dequeue
|
|
2702
2613
|
*/
|
|
2703
2614
|
dequeue(id) {
|
|
2704
|
-
this.
|
|
2615
|
+
return this._queue.cancel(id);
|
|
2705
2616
|
}
|
|
2706
2617
|
/**
|
|
2707
2618
|
* Dequeue all tasks
|
|
2708
2619
|
*/
|
|
2709
2620
|
dequeueAll() {
|
|
2710
|
-
this.
|
|
2621
|
+
return this._queue.cancelAll();
|
|
2711
2622
|
}
|
|
2712
2623
|
/**
|
|
2713
2624
|
* Dequeue all tasks by callback
|
|
2714
2625
|
* @param callback Name of the callback to dequeue
|
|
2715
2626
|
*/
|
|
2716
2627
|
dequeueAllByCallback(callback) {
|
|
2717
|
-
this.
|
|
2628
|
+
return this._queue.cancelAll(callback);
|
|
2718
2629
|
}
|
|
2719
2630
|
/**
|
|
2720
2631
|
* Get a queued task by ID
|
|
@@ -2722,31 +2633,16 @@ var Agent = class Agent extends DurableObject {
|
|
|
2722
2633
|
* @returns The task or undefined if not found
|
|
2723
2634
|
*/
|
|
2724
2635
|
getQueue(id) {
|
|
2725
|
-
|
|
2726
|
-
SELECT * FROM cf_agents_queues WHERE id = ${id}
|
|
2727
|
-
`;
|
|
2728
|
-
if (!result || result.length === 0) return void 0;
|
|
2729
|
-
const row = result[0];
|
|
2730
|
-
return {
|
|
2731
|
-
...row,
|
|
2732
|
-
payload: JSON.parse(row.payload),
|
|
2733
|
-
retry: parseRetryOptions(row)
|
|
2734
|
-
};
|
|
2636
|
+
return this._queue.get(id);
|
|
2735
2637
|
}
|
|
2736
2638
|
/**
|
|
2737
|
-
* Get all
|
|
2639
|
+
* Get all queued tasks whose payload has `key` equal to `value`
|
|
2738
2640
|
* @param key Key to filter by
|
|
2739
2641
|
* @param value Value to filter by
|
|
2740
2642
|
* @returns Array of matching QueueItem objects
|
|
2741
2643
|
*/
|
|
2742
|
-
getQueues(key, value) {
|
|
2743
|
-
return this.
|
|
2744
|
-
SELECT * FROM cf_agents_queues
|
|
2745
|
-
`.filter((row) => JSON.parse(row.payload)[key] === value).map((row) => ({
|
|
2746
|
-
...row,
|
|
2747
|
-
payload: JSON.parse(row.payload),
|
|
2748
|
-
retry: parseRetryOptions(row)
|
|
2749
|
-
}));
|
|
2644
|
+
async getQueues(key, value) {
|
|
2645
|
+
return (await this._queue.list()).filter((item) => typeof item.payload === "object" && item.payload !== null && item.payload[key] === value);
|
|
2750
2646
|
}
|
|
2751
2647
|
_lifecycleRouteAddress() {
|
|
2752
2648
|
return this._dynamicAgents.lifecycleRouteAddress();
|
|
@@ -2758,7 +2654,8 @@ var Agent = class Agent extends DurableObject {
|
|
|
2758
2654
|
return this._dynamicAgents.routeLifecycleToTarget(target, envelope);
|
|
2759
2655
|
}
|
|
2760
2656
|
/** Single native-RPC aperture for routed Lifecycle capabilities. */
|
|
2761
|
-
_cf_routeLifecycle(target, envelope) {
|
|
2657
|
+
async _cf_routeLifecycle(target, envelope) {
|
|
2658
|
+
await this.__unsafe_ensureInitialized();
|
|
2762
2659
|
return this._dynamicAgents.routeLifecycle(target, envelope);
|
|
2763
2660
|
}
|
|
2764
2661
|
_rootAlarmOwner() {
|
|
@@ -2775,6 +2672,7 @@ var Agent = class Agent extends DurableObject {
|
|
|
2775
2672
|
* @internal
|
|
2776
2673
|
*/
|
|
2777
2674
|
async _cf_cleanupFacetPrefix(ownerPath) {
|
|
2675
|
+
await this.__unsafe_ensureInitialized();
|
|
2778
2676
|
await this._dynamicAgents.cleanupPrefix(ownerPath);
|
|
2779
2677
|
}
|
|
2780
2678
|
/**
|
|
@@ -2783,7 +2681,8 @@ var Agent = class Agent extends DurableObject {
|
|
|
2783
2681
|
* physical alarm, so this lets facet work use the root alarm heartbeat.
|
|
2784
2682
|
* @internal
|
|
2785
2683
|
*/
|
|
2786
|
-
_cf_acquireFacetKeepAlive(ownerPath) {
|
|
2684
|
+
async _cf_acquireFacetKeepAlive(ownerPath) {
|
|
2685
|
+
await this.__unsafe_ensureInitialized();
|
|
2787
2686
|
return this._dynamicAgents.acquireKeepAlive(ownerPath);
|
|
2788
2687
|
}
|
|
2789
2688
|
/**
|
|
@@ -2791,8 +2690,9 @@ var Agent = class Agent extends DurableObject {
|
|
|
2791
2690
|
* Idempotent so disposer calls can safely race or run twice.
|
|
2792
2691
|
* @internal
|
|
2793
2692
|
*/
|
|
2794
|
-
_cf_releaseFacetKeepAlive(token) {
|
|
2795
|
-
|
|
2693
|
+
async _cf_releaseFacetKeepAlive(token) {
|
|
2694
|
+
await this.__unsafe_ensureInitialized();
|
|
2695
|
+
await this._dynamicAgents.releaseKeepAlive(token);
|
|
2796
2696
|
}
|
|
2797
2697
|
/**
|
|
2798
2698
|
* Register a facet's durable run row in the root-side index so root
|
|
@@ -2800,15 +2700,17 @@ var Agent = class Agent extends DurableObject {
|
|
|
2800
2700
|
* The facet remains authoritative for snapshots and recovery hooks.
|
|
2801
2701
|
* @internal
|
|
2802
2702
|
*/
|
|
2803
|
-
_cf_registerFacetRun(ownerPath, runId) {
|
|
2804
|
-
|
|
2703
|
+
async _cf_registerFacetRun(ownerPath, runId) {
|
|
2704
|
+
await this.__unsafe_ensureInitialized();
|
|
2705
|
+
await this._dynamicAgents.registerRun(ownerPath, runId);
|
|
2805
2706
|
}
|
|
2806
2707
|
/**
|
|
2807
2708
|
* Remove a completed facet fiber from the root-side index.
|
|
2808
2709
|
* @internal
|
|
2809
2710
|
*/
|
|
2810
|
-
_cf_unregisterFacetRun(ownerPath, runId) {
|
|
2811
|
-
|
|
2711
|
+
async _cf_unregisterFacetRun(ownerPath, runId) {
|
|
2712
|
+
await this.__unsafe_ensureInitialized();
|
|
2713
|
+
await this._dynamicAgents.unregisterRun(ownerPath, runId);
|
|
2812
2714
|
}
|
|
2813
2715
|
/**
|
|
2814
2716
|
* Schedule a task to be executed in the future
|
|
@@ -3493,6 +3395,7 @@ var Agent = class Agent extends DurableObject {
|
|
|
3493
3395
|
};
|
|
3494
3396
|
let root;
|
|
3495
3397
|
let registeredFacetRun = false;
|
|
3398
|
+
let bodyOutcome;
|
|
3496
3399
|
let dispose = () => {};
|
|
3497
3400
|
try {
|
|
3498
3401
|
if ("initialSnapshot" in (options ?? {})) writeSnapshot(options?.initialSnapshot);
|
|
@@ -3506,16 +3409,29 @@ var Agent = class Agent extends DurableObject {
|
|
|
3506
3409
|
writeSnapshot(options?.wrapStash ? options.wrapStash(data) : data);
|
|
3507
3410
|
};
|
|
3508
3411
|
try {
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3412
|
+
let result;
|
|
3413
|
+
try {
|
|
3414
|
+
result = await _fiberALS.run({
|
|
3415
|
+
id,
|
|
3416
|
+
signal,
|
|
3417
|
+
stash
|
|
3418
|
+
}, () => fn({
|
|
3419
|
+
id,
|
|
3420
|
+
signal,
|
|
3421
|
+
stash,
|
|
3422
|
+
snapshot: null
|
|
3423
|
+
}));
|
|
3424
|
+
} catch (error) {
|
|
3425
|
+
bodyOutcome = {
|
|
3426
|
+
status: signal.aborted ? "aborted" : "error",
|
|
3427
|
+
error: this._fiberErrorMessage(error)
|
|
3428
|
+
};
|
|
3429
|
+
throw error;
|
|
3430
|
+
}
|
|
3431
|
+
bodyOutcome = {
|
|
3432
|
+
status: "completed",
|
|
3433
|
+
error: null
|
|
3434
|
+
};
|
|
3519
3435
|
options?.beforeRunCleanup?.({ ok: true });
|
|
3520
3436
|
this._emit("fiber:run:completed", {
|
|
3521
3437
|
fiberId: id,
|
|
@@ -3540,14 +3456,29 @@ var Agent = class Agent extends DurableObject {
|
|
|
3540
3456
|
}
|
|
3541
3457
|
} finally {
|
|
3542
3458
|
this._runFiberActiveFibers.delete(id);
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
"
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3459
|
+
let rowDeleted = false;
|
|
3460
|
+
try {
|
|
3461
|
+
this._withAgentSpan("finalize_fiber", "fiber", {
|
|
3462
|
+
"cloudflare.agents.fiber.id": id,
|
|
3463
|
+
"cloudflare.agents.fiber.name": name
|
|
3464
|
+
}, () => {
|
|
3465
|
+
if (bodyOutcome) try {
|
|
3466
|
+
this.sql`
|
|
3467
|
+
UPDATE cf_agents_runs
|
|
3468
|
+
SET completed_at = ${Date.now()},
|
|
3469
|
+
outcome = ${bodyOutcome.status},
|
|
3470
|
+
error_message = ${bodyOutcome.error}
|
|
3471
|
+
WHERE id = ${id}
|
|
3472
|
+
`;
|
|
3473
|
+
} catch {}
|
|
3474
|
+
this.sql`DELETE FROM cf_agents_runs WHERE id = ${id}`;
|
|
3475
|
+
});
|
|
3476
|
+
rowDeleted = true;
|
|
3477
|
+
} catch (error) {
|
|
3478
|
+
console.error(`[Agent] Failed to finalize fiber "${name}" (${id}); leaving run row for recovery:`, error);
|
|
3479
|
+
}
|
|
3549
3480
|
dispose();
|
|
3550
|
-
if (root && registeredFacetRun) try {
|
|
3481
|
+
if (root && registeredFacetRun && rowDeleted) try {
|
|
3551
3482
|
await root._cf_unregisterFacetRun(this.selfPath, id);
|
|
3552
3483
|
} catch (e) {
|
|
3553
3484
|
console.error("[Agent] Failed to unregister facet fiber:", e);
|
|
@@ -3604,7 +3535,11 @@ var Agent = class Agent extends DurableObject {
|
|
|
3604
3535
|
const fiberRecoveryMaxAgeMs = this._resolvedOptions.fiberRecoveryMaxAgeMs;
|
|
3605
3536
|
let madeProgress = false;
|
|
3606
3537
|
try {
|
|
3607
|
-
const rows = this.sql`
|
|
3538
|
+
const rows = this.sql`
|
|
3539
|
+
SELECT id, name, snapshot, created_at, completed_at, outcome,
|
|
3540
|
+
error_message
|
|
3541
|
+
FROM cf_agents_runs
|
|
3542
|
+
`;
|
|
3608
3543
|
for (const row of rows) {
|
|
3609
3544
|
if (scanDeadlineMs > 0 && Date.now() - scanStartedAt > scanDeadlineMs) {
|
|
3610
3545
|
this._emit("fiber:recovery:skipped", {
|
|
@@ -3616,6 +3551,27 @@ var Agent = class Agent extends DurableObject {
|
|
|
3616
3551
|
break;
|
|
3617
3552
|
}
|
|
3618
3553
|
if (this._runFiberActiveFibers.has(row.id)) continue;
|
|
3554
|
+
const managedRow = this._readFiber(row.id);
|
|
3555
|
+
if (row.completed_at !== null && (!managedRow || row.outcome !== null)) {
|
|
3556
|
+
if (managedRow) this.sql`
|
|
3557
|
+
UPDATE cf_agents_fibers
|
|
3558
|
+
SET status = ${row.outcome},
|
|
3559
|
+
error_message = ${row.error_message},
|
|
3560
|
+
completed_at = ${row.completed_at}
|
|
3561
|
+
WHERE fiber_id = ${row.id}
|
|
3562
|
+
AND status IN ('pending', 'running')
|
|
3563
|
+
`;
|
|
3564
|
+
this.sql`DELETE FROM cf_agents_runs WHERE id = ${row.id}`;
|
|
3565
|
+
madeProgress = true;
|
|
3566
|
+
if (managedRow) this._notifyManagedFiberTerminal(row.id);
|
|
3567
|
+
continue;
|
|
3568
|
+
}
|
|
3569
|
+
if (managedRow && this._isTerminalFiberStatus(managedRow.status)) {
|
|
3570
|
+
this.sql`DELETE FROM cf_agents_runs WHERE id = ${row.id}`;
|
|
3571
|
+
madeProgress = true;
|
|
3572
|
+
this._notifyManagedFiberTerminal(row.id);
|
|
3573
|
+
continue;
|
|
3574
|
+
}
|
|
3619
3575
|
const snapshot = this._parseFiberRecoverySnapshot(row.id, row.snapshot);
|
|
3620
3576
|
const ctx = {
|
|
3621
3577
|
id: row.id,
|
|
@@ -3624,7 +3580,6 @@ var Agent = class Agent extends DurableObject {
|
|
|
3624
3580
|
createdAt: row.created_at,
|
|
3625
3581
|
recoveryReason: "interrupted"
|
|
3626
3582
|
};
|
|
3627
|
-
const managedRow = this._readFiber(row.id);
|
|
3628
3583
|
this._emit("fiber:recovery:detected", {
|
|
3629
3584
|
...this._fiberRecoveryPayload(ctx, managedRow),
|
|
3630
3585
|
elapsedMs: Date.now() - row.created_at
|
|
@@ -3637,12 +3592,6 @@ var Agent = class Agent extends DurableObject {
|
|
|
3637
3592
|
elapsedMs: Date.now() - row.created_at
|
|
3638
3593
|
});
|
|
3639
3594
|
if (managedRow) {
|
|
3640
|
-
if (this._isTerminalFiberStatus(managedRow.status)) {
|
|
3641
|
-
this.sql`DELETE FROM cf_agents_runs WHERE id = ${row.id}`;
|
|
3642
|
-
madeProgress = true;
|
|
3643
|
-
this._notifyManagedFiberTerminal(row.id);
|
|
3644
|
-
continue;
|
|
3645
|
-
}
|
|
3646
3595
|
const completedAt = Date.now();
|
|
3647
3596
|
this.sql`
|
|
3648
3597
|
UPDATE cf_agents_fibers
|
|
@@ -3780,8 +3729,9 @@ var Agent = class Agent extends DurableObject {
|
|
|
3780
3729
|
* cleanup as `parent.deleteSubAgent(Cls, name)` from the parent.
|
|
3781
3730
|
* @internal
|
|
3782
3731
|
*/
|
|
3783
|
-
_cf_destroyDescendantFacet(targetPath) {
|
|
3784
|
-
|
|
3732
|
+
async _cf_destroyDescendantFacet(targetPath) {
|
|
3733
|
+
await this.__unsafe_ensureInitialized();
|
|
3734
|
+
await this._dynamicAgents.destroyDescendant(targetPath);
|
|
3785
3735
|
}
|
|
3786
3736
|
/**
|
|
3787
3737
|
* Whether any runFiber recovery work is still outstanding: orphaned
|
|
@@ -3819,6 +3769,15 @@ var Agent = class Agent extends DurableObject {
|
|
|
3819
3769
|
*/
|
|
3820
3770
|
async _syncHostJobs() {
|
|
3821
3771
|
if (this._destroyed) return;
|
|
3772
|
+
if (this._isFacet) {
|
|
3773
|
+
const work = this.lifecycle.jobs;
|
|
3774
|
+
for (const id of [
|
|
3775
|
+
HOST_JOB_DESTROY_ID,
|
|
3776
|
+
HOST_JOB_KEEP_ALIVE_ID,
|
|
3777
|
+
HOST_JOB_HOUSEKEEPING_ID
|
|
3778
|
+
]) if (work.get(id)) await work.cancel(id);
|
|
3779
|
+
return;
|
|
3780
|
+
}
|
|
3822
3781
|
await this._withAgentSpan("schedule_agent_alarm", "alarm", {}, async () => {
|
|
3823
3782
|
const work = this.lifecycle.jobs;
|
|
3824
3783
|
const nowMs = Date.now();
|
|
@@ -3950,9 +3909,10 @@ var Agent = class Agent extends DurableObject {
|
|
|
3950
3909
|
className: match.childClass,
|
|
3951
3910
|
name: match.childName
|
|
3952
3911
|
});
|
|
3953
|
-
|
|
3912
|
+
const isWebSocketUpgrade = request.headers.get("Upgrade")?.toLowerCase() === "websocket";
|
|
3913
|
+
if (decision instanceof Response) return isWebSocketUpgrade && !decision.webSocket ? rejectSubAgentWebSocket(decision) : decision;
|
|
3954
3914
|
const forwardReq = decision instanceof Request ? decision : request;
|
|
3955
|
-
if (
|
|
3915
|
+
if (isWebSocketUpgrade) {
|
|
3956
3916
|
const acceptHeaders = new Headers(forwardReq.headers);
|
|
3957
3917
|
const routedUrl = new URL(forwardReq.url);
|
|
3958
3918
|
routedUrl.pathname = new URL(request.url).pathname;
|
|
@@ -3989,18 +3949,27 @@ var Agent = class Agent extends DurableObject {
|
|
|
3989
3949
|
}
|
|
3990
3950
|
}
|
|
3991
3951
|
async _cf_broadcastToSubAgent(ownerPath, message, without) {
|
|
3952
|
+
await this.__unsafe_ensureInitialized();
|
|
3992
3953
|
await this._dynamicAgents.broadcastToPath(ownerPath, message, without);
|
|
3993
3954
|
}
|
|
3994
|
-
_cf_subAgentConnectionMetas(ownerPath) {
|
|
3955
|
+
async _cf_subAgentConnectionMetas(ownerPath) {
|
|
3956
|
+
await this.__unsafe_ensureInitialized();
|
|
3995
3957
|
return this._dynamicAgents.connectionMetas(ownerPath);
|
|
3996
3958
|
}
|
|
3997
|
-
_cf_sendToSubAgentConnection(connectionId, message) {
|
|
3998
|
-
|
|
3959
|
+
async _cf_sendToSubAgentConnection(connectionId, message) {
|
|
3960
|
+
await this.__unsafe_ensureInitialized();
|
|
3961
|
+
await this._dynamicAgents.sendToConnection(connectionId, message);
|
|
3962
|
+
}
|
|
3963
|
+
async _cf_closeSubAgentConnection(connectionId, code, reason) {
|
|
3964
|
+
await this.__unsafe_ensureInitialized();
|
|
3965
|
+
await this._dynamicAgents.closeConnection(connectionId, code, reason);
|
|
3999
3966
|
}
|
|
4000
|
-
|
|
4001
|
-
|
|
3967
|
+
async _cf_closeSubAgentConnectionsForPrefix(prefix, code, reason) {
|
|
3968
|
+
await this.__unsafe_ensureInitialized();
|
|
3969
|
+
this._dynamicAgents.closeConnectionsForPrefix(prefix, code, reason);
|
|
4002
3970
|
}
|
|
4003
|
-
_cf_setSubAgentConnectionState(connectionId, state) {
|
|
3971
|
+
async _cf_setSubAgentConnectionState(connectionId, state) {
|
|
3972
|
+
await this.__unsafe_ensureInitialized();
|
|
4004
3973
|
return this._dynamicAgents.setConnectionState(connectionId, state);
|
|
4005
3974
|
}
|
|
4006
3975
|
_cf_connectionTargetsSubAgent(connection) {
|
|
@@ -4316,6 +4285,9 @@ var Agent = class Agent extends DurableObject {
|
|
|
4316
4285
|
const runId = options.runId ?? nanoid(12);
|
|
4317
4286
|
const agentType = cls.name;
|
|
4318
4287
|
const detached = this._parseDetachedOption(options.detached);
|
|
4288
|
+
const eventDelivery = options.eventDelivery ?? "full";
|
|
4289
|
+
if (eventDelivery !== "full" && eventDelivery !== "terminal") throw new Error(`runAgentTool: eventDelivery must be "full" or "terminal", got ${JSON.stringify(eventDelivery)}.`);
|
|
4290
|
+
if (detached && eventDelivery === "terminal") throw new Error("runAgentTool: eventDelivery \"terminal\" is not supported for detached runs.");
|
|
4319
4291
|
const existing = this._readAgentToolRun(runId);
|
|
4320
4292
|
if (existing) {
|
|
4321
4293
|
if (detached) {
|
|
@@ -4406,13 +4378,14 @@ var Agent = class Agent extends DurableObject {
|
|
|
4406
4378
|
input_redacted, status, display_metadata, display_order, started_at,
|
|
4407
4379
|
detached, detached_on_finish, detached_notify_source,
|
|
4408
4380
|
detached_max_budget_at, detached_no_progress_budget_ms,
|
|
4409
|
-
detached_on_milestones
|
|
4381
|
+
detached_on_milestones, event_delivery
|
|
4410
4382
|
) VALUES (
|
|
4411
4383
|
${runId}, ${options.parentToolCallId ?? null}, ${agentType},
|
|
4412
4384
|
${inputPreviewJson}, 1, 'starting', ${displayJson}, ${displayOrder},
|
|
4413
4385
|
${startedAt}, ${detached ? 1 : 0}, ${detached?.onFinishName ?? null},
|
|
4414
4386
|
${detached?.notifySource ?? null}, ${detachedMaxBudgetAt},
|
|
4415
|
-
${detachedNoProgressBudgetMs}, ${detachedOnMilestonesJson}
|
|
4387
|
+
${detachedNoProgressBudgetMs}, ${detachedOnMilestonesJson},
|
|
4388
|
+
${eventDelivery === "terminal" ? "terminal" : null}
|
|
4416
4389
|
)
|
|
4417
4390
|
`;
|
|
4418
4391
|
const runInfo = {
|
|
@@ -4437,7 +4410,10 @@ var Agent = class Agent extends DurableObject {
|
|
|
4437
4410
|
});
|
|
4438
4411
|
const child = await this.subAgent(cls, runId);
|
|
4439
4412
|
const adapter = this._asAgentToolChildAdapter(child);
|
|
4440
|
-
const childStart = await adapter.startAgentToolRun(options.input, {
|
|
4413
|
+
const childStart = await adapter.startAgentToolRun(options.input, {
|
|
4414
|
+
runId,
|
|
4415
|
+
...eventDelivery === "terminal" ? { eventDelivery } : {}
|
|
4416
|
+
});
|
|
4441
4417
|
this._markAgentToolRunning(runId);
|
|
4442
4418
|
if (detached) {
|
|
4443
4419
|
if (options.signal) console.warn(`[agents] runAgentTool: \`signal\` is ignored for a detached run (${runId}); a detached child must outlive the spawning turn. Use cancelAgentTool(runId) to cancel it.`);
|
|
@@ -5048,29 +5024,79 @@ var Agent = class Agent extends DurableObject {
|
|
|
5048
5024
|
else this.broadcast(body);
|
|
5049
5025
|
}
|
|
5050
5026
|
_broadcastAgentToolChunks(parentToolCallId, runId, chunks, sequence, replay, connection) {
|
|
5027
|
+
const terminalOnly = this._isTerminalOnlyAgentToolRun(runId);
|
|
5051
5028
|
let next = sequence;
|
|
5052
|
-
for (const chunk of chunks)
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5029
|
+
for (const chunk of chunks) {
|
|
5030
|
+
const chunkSequence = next++;
|
|
5031
|
+
if (terminalOnly && !isAgentToolLifecycleChunk(chunk.body)) continue;
|
|
5032
|
+
this._broadcastAgentToolEvent(parentToolCallId, chunkSequence, {
|
|
5033
|
+
kind: "chunk",
|
|
5034
|
+
runId,
|
|
5035
|
+
body: chunk.body
|
|
5036
|
+
}, replay, connection);
|
|
5037
|
+
}
|
|
5057
5038
|
return next;
|
|
5058
5039
|
}
|
|
5059
|
-
|
|
5060
|
-
|
|
5040
|
+
_isTerminalOnlyAgentToolRun(runId) {
|
|
5041
|
+
return this.sql`
|
|
5042
|
+
SELECT event_delivery FROM cf_agent_tool_runs WHERE run_id = ${runId}
|
|
5043
|
+
`[0]?.event_delivery === "terminal";
|
|
5044
|
+
}
|
|
5045
|
+
async _broadcastAgentToolStoredChunks(row, sequence, replay, connection, timeoutMs) {
|
|
5046
|
+
const deadline = this._agentToolRecoveryDeadline(timeoutMs);
|
|
5047
|
+
const resolving = this._cf_resolveSubAgent(row.agent_type, row.run_id);
|
|
5048
|
+
const child = deadline === void 0 ? await resolving : await this._settleWithinRecoveryTimeout(resolving, deadline());
|
|
5049
|
+
if (child === void 0) return sequence;
|
|
5061
5050
|
const adapter = this._asAgentToolChildAdapter(child);
|
|
5062
|
-
return this._broadcastAgentToolStoredChunksFromAdapter(adapter, row, sequence, replay, connection);
|
|
5051
|
+
return this._broadcastAgentToolStoredChunksFromAdapter(adapter, row, sequence, replay, connection, deadline?.());
|
|
5052
|
+
}
|
|
5053
|
+
/**
|
|
5054
|
+
* One budget shared by every step of a bounded recovery read, so a run's
|
|
5055
|
+
* total time stays within `timeoutMs`. Returns the remaining milliseconds,
|
|
5056
|
+
* never below 1 because a non-positive timeout means "unbounded".
|
|
5057
|
+
*/
|
|
5058
|
+
_agentToolRecoveryDeadline(timeoutMs) {
|
|
5059
|
+
if (timeoutMs === void 0 || timeoutMs <= 0) return void 0;
|
|
5060
|
+
const deadline = Date.now() + timeoutMs;
|
|
5061
|
+
return () => Math.max(1, deadline - Date.now());
|
|
5063
5062
|
}
|
|
5064
5063
|
async _broadcastAgentToolStoredChunksFromAdapter(adapter, row, sequence, replay, connection, timeoutMs) {
|
|
5065
|
-
const
|
|
5066
|
-
|
|
5067
|
-
|
|
5064
|
+
const deadline = this._agentToolRecoveryDeadline(timeoutMs);
|
|
5065
|
+
const chunks = await this._getAgentToolChunksForRecovery(adapter, row.run_id, deadline?.());
|
|
5066
|
+
const next = chunks ? this._broadcastAgentToolChunks(row.parent_tool_call_id ?? void 0, row.run_id, chunks, sequence, replay, connection) : sequence;
|
|
5067
|
+
return this._broadcastAgentToolMilestones(adapter, row, next, replay, connection, deadline?.());
|
|
5068
|
+
}
|
|
5069
|
+
/**
|
|
5070
|
+
* Milestones are persisted on the child run rather than in its chunk log, so
|
|
5071
|
+
* replay re-emits them from the child's inspection. The client dedupes them
|
|
5072
|
+
* on the milestone's own sequence, so a milestone already seen live is a no-op.
|
|
5073
|
+
* They carry the next stored-chunk sequence without consuming it, matching
|
|
5074
|
+
* the live path.
|
|
5075
|
+
*/
|
|
5076
|
+
async _broadcastAgentToolMilestones(adapter, row, sequence, replay, connection, timeoutMs) {
|
|
5077
|
+
const milestones = (await this._settleWithinRecoveryTimeout(adapter.inspectAgentToolRun(row.run_id, { reconcile: false }), timeoutMs))?.milestones ?? [];
|
|
5078
|
+
for (const milestone of milestones) this._broadcastAgentToolEvent(row.parent_tool_call_id ?? void 0, sequence, {
|
|
5079
|
+
kind: "chunk",
|
|
5080
|
+
runId: row.run_id,
|
|
5081
|
+
body: JSON.stringify({
|
|
5082
|
+
type: AGENT_TOOL_MILESTONE_PART,
|
|
5083
|
+
data: {
|
|
5084
|
+
name: milestone.name,
|
|
5085
|
+
sequence: milestone.sequence,
|
|
5086
|
+
at: milestone.at,
|
|
5087
|
+
...milestone.data !== void 0 ? { data: milestone.data } : {}
|
|
5088
|
+
}
|
|
5089
|
+
})
|
|
5090
|
+
}, replay, connection);
|
|
5091
|
+
return sequence;
|
|
5068
5092
|
}
|
|
5069
5093
|
async _forwardAgentToolStream(stream, parentToolCallId, runId, sequence, signal, idleTimeoutMs) {
|
|
5070
5094
|
let next = sequence;
|
|
5095
|
+
let forwarded = false;
|
|
5071
5096
|
if (signal?.aborted) return {
|
|
5072
5097
|
next,
|
|
5073
|
-
ended: "aborted"
|
|
5098
|
+
ended: "aborted",
|
|
5099
|
+
forwarded
|
|
5074
5100
|
};
|
|
5075
5101
|
let ended = "done";
|
|
5076
5102
|
const reader = stream.getReader();
|
|
@@ -5098,12 +5124,18 @@ var Agent = class Agent extends DurableObject {
|
|
|
5098
5124
|
idleTimer = setTimeout(() => resolveIdle?.(), idleTimeoutMs);
|
|
5099
5125
|
};
|
|
5100
5126
|
let forwardedSinceProgress = false;
|
|
5127
|
+
const terminalOnly = this._isTerminalOnlyAgentToolRun(runId);
|
|
5101
5128
|
try {
|
|
5102
5129
|
const forwardChunk = (chunk) => {
|
|
5103
|
-
|
|
5130
|
+
const lifecycle = isAgentToolLifecycleChunk(chunk.body);
|
|
5131
|
+
const unstoredId = typeof chunk.unstoredId === "string" ? chunk.unstoredId : void 0;
|
|
5132
|
+
const chunkSequence = lifecycle || unstoredId !== void 0 ? next : next++;
|
|
5133
|
+
forwarded = true;
|
|
5134
|
+
if (!terminalOnly || lifecycle) this._broadcastAgentToolEvent(parentToolCallId, chunkSequence, {
|
|
5104
5135
|
kind: "chunk",
|
|
5105
5136
|
runId,
|
|
5106
|
-
body: chunk.body
|
|
5137
|
+
body: chunk.body,
|
|
5138
|
+
...unstoredId !== void 0 ? { unstoredId } : {}
|
|
5107
5139
|
});
|
|
5108
5140
|
this._observeForwardedProgress(runId, chunk.body);
|
|
5109
5141
|
forwardedSinceProgress = true;
|
|
@@ -5171,7 +5203,8 @@ var Agent = class Agent extends DurableObject {
|
|
|
5171
5203
|
}
|
|
5172
5204
|
return {
|
|
5173
5205
|
next,
|
|
5174
|
-
ended
|
|
5206
|
+
ended,
|
|
5207
|
+
forwarded
|
|
5175
5208
|
};
|
|
5176
5209
|
}
|
|
5177
5210
|
/**
|
|
@@ -5372,9 +5405,9 @@ var Agent = class Agent extends DurableObject {
|
|
|
5372
5405
|
* dies and recovers again during deploy churn is still collected. A genuinely
|
|
5373
5406
|
* silent/hung child can never block recovery forever: it seals `interrupted`
|
|
5374
5407
|
* after one `noProgressTimeoutMs` window. `maxWindowMs` is an OPTIONAL hard
|
|
5375
|
-
* wall-clock ceiling (default `Infinity` — uncapped,
|
|
5376
|
-
*
|
|
5377
|
-
*
|
|
5408
|
+
* wall-clock ceiling (default `Infinity` — uncapped, #1672); set it finite
|
|
5409
|
+
* to also bound a child that keeps progressing, which seals
|
|
5410
|
+
* `window-exceeded` and tears the child down.
|
|
5378
5411
|
*
|
|
5379
5412
|
* Returns the terminal `result` (and `completedAt`) when the child reaches a
|
|
5380
5413
|
* terminal status, plus the advanced broadcast `sequence`. Returns
|
|
@@ -5419,18 +5452,22 @@ var Agent = class Agent extends DurableObject {
|
|
|
5419
5452
|
try {
|
|
5420
5453
|
while (!ceilingController.signal.aborted) {
|
|
5421
5454
|
let afterSequence = -1;
|
|
5455
|
+
let storedCount = 0;
|
|
5422
5456
|
try {
|
|
5423
5457
|
const existing = await adapter.getAgentToolChunks(row.run_id);
|
|
5424
5458
|
const last = existing[existing.length - 1];
|
|
5425
5459
|
if (last) afterSequence = last.sequence;
|
|
5460
|
+
storedCount = existing.length;
|
|
5426
5461
|
} catch {}
|
|
5427
|
-
|
|
5462
|
+
nextSequence = storedCount + 1;
|
|
5428
5463
|
let streamEnded = "idle";
|
|
5464
|
+
let forwardedAny = false;
|
|
5429
5465
|
try {
|
|
5430
5466
|
const stream = await adapter.tailAgentToolRun(row.run_id, { afterSequence });
|
|
5431
5467
|
const forwarded = await this._forwardAgentToolStream(stream, row.parent_tool_call_id ?? void 0, row.run_id, nextSequence, ceilingController.signal, noProgressTimeoutMs);
|
|
5432
5468
|
nextSequence = forwarded.next;
|
|
5433
5469
|
streamEnded = forwarded.ended;
|
|
5470
|
+
forwardedAny = forwarded.forwarded;
|
|
5434
5471
|
} catch {}
|
|
5435
5472
|
const terminal = await collectTerminal(nextSequence);
|
|
5436
5473
|
if (terminal) return terminal;
|
|
@@ -5439,7 +5476,7 @@ var Agent = class Agent extends DurableObject {
|
|
|
5439
5476
|
break;
|
|
5440
5477
|
}
|
|
5441
5478
|
if (streamEnded !== "done") break;
|
|
5442
|
-
if (
|
|
5479
|
+
if (!forwardedAny) break;
|
|
5443
5480
|
}
|
|
5444
5481
|
} finally {
|
|
5445
5482
|
if (ceilingTimer !== void 0) clearTimeout(ceilingTimer);
|
|
@@ -5469,7 +5506,7 @@ var Agent = class Agent extends DurableObject {
|
|
|
5469
5506
|
display: this._parseAgentToolJson(row.display_metadata)
|
|
5470
5507
|
}, true, connection);
|
|
5471
5508
|
try {
|
|
5472
|
-
sequence = await this._broadcastAgentToolStoredChunks(row, sequence, true, connection);
|
|
5509
|
+
sequence = await this._broadcastAgentToolStoredChunks(row, sequence, true, connection, DEFAULT_AGENT_TOOL_RECOVERY_TIMEOUT_MS);
|
|
5473
5510
|
} catch {}
|
|
5474
5511
|
if (this._isAgentToolTerminal(row.status)) this._broadcastAgentToolTerminal(parentToolCallId, sequence, {
|
|
5475
5512
|
runId: row.run_id,
|
|
@@ -5656,13 +5693,17 @@ var Agent = class Agent extends DurableObject {
|
|
|
5656
5693
|
`.map((row) => row.run_id);
|
|
5657
5694
|
}
|
|
5658
5695
|
async _getAgentToolChunksForRecovery(adapter, runId, timeoutMs) {
|
|
5659
|
-
|
|
5660
|
-
|
|
5696
|
+
return this._settleWithinRecoveryTimeout(adapter.getAgentToolChunks(runId), timeoutMs);
|
|
5697
|
+
}
|
|
5698
|
+
/** Resolve to `undefined` when `promise` rejects or outlasts `timeoutMs`. */
|
|
5699
|
+
async _settleWithinRecoveryTimeout(promise, timeoutMs) {
|
|
5700
|
+
const settled = promise.catch(() => void 0);
|
|
5701
|
+
if (timeoutMs === void 0 || timeoutMs <= 0) return settled;
|
|
5661
5702
|
let timeoutId;
|
|
5662
5703
|
const timeout = new Promise((resolve) => {
|
|
5663
5704
|
timeoutId = setTimeout(() => resolve(void 0), timeoutMs);
|
|
5664
5705
|
});
|
|
5665
|
-
const result = await Promise.race([
|
|
5706
|
+
const result = await Promise.race([settled, timeout]);
|
|
5666
5707
|
if (timeoutId !== void 0) clearTimeout(timeoutId);
|
|
5667
5708
|
return result;
|
|
5668
5709
|
}
|
|
@@ -6960,4 +7001,4 @@ var StreamingResponse = class {
|
|
|
6960
7001
|
//#endregion
|
|
6961
7002
|
export { routeAgentEmail as a, getCurrentAgent as i, DEFAULT_AGENT_STATIC_OPTIONS as n, StreamingResponse as r, Agent as t };
|
|
6962
7003
|
|
|
6963
|
-
//# sourceMappingURL=src-
|
|
7004
|
+
//# sourceMappingURL=src-BZeu-SCD.js.map
|