agents 0.11.3 → 0.11.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +44 -1
  2. package/dist/browser/ai.js +1 -1
  3. package/dist/browser/index.js +1 -1
  4. package/dist/browser/tanstack-ai.js +1 -1
  5. package/dist/chat/index.d.ts +73 -1
  6. package/dist/{classPrivateFieldGet2-BVdP0e3Z.js → classPrivateFieldGet2-DAZNVUKb.js} +5 -5
  7. package/dist/{client-PEDsNnfY.js → client-BYF13FDD.js} +6 -3
  8. package/dist/{client-PEDsNnfY.js.map → client-BYF13FDD.js.map} +1 -1
  9. package/dist/client.d.ts +1 -1
  10. package/dist/client.js +9 -9
  11. package/dist/client.js.map +1 -1
  12. package/dist/experimental/memory/session/index.js +3 -3
  13. package/dist/experimental/memory/session/index.js.map +1 -1
  14. package/dist/experimental/webmcp.d.ts +212 -0
  15. package/dist/experimental/webmcp.js +298 -0
  16. package/dist/experimental/webmcp.js.map +1 -0
  17. package/dist/{index-D49HdAiY.d.ts → index-DabjI66m.d.ts} +365 -20
  18. package/dist/index.d.ts +12 -2
  19. package/dist/index.js +311 -43
  20. package/dist/index.js.map +1 -1
  21. package/dist/mcp/client.d.ts +1 -1
  22. package/dist/mcp/client.js +1 -1
  23. package/dist/mcp/index.d.ts +1 -1
  24. package/dist/mcp/index.js +5 -5
  25. package/dist/mcp/index.js.map +1 -1
  26. package/dist/react.d.ts +53 -5
  27. package/dist/react.js +55 -15
  28. package/dist/react.js.map +1 -1
  29. package/dist/{retries-JlwH9mnV.d.ts → retries-fLD8cGNf.d.ts} +1 -1
  30. package/dist/retries.d.ts +1 -1
  31. package/dist/{shared-BtPEbm_U.js → shared-BovR6hRc.js} +3 -3
  32. package/dist/{shared-BtPEbm_U.js.map → shared-BovR6hRc.js.map} +1 -1
  33. package/dist/sub-routing.d.ts +14 -0
  34. package/dist/sub-routing.js +171 -0
  35. package/dist/sub-routing.js.map +1 -0
  36. package/dist/utils.d.ts +21 -1
  37. package/dist/utils.js +36 -1
  38. package/dist/utils.js.map +1 -1
  39. package/dist/workflows.d.ts +1 -1
  40. package/package.json +13 -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-BVdP0e3Z.js";
5
+ import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-DAZNVUKb.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-PEDsNnfY.js";
8
+ import { o as RPC_DO_PREFIX, r as MCPConnectionState, s as DisposableStore, t as MCPClientManager } from "./client-BYF13FDD.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";
@@ -19,13 +20,13 @@ let _Symbol$dispose;
19
20
  * Type guard for RPC request messages
20
21
  */
21
22
  function isRPCRequest(msg) {
22
- return typeof msg === "object" && msg !== null && "type" in msg && msg.type === MessageType.RPC && "id" in msg && typeof msg.id === "string" && "method" in msg && typeof msg.method === "string" && "args" in msg && Array.isArray(msg.args);
23
+ 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);
23
24
  }
24
25
  /**
25
26
  * Type guard for state update messages
26
27
  */
27
28
  function isStateUpdateMessage(msg) {
28
- return typeof msg === "object" && msg !== null && "type" in msg && msg.type === MessageType.CF_AGENT_STATE && "state" in msg;
29
+ return typeof msg === "object" && msg !== null && "type" in msg && msg.type === "cf_agent_state" && "state" in msg;
29
30
  }
30
31
  const callableMetadata = /* @__PURE__ */ new WeakMap();
31
32
  /**
@@ -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
  */
