agents 0.11.4 → 0.11.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -1
- package/dist/browser/ai.js +1 -1
- package/dist/browser/index.js +1 -1
- package/dist/browser/tanstack-ai.js +1 -1
- package/dist/browser/tanstack-ai.js.map +1 -1
- package/dist/chat/index.d.ts +169 -23
- package/dist/chat/index.js +232 -1
- package/dist/chat/index.js.map +1 -1
- package/dist/{classPrivateFieldGet2-DAZNVUKb.js → classPrivateFieldGet2-Bqby-AHD.js} +5 -5
- package/dist/{client-B_xdiZbn.js → client-D1kFXo80.js} +9 -1
- package/dist/{client-B_xdiZbn.js.map → client-D1kFXo80.js.map} +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js +2 -2
- package/dist/client.js.map +1 -1
- package/dist/compaction-helpers-C_cN3z55.js.map +1 -1
- package/dist/email.js.map +1 -1
- package/dist/experimental/memory/session/index.js.map +1 -1
- package/dist/{index-D9qo_Inc.d.ts → index-BM7Nk0QD.d.ts} +378 -21
- package/dist/index.d.ts +12 -2
- package/dist/index.js +329 -30
- package/dist/index.js.map +1 -1
- package/dist/mcp/client.d.ts +1 -1
- package/dist/mcp/client.js +1 -1
- package/dist/mcp/do-oauth-client-provider.js.map +1 -1
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +1 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/x402.js.map +1 -1
- package/dist/react.d.ts +53 -5
- package/dist/react.js +47 -7
- package/dist/react.js.map +1 -1
- package/dist/{retries-JlwH9mnV.d.ts → retries-fLD8cGNf.d.ts} +1 -1
- package/dist/retries.d.ts +1 -1
- package/dist/{shared-BovR6hRc.js → shared-mfBbxjS1.js} +3 -3
- package/dist/{shared-BovR6hRc.js.map → shared-mfBbxjS1.js.map} +1 -1
- package/dist/sub-routing.d.ts +14 -0
- package/dist/sub-routing.js +171 -0
- package/dist/sub-routing.js.map +1 -0
- package/dist/utils.d.ts +21 -1
- package/dist/utils.js +36 -1
- package/dist/utils.js.map +1 -1
- package/dist/workflows.d.ts +1 -1
- package/dist/workflows.js.map +1 -1
- package/package.json +6 -19
package/dist/index.js
CHANGED
|
@@ -2,9 +2,10 @@ import { MessageType } from "./types.js";
|
|
|
2
2
|
import { camelCaseToKebabCase } from "./utils.js";
|
|
3
3
|
import { createHeaderBasedEmailResolver, signAgentHeaders } from "./email.js";
|
|
4
4
|
import { __DO_NOT_USE_WILL_BREAK__agentContext } from "./internal_context.js";
|
|
5
|
-
import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-
|
|
5
|
+
import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-Bqby-AHD.js";
|
|
6
|
+
import { SUB_PREFIX, getSubAgentByName, parseSubAgentPath, routeSubAgentRequest } from "./sub-routing.js";
|
|
6
7
|
import { isErrorRetryable, tryN, validateRetryOptions } from "./retries.js";
|
|
7
|
-
import { o as RPC_DO_PREFIX, r as MCPConnectionState, s as DisposableStore, t as MCPClientManager } from "./client-
|
|
8
|
+
import { o as RPC_DO_PREFIX, r as MCPConnectionState, s as DisposableStore, t as MCPClientManager } from "./client-D1kFXo80.js";
|
|
8
9
|
import { DurableObjectOAuthClientProvider } from "./mcp/do-oauth-client-provider.js";
|
|
9
10
|
import { genericObservability } from "./observability/index.js";
|
|
10
11
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -79,6 +80,14 @@ const STATE_ROW_ID = "cf_state_row_id";
|
|
|
79
80
|
const STATE_WAS_CHANGED = "cf_state_was_changed";
|
|
80
81
|
const DEFAULT_STATE = {};
|
|
81
82
|
/**
|
|
83
|
+
* Validate that a stored `parentPath` has the expected shape. Used
|
|
84
|
+
* when restoring from DO storage to guard against corrupted data.
|
|
85
|
+
*/
|
|
86
|
+
function isValidParentPath(value) {
|
|
87
|
+
if (!Array.isArray(value)) return false;
|
|
88
|
+
return value.every((entry) => entry != null && typeof entry === "object" && typeof entry.className === "string" && typeof entry.name === "string");
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
82
91
|
* Internal key used to store the readonly flag in connection state.
|
|
83
92
|
* Prefixed with _cf_ to avoid collision with user state keys.
|
|
84
93
|
*/
|
|
@@ -148,10 +157,22 @@ const _sendIdentityWarnedClasses = /* @__PURE__ */ new WeakSet();
|
|
|
148
157
|
* Child classes can override specific options without spreading.
|
|
149
158
|
*/
|
|
150
159
|
const DEFAULT_AGENT_STATIC_OPTIONS = {
|
|
160
|
+
/** Whether the Agent should hibernate when inactive */
|
|
151
161
|
hibernate: true,
|
|
162
|
+
/** Whether to send identity (name, agent) to clients on connect */
|
|
152
163
|
sendIdentityOnConnect: true,
|
|
164
|
+
/**
|
|
165
|
+
* Timeout in seconds before a running interval schedule is considered "hung"
|
|
166
|
+
* and force-reset. Increase this if you have callbacks that legitimately
|
|
167
|
+
* take longer than 30 seconds.
|
|
168
|
+
*/
|
|
153
169
|
hungScheduleTimeoutSeconds: 30,
|
|
170
|
+
/**
|
|
171
|
+
* Interval in milliseconds for keepAlive() alarm heartbeats.
|
|
172
|
+
* Lower values mean faster recovery after eviction but more frequent alarms.
|
|
173
|
+
*/
|
|
154
174
|
keepAliveIntervalMs: DEFAULT_KEEP_ALIVE_INTERVAL_MS,
|
|
175
|
+
/** Default retry options for schedule(), queue(), and this.retry() */
|
|
155
176
|
retry: {
|
|
156
177
|
maxAttempts: 3,
|
|
157
178
|
baseDelayMs: 100,
|
|
@@ -459,6 +480,7 @@ var Agent = class Agent extends Server {
|
|
|
459
480
|
this._rawStateAccessors = /* @__PURE__ */ new WeakMap();
|
|
460
481
|
this._persistenceHookMode = "none";
|
|
461
482
|
this._isFacet = false;
|
|
483
|
+
this._parentPath = [];
|
|
462
484
|
this._insideOnStart = false;
|
|
463
485
|
this._warnedScheduleInOnStart = /* @__PURE__ */ new Set();
|
|
464
486
|
this._keepAliveRefs = 0;
|
|
@@ -468,6 +490,7 @@ var Agent = class Agent extends Server {
|
|
|
468
490
|
this.initialState = DEFAULT_STATE;
|
|
469
491
|
this.observability = genericObservability;
|
|
470
492
|
this._flushingQueue = false;
|
|
493
|
+
this._subAgentRegistryReady = false;
|
|
471
494
|
if (!wrappedClasses.has(this.constructor)) {
|
|
472
495
|
this._autoWrapCustomMethods();
|
|
473
496
|
wrappedClasses.add(this.constructor);
|
|
@@ -621,7 +644,7 @@ var Agent = class Agent extends Server {
|
|
|
621
644
|
if (this.shouldSendProtocolMessages(connection, ctx)) {
|
|
622
645
|
if (this._resolvedOptions.sendIdentityOnConnect) {
|
|
623
646
|
const ctor = this.constructor;
|
|
624
|
-
if (ctor.options?.sendIdentityOnConnect === void 0 && !_sendIdentityWarnedClasses.has(ctor)) {
|
|
647
|
+
if (ctor.options?.sendIdentityOnConnect === void 0 && !_sendIdentityWarnedClasses.has(ctor) && !this._isFacet) {
|
|
625
648
|
if (!new URL(ctx.request.url).pathname.includes(this.name)) {
|
|
626
649
|
_sendIdentityWarnedClasses.add(ctor);
|
|
627
650
|
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.`);
|
|
@@ -671,6 +694,8 @@ var Agent = class Agent extends Server {
|
|
|
671
694
|
email: void 0
|
|
672
695
|
}, async () => {
|
|
673
696
|
if (await this.ctx.storage.get("cf_agents_is_facet")) this._isFacet = true;
|
|
697
|
+
const storedParentPath = await this.ctx.storage.get("cf_agents_parent_path");
|
|
698
|
+
if (isValidParentPath(storedParentPath)) this._parentPath = storedParentPath;
|
|
674
699
|
await this._tryCatch(async () => {
|
|
675
700
|
await this.mcp.restoreConnectionsFromStorage(this.name);
|
|
676
701
|
await this._restoreRpcMcpServers();
|
|
@@ -718,23 +743,10 @@ var Agent = class Agent extends Server {
|
|
|
718
743
|
* @param excludeIds Additional connection IDs to exclude (e.g. the source)
|
|
719
744
|
*/
|
|
720
745
|
_broadcastProtocol(msg, excludeIds = []) {
|
|
721
|
-
if (this._isFacet) return;
|
|
722
746
|
const exclude = [...excludeIds];
|
|
723
747
|
for (const conn of this.getConnections()) if (!this.isConnectionProtocolEnabled(conn)) exclude.push(conn.id);
|
|
724
748
|
this.broadcast(msg, exclude);
|
|
725
749
|
}
|
|
726
|
-
/**
|
|
727
|
-
* When running as a facet, the parent DO owns the WebSocket registry
|
|
728
|
-
* (`ctx.getWebSockets()`). Iterating from the child isolate throws
|
|
729
|
-
* "Cannot perform I/O on behalf of a different Durable Object".
|
|
730
|
-
* Downstream callers (e.g. chat-streaming paths) invoke
|
|
731
|
-
* `this.broadcast()` directly, bypassing `_broadcastProtocol`'s
|
|
732
|
-
* guard, so override at the base to catch every path.
|
|
733
|
-
*/
|
|
734
|
-
broadcast(msg, without) {
|
|
735
|
-
if (this._isFacet) return;
|
|
736
|
-
super.broadcast(msg, without);
|
|
737
|
-
}
|
|
738
750
|
_setStateInternal(nextState, source = "server") {
|
|
739
751
|
this.validateStateChange(nextState, source);
|
|
740
752
|
this._state = nextState;
|
|
@@ -1690,6 +1702,12 @@ var Agent = class Agent extends Server {
|
|
|
1690
1702
|
* alarm system, without creating schedule rows or emitting observability
|
|
1691
1703
|
* events. Configure via `static options = { keepAliveIntervalMs: 5000 }`.
|
|
1692
1704
|
*
|
|
1705
|
+
* No-op on facets. Facets share the parent's isolate and don't
|
|
1706
|
+
* need a separate alarm heartbeat — the parent's own activity,
|
|
1707
|
+
* any open WebSocket to the facet, and any in-flight Promise
|
|
1708
|
+
* already keep the shared machine alive for the duration of
|
|
1709
|
+
* real work.
|
|
1710
|
+
*
|
|
1693
1711
|
* @example
|
|
1694
1712
|
* ```ts
|
|
1695
1713
|
* const dispose = await this.keepAlive();
|
|
@@ -1701,7 +1719,7 @@ var Agent = class Agent extends Server {
|
|
|
1701
1719
|
* ```
|
|
1702
1720
|
*/
|
|
1703
1721
|
async keepAlive() {
|
|
1704
|
-
if (this._isFacet)
|
|
1722
|
+
if (this._isFacet) return () => {};
|
|
1705
1723
|
this._keepAliveRefs++;
|
|
1706
1724
|
if (this._keepAliveRefs === 1) await this._scheduleNextAlarm();
|
|
1707
1725
|
let disposed = false;
|
|
@@ -1888,7 +1906,10 @@ var Agent = class Agent extends Server {
|
|
|
1888
1906
|
* Executes any scheduled tasks that are due.
|
|
1889
1907
|
*
|
|
1890
1908
|
* Calls super.alarm() first to ensure PartyServer's #ensureInitialized()
|
|
1891
|
-
* runs, which
|
|
1909
|
+
* runs, which resolves this.name from ctx.id.name (including for
|
|
1910
|
+
* facets, which are spawned with an explicit id so they have their
|
|
1911
|
+
* own ctx.id.name; pre-2026-03-15 alarms fall back to the legacy
|
|
1912
|
+
* __ps_name storage record) and calls onStart() if needed.
|
|
1892
1913
|
*
|
|
1893
1914
|
* @remarks
|
|
1894
1915
|
* To schedule a task, please use the `this.schedule` method instead.
|
|
@@ -2001,6 +2022,108 @@ var Agent = class Agent extends Server {
|
|
|
2001
2022
|
await this._scheduleNextAlarm();
|
|
2002
2023
|
}
|
|
2003
2024
|
/**
|
|
2025
|
+
* Intercept incoming HTTP/WS requests whose URL contains a
|
|
2026
|
+
* `/sub/{child-class}/{child-name}` marker and forward them to
|
|
2027
|
+
* the facet. The `onBeforeSubAgent` hook fires first (authorize,
|
|
2028
|
+
* mutate, or short-circuit). If the hook doesn't return a
|
|
2029
|
+
* Response, the framework resolves the facet and hands the
|
|
2030
|
+
* request off.
|
|
2031
|
+
*
|
|
2032
|
+
* After a WebSocket upgrade completes, subsequent frames route
|
|
2033
|
+
* directly to the child — the parent is only on the path for the
|
|
2034
|
+
* initial request.
|
|
2035
|
+
*
|
|
2036
|
+
* @experimental The API surface may change before stabilizing.
|
|
2037
|
+
*/
|
|
2038
|
+
async fetch(request) {
|
|
2039
|
+
const ctx = this.ctx;
|
|
2040
|
+
const match = parseSubAgentPath(request.url, { knownClasses: ctx.exports ? Object.keys(ctx.exports) : void 0 });
|
|
2041
|
+
if (!match) return super.fetch(request);
|
|
2042
|
+
const decision = await this.onBeforeSubAgent(request, {
|
|
2043
|
+
className: match.childClass,
|
|
2044
|
+
name: match.childName
|
|
2045
|
+
});
|
|
2046
|
+
if (decision instanceof Response) return decision;
|
|
2047
|
+
const forwardReq = decision instanceof Request ? decision : request;
|
|
2048
|
+
return this._cf_forwardToFacet(forwardReq, match);
|
|
2049
|
+
}
|
|
2050
|
+
/**
|
|
2051
|
+
* Parent-side middleware hook. Fires before a request is
|
|
2052
|
+
* forwarded into a facet sub-agent. Mirrors `onBeforeConnect` /
|
|
2053
|
+
* `onBeforeRequest`.
|
|
2054
|
+
*
|
|
2055
|
+
* - return `void` (default) → forward the original request
|
|
2056
|
+
* - return `Request` → forward this (modified) request
|
|
2057
|
+
* - return `Response` → return this response to the
|
|
2058
|
+
* client; do not wake the child
|
|
2059
|
+
*
|
|
2060
|
+
* Default implementation: return void (permissive).
|
|
2061
|
+
*
|
|
2062
|
+
* The hook receives the **original** request with its URL intact —
|
|
2063
|
+
* including the `/sub/{class}/{name}` segment. The routing
|
|
2064
|
+
* decision for which facet to wake is fixed at parse time, so if
|
|
2065
|
+
* you return a modified `Request`, its headers, body, method, and
|
|
2066
|
+
* query string flow through to the child, but the **pathname**
|
|
2067
|
+
* the child sees is always the tail after `/sub/{class}/{name}`.
|
|
2068
|
+
* Customize via headers/body rather than URL-rewriting.
|
|
2069
|
+
*
|
|
2070
|
+
* WebSocket upgrade requests flow through this hook the same way as
|
|
2071
|
+
* plain HTTP. If you return a mutated `Request`, make sure it still
|
|
2072
|
+
* carries the original `Upgrade: websocket` and `Sec-WebSocket-*`
|
|
2073
|
+
* headers — the simplest safe recipe is to clone the incoming
|
|
2074
|
+
* request's headers (via `new Headers(req.headers)`) and only add
|
|
2075
|
+
* or replace entries, rather than constructing a fresh `Headers`
|
|
2076
|
+
* object from scratch.
|
|
2077
|
+
*
|
|
2078
|
+
* @experimental The API surface may change before stabilizing.
|
|
2079
|
+
*
|
|
2080
|
+
* @example
|
|
2081
|
+
* ```ts
|
|
2082
|
+
* class Inbox extends Agent {
|
|
2083
|
+
* override async onBeforeSubAgent(req, { className, name }) {
|
|
2084
|
+
* // Strict registry gate
|
|
2085
|
+
* if (!this.hasSubAgent(className, name)) {
|
|
2086
|
+
* return new Response("Not found", { status: 404 });
|
|
2087
|
+
* }
|
|
2088
|
+
* }
|
|
2089
|
+
* }
|
|
2090
|
+
* ```
|
|
2091
|
+
*/
|
|
2092
|
+
async onBeforeSubAgent(_request, _child) {}
|
|
2093
|
+
/**
|
|
2094
|
+
* Resolve the facet Fetcher for the match and forward the
|
|
2095
|
+
* request to it with `/sub/{class}/{name}` stripped.
|
|
2096
|
+
*
|
|
2097
|
+
* @internal
|
|
2098
|
+
*/
|
|
2099
|
+
async _cf_forwardToFacet(req, match) {
|
|
2100
|
+
let fetcher;
|
|
2101
|
+
try {
|
|
2102
|
+
fetcher = await this._cf_resolveSubAgent(match.childClass, match.childName);
|
|
2103
|
+
} catch (err) {
|
|
2104
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2105
|
+
console.error("[agents] sub-agent route failed:", message);
|
|
2106
|
+
if (/null character/i.test(message) || /reserved/i.test(message)) return new Response("Bad Request", { status: 400 });
|
|
2107
|
+
return new Response("Not Found", { status: 404 });
|
|
2108
|
+
}
|
|
2109
|
+
const rewritten = new URL(req.url);
|
|
2110
|
+
rewritten.pathname = match.remainingPath;
|
|
2111
|
+
const forwarded = new Request(rewritten, req);
|
|
2112
|
+
return fetcher.fetch(forwarded);
|
|
2113
|
+
}
|
|
2114
|
+
/**
|
|
2115
|
+
* Bridge method used by `getSubAgentByName`. Resolves the facet
|
|
2116
|
+
* on each call (idempotent via `subAgent`) and dispatches one
|
|
2117
|
+
* RPC method. Stateless — no cached references.
|
|
2118
|
+
*
|
|
2119
|
+
* @internal
|
|
2120
|
+
*/
|
|
2121
|
+
async _cf_invokeSubAgent(className, name, method, args) {
|
|
2122
|
+
const handle = await this._cf_resolveSubAgent(className, name);
|
|
2123
|
+
if (typeof handle[method] !== "function") throw new Error(`Method "${method}" not found on ${className}.`);
|
|
2124
|
+
return await handle[method](...args);
|
|
2125
|
+
}
|
|
2126
|
+
/**
|
|
2004
2127
|
* Initialize this agent as a facet in a single RPC.
|
|
2005
2128
|
*
|
|
2006
2129
|
* Runs entirely inside the child's isolate, so every storage write
|
|
@@ -2010,19 +2133,111 @@ var Agent = class Agent extends Server {
|
|
|
2010
2133
|
* parent and triggered "Cannot perform I/O on behalf of a different
|
|
2011
2134
|
* Durable Object" on the child.
|
|
2012
2135
|
*
|
|
2013
|
-
*
|
|
2014
|
-
*
|
|
2015
|
-
*
|
|
2016
|
-
*
|
|
2136
|
+
* We set `_isFacet` eagerly (before `__unsafe_ensureInitialized`
|
|
2137
|
+
* runs `onStart()`) so any code that legitimately branches on it
|
|
2138
|
+
* — e.g. skipping parent-owned alarms in schedule guards — sees
|
|
2139
|
+
* the flag during the first `onStart()` run. Broadcast paths no
|
|
2140
|
+
* longer special-case facets, since facets can be directly
|
|
2141
|
+
* addressed via sub-agent routing and have their own WebSocket
|
|
2142
|
+
* connections.
|
|
2143
|
+
*
|
|
2144
|
+
* The facet's name (and `this.name` getter) is handled entirely by
|
|
2145
|
+
* partyserver via `ctx.id.name`, which is populated because the
|
|
2146
|
+
* parent passed an explicit `id: parentNs.idFromName(name)` to
|
|
2147
|
+
* `ctx.facets.get()` — see {@link _cf_resolveSubAgent}. No
|
|
2148
|
+
* `setName()` call or `__ps_name` storage write is needed; the
|
|
2149
|
+
* facet's name survives cold wake automatically because the
|
|
2150
|
+
* factory re-runs and `idFromName` is deterministic.
|
|
2017
2151
|
*
|
|
2018
2152
|
* @internal Called by {@link subAgent}.
|
|
2019
2153
|
*/
|
|
2020
|
-
async _cf_initAsFacet(name) {
|
|
2154
|
+
async _cf_initAsFacet(name, parentPath = []) {
|
|
2155
|
+
if (this.name !== name) throw new Error(`Facet bootstrap mismatch: expected this.name === "${name}" but got "${this.name}". This usually means the parent passed the wrong (or no) id to ctx.facets.get(). See _cf_resolveSubAgent.`);
|
|
2021
2156
|
this._isFacet = true;
|
|
2022
|
-
|
|
2157
|
+
this._parentPath = parentPath;
|
|
2158
|
+
await Promise.all([this.ctx.storage.put("cf_agents_is_facet", true), this.ctx.storage.put("cf_agents_parent_path", parentPath)]);
|
|
2023
2159
|
await this.__unsafe_ensureInitialized();
|
|
2024
2160
|
}
|
|
2025
2161
|
/**
|
|
2162
|
+
* Ancestor chain for this agent, root-first. Empty for top-level
|
|
2163
|
+
* DOs. Populated at facet init time; survives hibernation.
|
|
2164
|
+
*
|
|
2165
|
+
* @example
|
|
2166
|
+
* ```ts
|
|
2167
|
+
* class Chat extends Agent {
|
|
2168
|
+
* onStart() {
|
|
2169
|
+
* console.log("chat started under:", this.parentPath);
|
|
2170
|
+
* // → [{ className: "Tenant", name: "acme" }, { className: "Inbox", name: "alice" }]
|
|
2171
|
+
* }
|
|
2172
|
+
* }
|
|
2173
|
+
* ```
|
|
2174
|
+
*
|
|
2175
|
+
* @experimental The API surface may change before stabilizing.
|
|
2176
|
+
*/
|
|
2177
|
+
get parentPath() {
|
|
2178
|
+
return this._parentPath;
|
|
2179
|
+
}
|
|
2180
|
+
/**
|
|
2181
|
+
* Ancestor chain + self, root-first. Convenient for logging.
|
|
2182
|
+
*
|
|
2183
|
+
* @experimental The API surface may change before stabilizing.
|
|
2184
|
+
*/
|
|
2185
|
+
get selfPath() {
|
|
2186
|
+
return [...this._parentPath, {
|
|
2187
|
+
className: this.constructor.name,
|
|
2188
|
+
name: this.name
|
|
2189
|
+
}];
|
|
2190
|
+
}
|
|
2191
|
+
/**
|
|
2192
|
+
* Resolve a typed RPC stub for this facet's **immediate** parent
|
|
2193
|
+
* agent.
|
|
2194
|
+
*
|
|
2195
|
+
* Symmetric with `subAgent(Cls, name)`: while `subAgent` opens a
|
|
2196
|
+
* stub from parent to child, `parentAgent` opens one from child
|
|
2197
|
+
* to parent. Pass the direct parent's class reference — the
|
|
2198
|
+
* framework verifies it matches the last entry of
|
|
2199
|
+
* `this.parentPath` at runtime, then looks up `env[Cls.name]` to
|
|
2200
|
+
* find the namespace binding.
|
|
2201
|
+
*
|
|
2202
|
+
* `this.parentPath` is root-first, so the direct parent is the
|
|
2203
|
+
* **last** entry: `this.parentPath.at(-1)`. For grandparents and
|
|
2204
|
+
* further ancestors, iterate `this.parentPath` and use
|
|
2205
|
+
* `getAgentByName(env.X, this.parentPath[i].name)` directly.
|
|
2206
|
+
*
|
|
2207
|
+
* Assumes the standard "binding name matches class name" convention.
|
|
2208
|
+
* If your `wrangler.jsonc` binds the parent under a different name
|
|
2209
|
+
* (e.g. `{ class_name: "Inbox", name: "MY_INBOX" }`), call
|
|
2210
|
+
* `getAgentByName(env.MY_INBOX, this.parentPath.at(-1)!.name)`
|
|
2211
|
+
* directly instead.
|
|
2212
|
+
*
|
|
2213
|
+
* @experimental The API surface may change before stabilizing.
|
|
2214
|
+
*
|
|
2215
|
+
* @throws If this agent is not a facet (no parent).
|
|
2216
|
+
* @throws If `Cls.name` doesn't match the recorded direct-parent
|
|
2217
|
+
* class (guards against accidentally reaching the wrong
|
|
2218
|
+
* DO, especially in nested Root → Mid → Leaf chains).
|
|
2219
|
+
* @throws If no env binding named `Cls.name` is found.
|
|
2220
|
+
*
|
|
2221
|
+
* @example
|
|
2222
|
+
* ```ts
|
|
2223
|
+
* class Chat extends AIChatAgent<Env> {
|
|
2224
|
+
* async onChatMessage(...) {
|
|
2225
|
+
* const inbox = await this.parentAgent(Inbox);
|
|
2226
|
+
* const memory = await inbox.getSharedMemory("facts");
|
|
2227
|
+
* // ...
|
|
2228
|
+
* }
|
|
2229
|
+
* }
|
|
2230
|
+
* ```
|
|
2231
|
+
*/
|
|
2232
|
+
async parentAgent(cls) {
|
|
2233
|
+
const parent = this._parentPath[this._parentPath.length - 1];
|
|
2234
|
+
if (!parent) throw new Error(`parentAgent(): ${this.constructor.name} is not a facet — only sub-agents (spawned via \`subAgent()\`) have a parent.`);
|
|
2235
|
+
if (cls.name !== parent.className) throw new Error(`parentAgent(${cls.name}): this facet's recorded parent class is "${parent.className}", not "${cls.name}". Pass the class whose constructor actually spawned this facet.`);
|
|
2236
|
+
const binding = this.env[cls.name];
|
|
2237
|
+
if (!binding) throw new Error(`parentAgent(${cls.name}): no top-level binding "${cls.name}" found in env. If the parent is bound under a different name (e.g. "MY_${cls.name.toUpperCase()}"), use \`getAgentByName(env.MY_${cls.name.toUpperCase()}, this.parentPath.at(-1)!.name)\` directly.`);
|
|
2238
|
+
return await getServerByName(binding, parent.name);
|
|
2239
|
+
}
|
|
2240
|
+
/**
|
|
2026
2241
|
* Get or create a named sub-agent — a child Durable Object (facet)
|
|
2027
2242
|
* with its own isolated SQLite storage running on the same machine.
|
|
2028
2243
|
*
|
|
@@ -2043,12 +2258,39 @@ var Agent = class Agent extends Server {
|
|
|
2043
2258
|
* ```
|
|
2044
2259
|
*/
|
|
2045
2260
|
async subAgent(cls, name) {
|
|
2261
|
+
return await this._cf_resolveSubAgent(cls.name, name);
|
|
2262
|
+
}
|
|
2263
|
+
/**
|
|
2264
|
+
* Shared facet resolution — takes a CamelCase class name string
|
|
2265
|
+
* (matching `ctx.exports`) rather than a class reference. Both
|
|
2266
|
+
* `subAgent(cls, name)` and `_cf_invokeSubAgent(className, ...)`
|
|
2267
|
+
* funnel through here so registry bookkeeping and the
|
|
2268
|
+
* `_cf_initAsFacet` handshake are consistent.
|
|
2269
|
+
*
|
|
2270
|
+
* @internal
|
|
2271
|
+
*/
|
|
2272
|
+
async _cf_resolveSubAgent(className, name) {
|
|
2046
2273
|
const ctx = this.ctx;
|
|
2047
2274
|
if (!ctx.facets || !ctx.exports) throw new Error("subAgent() is not supported in this runtime — `ctx.facets` / `ctx.exports` are unavailable. Update to the latest `compatibility_date` in your wrangler.jsonc.");
|
|
2048
|
-
if (
|
|
2049
|
-
const
|
|
2050
|
-
|
|
2051
|
-
|
|
2275
|
+
if (camelCaseToKebabCase(className) === "sub") throw new Error(`Sub-agent class name "${className}" kebab-cases to "sub", which collides with the reserved URL separator — rename the class (e.g. "SubThing" or "Subtask").`);
|
|
2276
|
+
const Cls = ctx.exports[className];
|
|
2277
|
+
if (!Cls) throw new Error(`Sub-agent class "${className}" not found in worker exports. Make sure the class is exported from your worker entry point and that the export name matches the class name.`);
|
|
2278
|
+
if (name.includes("\0")) throw new Error(`Sub-agent name contains null character (\\0), which is reserved.`);
|
|
2279
|
+
const facetKey = `${className}\0${name}`;
|
|
2280
|
+
const parentClassName = this.constructor.name;
|
|
2281
|
+
const parentNs = ctx.exports[parentClassName];
|
|
2282
|
+
if (!parentNs?.idFromName) {
|
|
2283
|
+
const minificationHint = /^_*[a-z][a-z0-9]{0,2}$/.test(parentClassName) ? ` The class name "${parentClassName}" looks minified — make sure your bundler preserves class names (e.g. esbuild's \`keepNames: true\`).` : "";
|
|
2284
|
+
throw new Error(`Sub-agent bootstrap requires the parent class "${parentClassName}" to be bound as a Durable Object namespace, but ctx.exports["${parentClassName}"] is missing or doesn't expose idFromName.${minificationHint} Make sure the parent agent class is registered in your wrangler.jsonc durable_objects.bindings under its class name.`);
|
|
2285
|
+
}
|
|
2286
|
+
const facetId = parentNs.idFromName(name);
|
|
2287
|
+
const stub = ctx.facets.get(facetKey, () => ({
|
|
2288
|
+
class: Cls,
|
|
2289
|
+
id: facetId
|
|
2290
|
+
}));
|
|
2291
|
+
const childParentPath = this.selfPath;
|
|
2292
|
+
await stub._cf_initAsFacet(name, childParentPath);
|
|
2293
|
+
this._recordSubAgent(className, name);
|
|
2052
2294
|
return stub;
|
|
2053
2295
|
}
|
|
2054
2296
|
/**
|
|
@@ -2082,7 +2324,63 @@ var Agent = class Agent extends Server {
|
|
|
2082
2324
|
const ctx = this.ctx;
|
|
2083
2325
|
if (!ctx.facets) throw new Error("deleteSubAgent() is not supported in this runtime — `ctx.facets` is unavailable. Update to the latest `compatibility_date` in your wrangler.jsonc.");
|
|
2084
2326
|
const facetKey = `${cls.name}\0${name}`;
|
|
2085
|
-
|
|
2327
|
+
try {
|
|
2328
|
+
ctx.facets.delete(facetKey);
|
|
2329
|
+
} catch {}
|
|
2330
|
+
this._forgetSubAgent(cls.name, name);
|
|
2331
|
+
}
|
|
2332
|
+
/** @internal */
|
|
2333
|
+
_ensureSubAgentRegistry() {
|
|
2334
|
+
if (this._subAgentRegistryReady) return;
|
|
2335
|
+
this.sql`
|
|
2336
|
+
CREATE TABLE IF NOT EXISTS cf_agents_sub_agents (
|
|
2337
|
+
class TEXT NOT NULL,
|
|
2338
|
+
name TEXT NOT NULL,
|
|
2339
|
+
created_at INTEGER NOT NULL,
|
|
2340
|
+
PRIMARY KEY (class, name)
|
|
2341
|
+
)
|
|
2342
|
+
`;
|
|
2343
|
+
this._subAgentRegistryReady = true;
|
|
2344
|
+
}
|
|
2345
|
+
/** @internal */
|
|
2346
|
+
_recordSubAgent(className, name) {
|
|
2347
|
+
this._ensureSubAgentRegistry();
|
|
2348
|
+
this.sql`
|
|
2349
|
+
INSERT OR IGNORE INTO cf_agents_sub_agents (class, name, created_at)
|
|
2350
|
+
VALUES (${className}, ${name}, ${Date.now()})
|
|
2351
|
+
`;
|
|
2352
|
+
}
|
|
2353
|
+
/** @internal */
|
|
2354
|
+
_forgetSubAgent(className, name) {
|
|
2355
|
+
this._ensureSubAgentRegistry();
|
|
2356
|
+
this.sql`
|
|
2357
|
+
DELETE FROM cf_agents_sub_agents
|
|
2358
|
+
WHERE class = ${className} AND name = ${name}
|
|
2359
|
+
`;
|
|
2360
|
+
}
|
|
2361
|
+
hasSubAgent(classOrName, name) {
|
|
2362
|
+
const className = typeof classOrName === "string" ? classOrName : classOrName.name;
|
|
2363
|
+
this._ensureSubAgentRegistry();
|
|
2364
|
+
return (this.sql`
|
|
2365
|
+
SELECT COUNT(*) AS n FROM cf_agents_sub_agents
|
|
2366
|
+
WHERE class = ${className} AND name = ${name}
|
|
2367
|
+
`[0]?.n ?? 0) > 0;
|
|
2368
|
+
}
|
|
2369
|
+
listSubAgents(classOrName) {
|
|
2370
|
+
const className = typeof classOrName === "string" ? classOrName : classOrName?.name;
|
|
2371
|
+
this._ensureSubAgentRegistry();
|
|
2372
|
+
return (className ? this.sql`
|
|
2373
|
+
SELECT class, name, created_at FROM cf_agents_sub_agents
|
|
2374
|
+
WHERE class = ${className}
|
|
2375
|
+
ORDER BY created_at ASC
|
|
2376
|
+
` : this.sql`
|
|
2377
|
+
SELECT class, name, created_at FROM cf_agents_sub_agents
|
|
2378
|
+
ORDER BY created_at ASC
|
|
2379
|
+
`).map((r) => ({
|
|
2380
|
+
className: r.class,
|
|
2381
|
+
name: r.name,
|
|
2382
|
+
createdAt: r.created_at
|
|
2383
|
+
}));
|
|
2086
2384
|
}
|
|
2087
2385
|
/**
|
|
2088
2386
|
* Destroy the Agent, removing all state and scheduled tasks
|
|
@@ -2093,6 +2391,7 @@ var Agent = class Agent extends Server {
|
|
|
2093
2391
|
this.sql`DROP TABLE IF EXISTS cf_agents_schedules`;
|
|
2094
2392
|
this.sql`DROP TABLE IF EXISTS cf_agents_queues`;
|
|
2095
2393
|
this.sql`DROP TABLE IF EXISTS cf_agents_workflows`;
|
|
2394
|
+
this.sql`DROP TABLE IF EXISTS cf_agents_sub_agents`;
|
|
2096
2395
|
if (!this._isFacet) await this.ctx.storage.deleteAlarm();
|
|
2097
2396
|
await this.ctx.storage.deleteAll();
|
|
2098
2397
|
this._disposables.dispose();
|
|
@@ -3267,6 +3566,6 @@ var StreamingResponse = class {
|
|
|
3267
3566
|
}
|
|
3268
3567
|
};
|
|
3269
3568
|
//#endregion
|
|
3270
|
-
export { Agent, DEFAULT_AGENT_STATIC_OPTIONS, DurableObjectOAuthClientProvider, MessageType, SqlError, StreamingResponse, __DO_NOT_USE_WILL_BREAK__agentContext, callable, createHeaderBasedEmailResolver, getAgentByName, getCurrentAgent, routeAgentEmail, routeAgentRequest, unstable_callable };
|
|
3569
|
+
export { Agent, DEFAULT_AGENT_STATIC_OPTIONS, DurableObjectOAuthClientProvider, MessageType, SUB_PREFIX, SqlError, StreamingResponse, __DO_NOT_USE_WILL_BREAK__agentContext, callable, createHeaderBasedEmailResolver, getAgentByName, getCurrentAgent, getSubAgentByName, parseSubAgentPath, routeAgentEmail, routeAgentRequest, routeSubAgentRequest, unstable_callable };
|
|
3271
3570
|
|
|
3272
3571
|
//# sourceMappingURL=index.js.map
|