@@ -459,6 +468,7 @@ var Agent = class Agent extends Server {
459
468
  this._rawStateAccessors = /* @__PURE__ */ new WeakMap();
460
469
  this._persistenceHookMode = "none";
461
470
  this._isFacet = false;
471
+ this._parentPath = [];
462
472
  this._insideOnStart = false;
463
473
  this._warnedScheduleInOnStart = /* @__PURE__ */ new Set();
464
474
  this._keepAliveRefs = 0;
@@ -468,6 +478,7 @@ var Agent = class Agent extends Server {
468
478
  this.initialState = DEFAULT_STATE;
469
479
  this.observability = genericObservability;
470
480
  this._flushingQueue = false;
481
+ this._subAgentRegistryReady = false;
471
482
  if (!wrappedClasses.has(this.constructor)) {
472
483
  this._autoWrapCustomMethods();
473
484
  wrappedClasses.add(this.constructor);
@@ -535,7 +546,7 @@ var Agent = class Agent extends Server {
535
546
  if (isStateUpdateMessage(parsed)) {
536
547
  if (this.isConnectionReadonly(connection)) {
537
548
  connection.send(JSON.stringify({
538
- type: MessageType.CF_AGENT_STATE_ERROR,
549
+ type: "cf_agent_state_error",
539
550
  error: "Connection is readonly"
540
551
  }));
541
552
  return;
@@ -545,7 +556,7 @@ var Agent = class Agent extends Server {
545
556
  } catch (e) {
546
557
  console.error("[Agent] State update rejected:", e);
547
558
  connection.send(JSON.stringify({
548
- type: MessageType.CF_AGENT_STATE_ERROR,
559
+ type: "cf_agent_state_error",
549
560
  error: "State update rejected"
550
561
  }));
551
562
  }
@@ -586,7 +597,7 @@ var Agent = class Agent extends Server {
586
597
  id,
587
598
  result,
588
599
  success: true,
589
- type: MessageType.RPC
600
+ type: "rpc"
590
601
  };
591
602
  connection.send(JSON.stringify(response));
592
603
  } catch (e) {
@@ -594,7 +605,7 @@ var Agent = class Agent extends Server {
594
605
  error: e instanceof Error ? e.message : "Unknown error occurred",
595
606
  id: parsed.id,
596
607
  success: false,
597
- type: MessageType.RPC
608
+ type: "rpc"
598
609
  };
599
610
  connection.send(JSON.stringify(response));
600
611
  console.error("RPC error:", e);
@@ -621,7 +632,7 @@ var Agent = class Agent extends Server {
621
632
  if (this.shouldSendProtocolMessages(connection, ctx)) {
622
633
  if (this._resolvedOptions.sendIdentityOnConnect) {
623
634
  const ctor = this.constructor;
624
- if (ctor.options?.sendIdentityOnConnect === void 0 && !_sendIdentityWarnedClasses.has(ctor)) {
635
+ if (ctor.options?.sendIdentityOnConnect === void 0 && !_sendIdentityWarnedClasses.has(ctor) && !this._isFacet) {
625
636
  if (!new URL(ctx.request.url).pathname.includes(this.name)) {
626
637
  _sendIdentityWarnedClasses.add(ctor);
627
638
  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.`);
@@ -630,16 +641,16 @@ var Agent = class Agent extends Server {
630
641
  connection.send(JSON.stringify({
631
642
  name: this.name,
632
643
  agent: camelCaseToKebabCase(this._ParentClass.name),
633
- type: MessageType.CF_AGENT_IDENTITY
644
+ type: "cf_agent_identity"
634
645
  }));
635
646
  }
636
647
  if (this.state) connection.send(JSON.stringify({
637
648
  state: this.state,
638
- type: MessageType.CF_AGENT_STATE
649
+ type: "cf_agent_state"
639
650
  }));
640
651
  connection.send(JSON.stringify({
641
652
  mcp: this.getMcpServers(),
642
- type: MessageType.CF_AGENT_MCP_SERVERS
653
+ type: "cf_agent_mcp_servers"
643
654
  }));
644
655
  } else this._setConnectionNoProtocol(connection);
645
656
  this._emit("connect", { connectionId: connection.id });
@@ -671,6 +682,8 @@ var Agent = class Agent extends Server {
671
682
  email: void 0
672
683
  }, async () => {
673
684
  if (await this.ctx.storage.get("cf_agents_is_facet")) this._isFacet = true;
685
+ const storedParentPath = await this.ctx.storage.get("cf_agents_parent_path");
686
+ if (isValidParentPath(storedParentPath)) this._parentPath = storedParentPath;
674
687
  await this._tryCatch(async () => {
675
688
  await this.mcp.restoreConnectionsFromStorage(this.name);
676
689
  await this._restoreRpcMcpServers();
@@ -718,23 +731,10 @@ var Agent = class Agent extends Server {
718
731
  * @param excludeIds Additional connection IDs to exclude (e.g. the source)
719
732
  */
720
733
  _broadcastProtocol(msg, excludeIds = []) {
721
- if (this._isFacet) return;
722
734
  const exclude = [...excludeIds];
723
735
  for (const conn of this.getConnections()) if (!this.isConnectionProtocolEnabled(conn)) exclude.push(conn.id);
724
736
  this.broadcast(msg, exclude);
725
737
  }
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
738
  _setStateInternal(nextState, source = "server") {
739
739
  this.validateStateChange(nextState, source);
740
740
  this._state = nextState;
@@ -744,7 +744,7 @@ var Agent = class Agent extends Server {
744
744
  `;
745
745
  this._broadcastProtocol(JSON.stringify({
746
746
  state: nextState,
747
- type: MessageType.CF_AGENT_STATE
747
+ type: "cf_agent_state"
748
748
  }), source !== "server" ? [source.id] : []);
749
749
  const { connection, request, email } = __DO_NOT_USE_WILL_BREAK__agentContext.getStore() || {};
750
750
  this.ctx.waitUntil((async () => {
@@ -1690,6 +1690,12 @@ var Agent = class Agent extends Server {
1690
1690
  * alarm system, without creating schedule rows or emitting observability
1691
1691
  * events. Configure via `static options = { keepAliveIntervalMs: 5000 }`.
1692
1692
  *
1693
+ * No-op on facets. Facets share the parent's isolate and don't
1694
+ * need a separate alarm heartbeat — the parent's own activity,
1695
+ * any open WebSocket to the facet, and any in-flight Promise
1696
+ * already keep the shared machine alive for the duration of
1697
+ * real work.
1698
+ *
1693
1699
  * @example
1694
1700
  * ```ts
1695
1701
  * const dispose = await this.keepAlive();
@@ -1701,7 +1707,7 @@ var Agent = class Agent extends Server {
1701
1707
  * ```
1702
1708
  */
1703
1709
  async keepAlive() {
1704
- if (this._isFacet) throw new Error("keepAlive() is not supported in sub-agents. Use keepAlive() from the parent agent instead.");
1710
+ if (this._isFacet) return () => {};
1705
1711
  this._keepAliveRefs++;
1706
1712
  if (this._keepAliveRefs === 1) await this._scheduleNextAlarm();
1707
1713
  let disposed = false;
@@ -2001,6 +2007,108 @@ var Agent = class Agent extends Server {
2001
2007
  await this._scheduleNextAlarm();
2002
2008
  }
2003
2009
  /**
2010
+ * Intercept incoming HTTP/WS requests whose URL contains a
2011
+ * `/sub/{child-class}/{child-name}` marker and forward them to
2012
+ * the facet. The `onBeforeSubAgent` hook fires first (authorize,
2013
+ * mutate, or short-circuit). If the hook doesn't return a
2014
+ * Response, the framework resolves the facet and hands the
2015
+ * request off.
2016
+ *
2017
+ * After a WebSocket upgrade completes, subsequent frames route
2018
+ * directly to the child — the parent is only on the path for the
2019
+ * initial request.
2020
+ *
2021
+ * @experimental The API surface may change before stabilizing.
2022
+ */
2023
+ async fetch(request) {
2024
+ const ctx = this.ctx;
2025
+ const match = parseSubAgentPath(request.url, { knownClasses: ctx.exports ? Object.keys(ctx.exports) : void 0 });
2026
+ if (!match) return super.fetch(request);
2027
+ const decision = await this.onBeforeSubAgent(request, {
2028
+ className: match.childClass,
2029
+ name: match.childName
2030
+ });
2031
+ if (decision instanceof Response) return decision;
2032
+ const forwardReq = decision instanceof Request ? decision : request;
2033
+ return this._cf_forwardToFacet(forwardReq, match);
2034
+ }
2035
+ /**
2036
+ * Parent-side middleware hook. Fires before a request is
2037
+ * forwarded into a facet sub-agent. Mirrors `onBeforeConnect` /
2038
+ * `onBeforeRequest`.
2039
+ *
2040
+ * - return `void` (default) → forward the original request
2041
+ * - return `Request` → forward this (modified) request
2042
+ * - return `Response` → return this response to the
2043
+ * client; do not wake the child
2044
+ *
2045
+ * Default implementation: return void (permissive).
2046
+ *
2047
+ * The hook receives the **original** request with its URL intact —
2048
+ * including the `/sub/{class}/{name}` segment. The routing
2049
+ * decision for which facet to wake is fixed at parse time, so if
2050
+ * you return a modified `Request`, its headers, body, method, and
2051
+ * query string flow through to the child, but the **pathname**
2052
+ * the child sees is always the tail after `/sub/{class}/{name}`.
2053
+ * Customize via headers/body rather than URL-rewriting.
2054
+ *
2055
+ * WebSocket upgrade requests flow through this hook the same way as
2056
+ * plain HTTP. If you return a mutated `Request`, make sure it still
2057
+ * carries the original `Upgrade: websocket` and `Sec-WebSocket-*`
2058
+ * headers — the simplest safe recipe is to clone the incoming
2059
+ * request's headers (via `new Headers(req.headers)`) and only add
2060
+ * or replace entries, rather than constructing a fresh `Headers`
2061
+ * object from scratch.
2062
+ *
2063
+ * @experimental The API surface may change before stabilizing.
2064
+ *
2065
+ * @example
2066
+ * ```ts
2067
+ * class Inbox extends Agent {
2068
+ * override async onBeforeSubAgent(req, { className, name }) {
2069
+ * // Strict registry gate
2070
+ * if (!this.hasSubAgent(className, name)) {
2071
+ * return new Response("Not found", { status: 404 });
2072
+ * }
2073
+ * }
2074
+ * }
2075
+ * ```
2076
+ */
2077
+ async onBeforeSubAgent(_request, _child) {}
2078
+ /**
2079
+ * Resolve the facet Fetcher for the match and forward the
2080
+ * request to it with `/sub/{class}/{name}` stripped.
2081
+ *
2082
+ * @internal
2083
+ */
2084
+ async _cf_forwardToFacet(req, match) {
2085
+ let fetcher;
2086
+ try {
2087
+ fetcher = await this._cf_resolveSubAgent(match.childClass, match.childName);
2088
+ } catch (err) {
2089
+ const message = err instanceof Error ? err.message : String(err);
2090
+ console.error("[agents] sub-agent route failed:", message);
2091
+ if (/null character/i.test(message) || /reserved/i.test(message)) return new Response("Bad Request", { status: 400 });
2092
+ return new Response("Not Found", { status: 404 });
2093
+ }
2094
+ const rewritten = new URL(req.url);
2095
+ rewritten.pathname = match.remainingPath;
2096
+ const forwarded = new Request(rewritten, req);
2097
+ return fetcher.fetch(forwarded);
2098
+ }
2099
+ /**
2100
+ * Bridge method used by `getSubAgentByName`. Resolves the facet
2101
+ * on each call (idempotent via `subAgent`) and dispatches one
2102
+ * RPC method. Stateless — no cached references.
2103
+ *
2104
+ * @internal
2105
+ */
2106
+ async _cf_invokeSubAgent(className, name, method, args) {
2107
+ const handle = await this._cf_resolveSubAgent(className, name);
2108
+ if (typeof handle[method] !== "function") throw new Error(`Method "${method}" not found on ${className}.`);
2109
+ return await handle[method](...args);
2110
+ }
2111
+ /**
2004
2112
  * Initialize this agent as a facet in a single RPC.
2005
2113
  *
2006
2114
  * Runs entirely inside the child's isolate, so every storage write
@@ -2010,19 +2118,105 @@ var Agent = class Agent extends Server {
2010
2118
  * parent and triggered "Cannot perform I/O on behalf of a different
2011
2119
  * Durable Object" on the child.
2012
2120
  *
2013
- * Order matters: set `_isFacet` BEFORE triggering initialization, so
2014
- * the first `onStart()` run (which calls `broadcastMcpServers`) sees
2015
- * the flag and skips broadcasts that would touch the parent DO's
2016
- * WebSocket registry.
2121
+ * We still set `_isFacet` eagerly (before `__unsafe_ensureInitialized`)
2122
+ * so any code that legitimately branches on it — e.g. skipping
2123
+ * parent-owned alarms in schedule guards sees the flag during
2124
+ * the first `onStart()` run. Broadcast paths no longer special-case
2125
+ * facets, since facets can be directly addressed via sub-agent
2126
+ * routing and have their own WebSocket connections.
2017
2127
  *
2018
2128
  * @internal Called by {@link subAgent}.
2019
2129
  */
2020
- async _cf_initAsFacet(name) {
2130
+ async _cf_initAsFacet(name, parentPath = []) {
2021
2131
  this._isFacet = true;
2022
- await Promise.all([this.ctx.storage.put("cf_agents_is_facet", true), this.ctx.storage.put("__ps_name", name)]);
2132
+ this._parentPath = parentPath;
2133
+ await Promise.all([
2134
+ this.ctx.storage.put("cf_agents_is_facet", true),
2135
+ this.ctx.storage.put("__ps_name", name),
2136
+ this.ctx.storage.put("cf_agents_parent_path", parentPath)
2137
+ ]);
2023
2138
  await this.__unsafe_ensureInitialized();
2024
2139
  }
2025
2140
  /**
2141
+ * Ancestor chain for this agent, root-first. Empty for top-level
2142
+ * DOs. Populated at facet init time; survives hibernation.
2143
+ *
2144
+ * @example
2145
+ * ```ts
2146
+ * class Chat extends Agent {
2147
+ * onStart() {
2148
+ * console.log("chat started under:", this.parentPath);
2149
+ * // → [{ className: "Tenant", name: "acme" }, { className: "Inbox", name: "alice" }]
2150
+ * }
2151
+ * }
2152
+ * ```
2153
+ *
2154
+ * @experimental The API surface may change before stabilizing.
2155
+ */
2156
+ get parentPath() {
2157
+ return this._parentPath;
2158
+ }
2159
+ /**
2160
+ * Ancestor chain + self, root-first. Convenient for logging.
2161
+ *
2162
+ * @experimental The API surface may change before stabilizing.
2163
+ */
2164
+ get selfPath() {
2165
+ return [...this._parentPath, {
2166
+ className: this.constructor.name,
2167
+ name: this.name
2168
+ }];
2169
+ }
2170
+ /**
2171
+ * Resolve a typed RPC stub for this facet's **immediate** parent
2172
+ * agent.
2173
+ *
2174
+ * Symmetric with `subAgent(Cls, name)`: while `subAgent` opens a
2175
+ * stub from parent to child, `parentAgent` opens one from child
2176
+ * to parent. Pass the direct parent's class reference — the
2177
+ * framework verifies it matches the last entry of
2178
+ * `this.parentPath` at runtime, then looks up `env[Cls.name]` to
2179
+ * find the namespace binding.
2180
+ *
2181
+ * `this.parentPath` is root-first, so the direct parent is the
2182
+ * **last** entry: `this.parentPath.at(-1)`. For grandparents and
2183
+ * further ancestors, iterate `this.parentPath` and use
2184
+ * `getAgentByName(env.X, this.parentPath[i].name)` directly.
2185
+ *
2186
+ * Assumes the standard "binding name matches class name" convention.
2187
+ * If your `wrangler.jsonc` binds the parent under a different name
2188
+ * (e.g. `{ class_name: "Inbox", name: "MY_INBOX" }`), call
2189
+ * `getAgentByName(env.MY_INBOX, this.parentPath.at(-1)!.name)`
2190
+ * directly instead.
2191
+ *
2192
+ * @experimental The API surface may change before stabilizing.
2193
+ *
2194
+ * @throws If this agent is not a facet (no parent).
2195
+ * @throws If `Cls.name` doesn't match the recorded direct-parent
2196
+ * class (guards against accidentally reaching the wrong
2197
+ * DO, especially in nested Root → Mid → Leaf chains).
2198
+ * @throws If no env binding named `Cls.name` is found.
2199
+ *
2200
+ * @example
2201
+ * ```ts
2202
+ * class Chat extends AIChatAgent<Env> {
2203
+ * async onChatMessage(...) {
2204
+ * const inbox = await this.parentAgent(Inbox);
2205
+ * const memory = await inbox.getSharedMemory("facts");
2206
+ * // ...
2207
+ * }
2208
+ * }
2209
+ * ```
2210
+ */
2211
+ async parentAgent(cls) {
2212
+ const parent = this._parentPath[this._parentPath.length - 1];
2213
+ if (!parent) throw new Error(`parentAgent(): ${this.constructor.name} is not a facet — only sub-agents (spawned via \`subAgent()\`) have a parent.`);
2214
+ 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.`);
2215
+ const binding = this.env[cls.name];
2216
+ 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.`);
2217
+ return await getServerByName(binding, parent.name);
2218
+ }
2219
+ /**
2026
2220
  * Get or create a named sub-agent — a child Durable Object (facet)
2027
2221
  * with its own isolated SQLite storage running on the same machine.
2028
2222
  *
@@ -2043,12 +2237,29 @@ var Agent = class Agent extends Server {
2043
2237
  * ```
2044
2238
  */
2045
2239
  async subAgent(cls, name) {
2240
+ return await this._cf_resolveSubAgent(cls.name, name);
2241
+ }
2242
+ /**
2243
+ * Shared facet resolution — takes a CamelCase class name string
2244
+ * (matching `ctx.exports`) rather than a class reference. Both
2245
+ * `subAgent(cls, name)` and `_cf_invokeSubAgent(className, ...)`
2246
+ * funnel through here so registry bookkeeping and the
2247
+ * `_cf_initAsFacet` handshake are consistent.
2248
+ *
2249
+ * @internal
2250
+ */
2251
+ async _cf_resolveSubAgent(className, name) {
2046
2252
  const ctx = this.ctx;
2047
2253
  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 (!ctx.exports[cls.name]) throw new Error(`Sub-agent class "${cls.name}" 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.`);
2049
- const facetKey = `${cls.name}\0${name}`;
2050
- const stub = ctx.facets.get(facetKey, () => ({ class: ctx.exports[cls.name] }));
2051
- await stub._cf_initAsFacet(name);
2254
+ 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").`);
2255
+ const Cls = ctx.exports[className];
2256
+ 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.`);
2257
+ if (name.includes("\0")) throw new Error(`Sub-agent name contains null character (\\0), which is reserved.`);
2258
+ const facetKey = `${className}\0${name}`;
2259
+ const stub = ctx.facets.get(facetKey, () => ({ class: Cls }));
2260
+ const childParentPath = this.selfPath;
2261
+ await stub._cf_initAsFacet(name, childParentPath);
2262
+ this._recordSubAgent(className, name);
2052
2263
  return stub;
2053
2264
  }
2054
2265
  /**
@@ -2082,7 +2293,63 @@ var Agent = class Agent extends Server {
2082
2293
  const ctx = this.ctx;
2083
2294
  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
2295
  const facetKey = `${cls.name}\0${name}`;
2085
- ctx.facets.delete(facetKey);
2296
+ try {
2297
+ ctx.facets.delete(facetKey);
2298
+ } catch {}
2299
+ this._forgetSubAgent(cls.name, name);
2300
+ }
2301
+ /** @internal */
2302
+ _ensureSubAgentRegistry() {
2303
+ if (this._subAgentRegistryReady) return;
2304
+ this.sql`
2305
+ CREATE TABLE IF NOT EXISTS cf_agents_sub_agents (
2306
+ class TEXT NOT NULL,
2307
+ name TEXT NOT NULL,
2308
+ created_at INTEGER NOT NULL,
2309
+ PRIMARY KEY (class, name)
2310
+ )
2311
+ `;
2312
+ this._subAgentRegistryReady = true;
2313
+ }
2314
+ /** @internal */
2315
+ _recordSubAgent(className, name) {
2316
+ this._ensureSubAgentRegistry();
2317
+ this.sql`
2318
+ INSERT OR IGNORE INTO cf_agents_sub_agents (class, name, created_at)
2319
+ VALUES (${className}, ${name}, ${Date.now()})
2320
+ `;
2321
+ }
2322
+ /** @internal */
2323
+ _forgetSubAgent(className, name) {
2324
+ this._ensureSubAgentRegistry();
2325
+ this.sql`
2326
+ DELETE FROM cf_agents_sub_agents
2327
+ WHERE class = ${className} AND name = ${name}
2328
+ `;
2329
+ }
2330
+ hasSubAgent(classOrName, name) {
2331
+ const className = typeof classOrName === "string" ? classOrName : classOrName.name;
2332
+ this._ensureSubAgentRegistry();
2333
+ return (this.sql`
2334
+ SELECT COUNT(*) AS n FROM cf_agents_sub_agents
2335
+ WHERE class = ${className} AND name = ${name}
2336
+ `[0]?.n ?? 0) > 0;
2337
+ }
2338
+ listSubAgents(classOrName) {
2339
+ const className = typeof classOrName === "string" ? classOrName : classOrName?.name;
2340
+ this._ensureSubAgentRegistry();
2341
+ return (className ? this.sql`
2342
+ SELECT class, name, created_at FROM cf_agents_sub_agents
2343
+ WHERE class = ${className}
2344
+ ORDER BY created_at ASC
2345
+ ` : this.sql`
2346
+ SELECT class, name, created_at FROM cf_agents_sub_agents
2347
+ ORDER BY created_at ASC
2348
+ `).map((r) => ({
2349
+ className: r.class,
2350
+ name: r.name,
2351
+ createdAt: r.created_at
2352
+ }));
2086
2353
  }
2087
2354
  /**
2088
2355
  * Destroy the Agent, removing all state and scheduled tasks
@@ -2093,6 +2360,7 @@ var Agent = class Agent extends Server {
2093
2360
  this.sql`DROP TABLE IF EXISTS cf_agents_schedules`;
2094
2361
  this.sql`DROP TABLE IF EXISTS cf_agents_queues`;
2095
2362
  this.sql`DROP TABLE IF EXISTS cf_agents_workflows`;
2363
+ this.sql`DROP TABLE IF EXISTS cf_agents_sub_agents`;
2096
2364
  if (!this._isFacet) await this.ctx.storage.deleteAlarm();
2097
2365
  await this.ctx.storage.deleteAll();
2098
2366
  this._disposables.dispose();
@@ -3034,7 +3302,7 @@ var Agent = class Agent extends Server {
3034
3302
  broadcastMcpServers() {
3035
3303
  this._broadcastProtocol(JSON.stringify({
3036
3304
  mcp: this.getMcpServers(),
3037
- type: MessageType.CF_AGENT_MCP_SERVERS
3305
+ type: "cf_agent_mcp_servers"
3038
3306
  }));
3039
3307
  }
3040
3308
  /**
@@ -3225,7 +3493,7 @@ var StreamingResponse = class {
3225
3493
  id: this._id,
3226
3494
  result: chunk,
3227
3495
  success: true,
3228
- type: MessageType.RPC
3496
+ type: "rpc"
3229
3497
  };
3230
3498
  this._connection.send(JSON.stringify(response));
3231
3499
  return true;
@@ -3243,7 +3511,7 @@ var StreamingResponse = class {
3243
3511
  id: this._id,
3244
3512
  result: finalChunk,
3245
3513
  success: true,
3246
- type: MessageType.RPC
3514
+ type: "rpc"
3247
3515
  };
3248
3516
  this._connection.send(JSON.stringify(response));
3249
3517
  return true;
@@ -3260,13 +3528,13 @@ var StreamingResponse = class {
3260
3528
  error: message,
3261
3529
  id: this._id,
3262
3530
  success: false,
3263
- type: MessageType.RPC
3531
+ type: "rpc"
3264
3532
  };
3265
3533
  this._connection.send(JSON.stringify(response));
3266
3534
  return true;
3267
3535
  }
3268
3536
  };
3269
3537
  //#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 };
3538
+ 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
3539
 
3272
3540
  //# sourceMappingURL=index.js.map