agents 0.13.3 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +6 -4
  2. package/dist/{agent-tool-types-l98LCbBl.d.ts → agent-tool-types-LInzZfLo.d.ts} +463 -116
  3. package/dist/agent-tool-types.d.ts +13 -11
  4. package/dist/{agent-tools-Bg5ilERh.d.ts → agent-tools-BE9xosUG.d.ts} +2 -2
  5. package/dist/agent-tools.d.ts +14 -20
  6. package/dist/agent-tools.js +10 -6
  7. package/dist/agent-tools.js.map +1 -1
  8. package/dist/browser/ai.d.ts +1 -1
  9. package/dist/browser/ai.js +1 -1
  10. package/dist/browser/index.d.ts +1 -1
  11. package/dist/browser/index.js +1 -1
  12. package/dist/browser/tanstack-ai.d.ts +1 -1
  13. package/dist/browser/tanstack-ai.js +1 -1
  14. package/dist/chat/index.d.ts +138 -19
  15. package/dist/chat/index.js +96 -12
  16. package/dist/chat/index.js.map +1 -1
  17. package/dist/chat-sdk/index.d.ts +4 -4
  18. package/dist/classPrivateMethodInitSpec-bG0tD96O.js +7 -0
  19. package/dist/{client-D1kFXo80.js → client-NradHZZz.js} +206 -75
  20. package/dist/client-NradHZZz.js.map +1 -0
  21. package/dist/client.d.ts +1 -1
  22. package/dist/{compaction-helpers-fJyf8j4m.js → compaction-helpers-BjT2NKRZ.js} +22 -3
  23. package/dist/compaction-helpers-BjT2NKRZ.js.map +1 -0
  24. package/dist/{compaction-helpers-B-pG5J22.d.ts → compaction-helpers-DpP_XP9J.d.ts} +59 -33
  25. package/dist/{do-oauth-client-provider-4OKQU9rT.d.ts → do-oauth-client-provider-CPm9rK5I.d.ts} +1 -1
  26. package/dist/{email-J0GGS3sa.d.ts → email-1fTSJwPm.d.ts} +1 -1
  27. package/dist/email.d.ts +2 -2
  28. package/dist/experimental/memory/session/index.d.ts +30 -25
  29. package/dist/experimental/memory/session/index.js +7 -2
  30. package/dist/experimental/memory/session/index.js.map +1 -1
  31. package/dist/experimental/memory/utils/index.d.ts +12 -10
  32. package/dist/experimental/memory/utils/index.js +2 -2
  33. package/dist/{index-DKey3P4s.d.ts → index-Brdu5nMI.d.ts} +270 -1
  34. package/dist/index.d.ts +74 -67
  35. package/dist/index.js +467 -63
  36. package/dist/index.js.map +1 -1
  37. package/dist/{internal_context-BZrMS0B5.d.ts → internal_context-CcZy2Em7.d.ts} +1 -1
  38. package/dist/internal_context.d.ts +1 -1
  39. package/dist/mcp/client.d.ts +17 -13
  40. package/dist/mcp/client.js +2 -2
  41. package/dist/mcp/do-oauth-client-provider.d.ts +1 -1
  42. package/dist/mcp/index.d.ts +35 -27
  43. package/dist/mcp/index.js +402 -69
  44. package/dist/mcp/index.js.map +1 -1
  45. package/dist/observability/index.d.ts +1 -1
  46. package/dist/observability/index.js +15 -1
  47. package/dist/observability/index.js.map +1 -1
  48. package/dist/react.d.ts +3 -3
  49. package/dist/{retries-BVdRl5ZE.d.ts → retries-ClWwxADl.d.ts} +1 -1
  50. package/dist/retries.d.ts +1 -1
  51. package/dist/serializable.d.ts +1 -1
  52. package/dist/{shared-Cvj92byG.d.ts → shared-CpY1FLvm.d.ts} +1 -1
  53. package/dist/{shared-CiKaIK4h.js → shared-DdOn6sp4.js} +3 -7
  54. package/dist/{shared-CiKaIK4h.js.map → shared-DdOn6sp4.js.map} +1 -1
  55. package/dist/skills/index.d.ts +236 -0
  56. package/dist/skills/index.js +1326 -0
  57. package/dist/skills/index.js.map +1 -0
  58. package/dist/sub-routing.d.ts +6 -6
  59. package/dist/{tool-output-truncation-CH-khbZ3.js → tool-output-truncation-BF4AZQlw.js} +1 -1
  60. package/dist/{tool-output-truncation-CH-khbZ3.js.map → tool-output-truncation-BF4AZQlw.js.map} +1 -1
  61. package/dist/{types-_JjKmv-l.d.ts → types-B0GymtN_.d.ts} +1 -1
  62. package/dist/types.d.ts +1 -1
  63. package/dist/vite.d.ts +1 -1
  64. package/dist/vite.js +248 -2
  65. package/dist/vite.js.map +1 -1
  66. package/dist/{workflow-types-Dkzg4hAx.d.ts → workflow-types-DPkuBi--.d.ts} +1 -1
  67. package/dist/workflow-types.d.ts +1 -1
  68. package/dist/workflows.d.ts +13 -3
  69. package/dist/workflows.js +10 -1
  70. package/dist/workflows.js.map +1 -1
  71. package/package.json +21 -3
  72. package/skills-module.d.ts +22 -0
  73. package/dist/client-D1kFXo80.js.map +0 -1
  74. package/dist/compaction-helpers-fJyf8j4m.js.map +0 -1
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import { createHeaderBasedEmailResolver, signAgentHeaders } from "./email.js";
5
5
  import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-Evpt0SEr.js";
6
6
  import { SUB_PREFIX, getSubAgentByName, parseSubAgentPath, routeSubAgentRequest } from "./sub-routing.js";
7
7
  import { isErrorRetryable, tryN, validateRetryOptions } from "./retries.js";
8
- import { o as RPC_DO_PREFIX, r as MCPConnectionState, s as DisposableStore, t as MCPClientManager } from "./client-D1kFXo80.js";
8
+ import { a as MCPConnectionState, c as RPC_DO_PREFIX, i as normalizeServerId, l as DisposableStore, n as MCP_SERVER_ID_MAX_LENGTH, t as MCPClientManager } from "./client-NradHZZz.js";
9
9
  import { DurableObjectOAuthClientProvider } from "./mcp/do-oauth-client-provider.js";
10
10
  import { genericObservability } from "./observability/index.js";
11
11
  import { AsyncLocalStorage } from "node:async_hooks";
@@ -114,6 +114,9 @@ function getNextCronTime(cron) {
114
114
  return parseCronExpression(cron).getNextDate();
115
115
  }
116
116
  const DEFAULT_KEEP_ALIVE_INTERVAL_MS = 3e4;
117
+ const DEFAULT_AGENT_TOOL_RECOVERY_TIMEOUT_MS = 2e3;
118
+ const DEFAULT_AGENT_TOOL_RECOVERY_TOTAL_TIMEOUT_MS = 5e3;
119
+ const DEFAULT_AGENT_TOOL_REATTACH_TIMEOUT_MS = 12e4;
117
120
  const SUB_AGENT_IDENTITY_VERSION_LEGACY = "legacy";
118
121
  const SUB_AGENT_IDENTITY_VERSION_PATH_V2 = "path-v2";
119
122
  const SUB_AGENT_IDENTITY_PATH_V2_PREFIX = "cf-agents:v2:";
@@ -262,7 +265,17 @@ const DEFAULT_AGENT_STATIC_OPTIONS = {
262
265
  maxAttempts: 3,
263
266
  baseDelayMs: 100,
264
267
  maxDelayMs: 3e3
265
- }
268
+ },
269
+ /** Timeout for internal framework fiber recovery hooks. */
270
+ fiberRecoveryHookTimeoutMs: 1e4,
271
+ /** Soft deadline for one interrupted-fiber recovery scan. */
272
+ fiberRecoveryScanDeadlineMs: 1e4,
273
+ /**
274
+ * Maximum age of an unmanaged interrupted-fiber row before recovery gives
275
+ * up. Bounds repeated retries of a `onFiberRecovered()` hook that keeps
276
+ * throwing so a poison row cannot re-trigger forever across boots.
277
+ */
278
+ fiberRecoveryMaxAgeMs: 1440 * 60 * 1e3
266
279
  };
267
280
  /**
268
281
  * Parse the raw `retry_options` TEXT column from a SQLite row into a
@@ -285,6 +298,19 @@ function resolveRetryConfig(taskRetry, defaults) {
285
298
  maxDelayMs: taskRetry?.maxDelayMs ?? defaults.maxDelayMs
286
299
  };
287
300
  }
301
+ /**
302
+ * Whether an error is a Durable Object reset caused by a code update (deploy).
303
+ *
304
+ * This is a transient, environmental failure: the invocation started on a
305
+ * superseded isolate, so every `ctx.storage` op throws this for the entire
306
+ * life of the invocation (code never reloads mid-invocation) — but the next
307
+ * fresh invocation runs the new code and succeeds. workerd surfaces it as a
308
+ * plain `Error` with this message, so a message match is the only signal.
309
+ */
310
+ function isDurableObjectCodeUpdateReset(error) {
311
+ const message = error instanceof Error ? error.message : typeof error === "string" ? error : "";
312
+ return /reset because its code was updated/i.test(message);
313
+ }
288
314
  function getCurrentAgent() {
289
315
  const store = __DO_NOT_USE_WILL_BREAK__agentContext.getStore();
290
316
  if (!store) return {
@@ -382,7 +408,10 @@ var Agent = class Agent extends Server {
382
408
  maxAttempts: userRetry?.maxAttempts ?? DEFAULT_AGENT_STATIC_OPTIONS.retry.maxAttempts,
383
409
  baseDelayMs: userRetry?.baseDelayMs ?? DEFAULT_AGENT_STATIC_OPTIONS.retry.baseDelayMs,
384
410
  maxDelayMs: userRetry?.maxDelayMs ?? DEFAULT_AGENT_STATIC_OPTIONS.retry.maxDelayMs
385
- }
411
+ },
412
+ fiberRecoveryHookTimeoutMs: ctor.options?.fiberRecoveryHookTimeoutMs ?? DEFAULT_AGENT_STATIC_OPTIONS.fiberRecoveryHookTimeoutMs,
413
+ fiberRecoveryScanDeadlineMs: ctor.options?.fiberRecoveryScanDeadlineMs ?? DEFAULT_AGENT_STATIC_OPTIONS.fiberRecoveryScanDeadlineMs,
414
+ fiberRecoveryMaxAgeMs: ctor.options?.fiberRecoveryMaxAgeMs ?? DEFAULT_AGENT_STATIC_OPTIONS.fiberRecoveryMaxAgeMs
386
415
  };
387
416
  return this._cachedOptions;
388
417
  }
@@ -885,7 +914,7 @@ var Agent = class Agent extends Server {
885
914
  this.broadcastMcpServers();
886
915
  this._checkOrphanedWorkflows();
887
916
  await this._checkRunFibers();
888
- const recoveredAgentToolFinishes = await this._reconcileAgentToolRuns({ deferFinishHooks: true });
917
+ const startupAgentToolRunIds = this._agentToolRunRecoveryRunIds();
889
918
  this._insideOnStart = true;
890
919
  this._warnedScheduleInOnStart.clear();
891
920
  let result;
@@ -894,7 +923,7 @@ var Agent = class Agent extends Server {
894
923
  } finally {
895
924
  this._insideOnStart = false;
896
925
  }
897
- await this._runDeferredAgentToolFinishHooks(recoveredAgentToolFinishes);
926
+ this._scheduleAgentToolRunRecovery({ runIds: startupAgentToolRunIds });
898
927
  return result;
899
928
  });
900
929
  });
@@ -2386,20 +2415,67 @@ var Agent = class Agent extends Server {
2386
2415
  return null;
2387
2416
  }
2388
2417
  }
2418
+ _fiberRecoveryPayload(ctx, managedRow, startedAt) {
2419
+ return {
2420
+ fiberId: ctx.id,
2421
+ fiberName: ctx.name,
2422
+ managed: managedRow !== null,
2423
+ recoveryReason: ctx.recoveryReason,
2424
+ elapsedMs: startedAt === void 0 ? void 0 : Date.now() - startedAt
2425
+ };
2426
+ }
2427
+ async _withFiberRecoveryTimeout(ctx, operation) {
2428
+ const timeoutMs = this._resolvedOptions.fiberRecoveryHookTimeoutMs;
2429
+ if (timeoutMs <= 0) return operation();
2430
+ let timer;
2431
+ try {
2432
+ return await Promise.race([operation(), new Promise((_, reject) => {
2433
+ timer = setTimeout(() => {
2434
+ reject(/* @__PURE__ */ new Error(`Fiber recovery hook timed out after ${timeoutMs}ms for "${ctx.name}" (${ctx.id})`));
2435
+ }, timeoutMs);
2436
+ })]);
2437
+ } finally {
2438
+ if (timer !== void 0) clearTimeout(timer);
2439
+ }
2440
+ }
2441
+ _recordFiberRecoveryFailure(ctx, managedRow, error, startedAt, reason = "handler_error") {
2442
+ const errorMessage = this._fiberErrorMessage(error);
2443
+ const completedAt = Date.now();
2444
+ if (managedRow) {
2445
+ this.sql`
2446
+ UPDATE cf_agents_fibers
2447
+ SET status = 'error',
2448
+ error_message = ${errorMessage},
2449
+ completed_at = ${completedAt}
2450
+ WHERE fiber_id = ${ctx.id}
2451
+ AND status = 'interrupted'
2452
+ `;
2453
+ this._notifyManagedFiberTerminal(ctx.id);
2454
+ }
2455
+ this._emit("fiber:recovery:failed", {
2456
+ ...this._fiberRecoveryPayload(ctx, managedRow, startedAt),
2457
+ error: errorMessage,
2458
+ reason
2459
+ });
2460
+ }
2389
2461
  async _runFiberRecoveryHook(ctx, managedRow) {
2462
+ const startedAt = Date.now();
2463
+ this._emit("fiber:recovery:attempt", this._fiberRecoveryPayload(ctx, managedRow));
2390
2464
  try {
2391
- if (!await this._handleInternalFiberRecovery(ctx)) {
2465
+ const handled = await this._withFiberRecoveryTimeout(ctx, () => this._handleInternalFiberRecovery(ctx));
2466
+ if (!handled) {
2392
2467
  const recoveryResult = await this.onFiberRecovered(ctx);
2393
2468
  if (managedRow && recoveryResult) this._applyManagedFiberRecoveryResult(ctx.id, recoveryResult);
2394
2469
  }
2470
+ this._emit("fiber:recovery:handled", {
2471
+ ...this._fiberRecoveryPayload(ctx, managedRow, startedAt),
2472
+ status: handled ? "internal" : managedRow ? "managed" : "user"
2473
+ });
2474
+ return true;
2395
2475
  } catch (e) {
2396
- if (managedRow) this.sql`
2397
- UPDATE cf_agents_fibers
2398
- SET error_message = ${this._fiberErrorMessage(e)}
2399
- WHERE fiber_id = ${ctx.id}
2400
- AND status = 'interrupted'
2401
- `;
2476
+ this._recordFiberRecoveryFailure(ctx, managedRow, e, startedAt);
2402
2477
  console.error(`[Agent] Fiber recovery failed for "${ctx.name}" (${ctx.id}):`, e);
2478
+ return false;
2403
2479
  }
2404
2480
  }
2405
2481
  _fiberInspectionFromRow(row) {
@@ -2684,6 +2760,12 @@ var Agent = class Agent extends Server {
2684
2760
  INSERT INTO cf_agents_runs (id, name, snapshot, created_at)
2685
2761
  VALUES (${id}, ${name}, NULL, ${Date.now()})
2686
2762
  `;
2763
+ const startedAt = Date.now();
2764
+ this._emit("fiber:run:started", {
2765
+ fiberId: id,
2766
+ fiberName: name,
2767
+ managed: options?.managed === true
2768
+ });
2687
2769
  this._runFiberActiveFibers.add(id);
2688
2770
  const writeSnapshot = (data) => {
2689
2771
  const snapshot = JSON.stringify(data);
@@ -2722,12 +2804,25 @@ var Agent = class Agent extends Server {
2722
2804
  snapshot: null
2723
2805
  }));
2724
2806
  options?.beforeRunCleanup?.({ ok: true });
2807
+ this._emit("fiber:run:completed", {
2808
+ fiberId: id,
2809
+ fiberName: name,
2810
+ managed: options?.managed === true,
2811
+ elapsedMs: Date.now() - startedAt
2812
+ });
2725
2813
  return result;
2726
2814
  } catch (error) {
2727
2815
  options?.beforeRunCleanup?.({
2728
2816
  ok: false,
2729
2817
  error
2730
2818
  });
2819
+ this._emit("fiber:run:failed", {
2820
+ fiberId: id,
2821
+ fiberName: name,
2822
+ managed: options?.managed === true,
2823
+ error: this._fiberErrorMessage(error),
2824
+ elapsedMs: Date.now() - startedAt
2825
+ });
2731
2826
  throw error;
2732
2827
  }
2733
2828
  } finally {
@@ -2777,18 +2872,42 @@ var Agent = class Agent extends Server {
2777
2872
  async _checkRunFibers() {
2778
2873
  if (this._runFiberRecoveryInProgress) return;
2779
2874
  this._runFiberRecoveryInProgress = true;
2875
+ const scanStartedAt = Date.now();
2876
+ const scanDeadlineMs = this._resolvedOptions.fiberRecoveryScanDeadlineMs;
2877
+ const fiberRecoveryMaxAgeMs = this._resolvedOptions.fiberRecoveryMaxAgeMs;
2780
2878
  try {
2781
2879
  const rows = this.sql`SELECT id, name, snapshot, created_at FROM cf_agents_runs`;
2782
2880
  for (const row of rows) {
2881
+ if (scanDeadlineMs > 0 && Date.now() - scanStartedAt > scanDeadlineMs) {
2882
+ this._emit("fiber:recovery:skipped", {
2883
+ fiberId: row.id,
2884
+ fiberName: row.name,
2885
+ reason: "scan_deadline_exceeded",
2886
+ elapsedMs: Date.now() - scanStartedAt
2887
+ });
2888
+ break;
2889
+ }
2783
2890
  if (this._runFiberActiveFibers.has(row.id)) continue;
2784
2891
  const snapshot = this._parseFiberRecoverySnapshot(row.id, row.snapshot);
2785
2892
  const ctx = {
2786
2893
  id: row.id,
2787
2894
  name: row.name,
2788
2895
  snapshot,
2789
- createdAt: row.created_at
2896
+ createdAt: row.created_at,
2897
+ recoveryReason: "interrupted"
2790
2898
  };
2791
2899
  const managedRow = this._readFiber(row.id);
2900
+ this._emit("fiber:recovery:detected", {
2901
+ ...this._fiberRecoveryPayload(ctx, managedRow),
2902
+ elapsedMs: Date.now() - row.created_at
2903
+ });
2904
+ this._emit("fiber:run:interrupted", {
2905
+ fiberId: row.id,
2906
+ fiberName: row.name,
2907
+ managed: managedRow !== null,
2908
+ recoveryReason: "interrupted",
2909
+ elapsedMs: Date.now() - row.created_at
2910
+ });
2792
2911
  if (managedRow) {
2793
2912
  if (this._isTerminalFiberStatus(managedRow.status)) {
2794
2913
  this.sql`DELETE FROM cf_agents_runs WHERE id = ${row.id}`;
@@ -2808,8 +2927,17 @@ var Agent = class Agent extends Server {
2808
2927
  ctx.metadata = this._parseFiberJsonObject(managedRow.metadata_json);
2809
2928
  ctx.status = "interrupted";
2810
2929
  }
2811
- await this._runFiberRecoveryHook(ctx, managedRow);
2812
- this.sql`DELETE FROM cf_agents_runs WHERE id = ${row.id}`;
2930
+ const recovered = await this._runFiberRecoveryHook(ctx, managedRow);
2931
+ const tooOld = fiberRecoveryMaxAgeMs > 0 && Date.now() - row.created_at > fiberRecoveryMaxAgeMs;
2932
+ if (recovered || managedRow || tooOld) {
2933
+ if (!recovered && !managedRow && tooOld) this._emit("fiber:recovery:skipped", {
2934
+ fiberId: row.id,
2935
+ fiberName: row.name,
2936
+ reason: "max_age_exceeded",
2937
+ elapsedMs: Date.now() - row.created_at
2938
+ });
2939
+ this.sql`DELETE FROM cf_agents_runs WHERE id = ${row.id}`;
2940
+ }
2813
2941
  if (managedRow) this._notifyManagedFiberTerminal(row.id);
2814
2942
  }
2815
2943
  const ledgerOnlyRows = this.sql`
@@ -2822,6 +2950,16 @@ var Agent = class Agent extends Server {
2822
2950
  AND r.id IS NULL
2823
2951
  `;
2824
2952
  for (const row of ledgerOnlyRows) {
2953
+ if (scanDeadlineMs > 0 && Date.now() - scanStartedAt > scanDeadlineMs) {
2954
+ this._emit("fiber:recovery:skipped", {
2955
+ fiberId: row.fiber_id,
2956
+ fiberName: row.name,
2957
+ reason: "scan_deadline_exceeded",
2958
+ elapsedMs: Date.now() - scanStartedAt,
2959
+ managed: true
2960
+ });
2961
+ break;
2962
+ }
2825
2963
  if (this._runFiberActiveFibers.has(row.fiber_id)) continue;
2826
2964
  const snapshot = this._parseFiberRecoverySnapshot(row.fiber_id, row.snapshot);
2827
2965
  const completedAt = Date.now();
@@ -2832,15 +2970,28 @@ var Agent = class Agent extends Server {
2832
2970
  WHERE fiber_id = ${row.fiber_id}
2833
2971
  AND status IN ('pending', 'running')
2834
2972
  `;
2835
- await this._runFiberRecoveryHook({
2973
+ const ctx = {
2836
2974
  id: row.fiber_id,
2837
2975
  name: row.name,
2838
2976
  snapshot,
2839
2977
  createdAt: row.created_at,
2840
2978
  idempotencyKey: row.idempotency_key ?? void 0,
2841
2979
  metadata: this._parseFiberJsonObject(row.metadata_json),
2842
- status: "interrupted"
2843
- }, row);
2980
+ status: "interrupted",
2981
+ recoveryReason: "interrupted"
2982
+ };
2983
+ this._emit("fiber:recovery:detected", {
2984
+ ...this._fiberRecoveryPayload(ctx, row),
2985
+ elapsedMs: Date.now() - row.created_at
2986
+ });
2987
+ this._emit("fiber:run:interrupted", {
2988
+ fiberId: row.fiber_id,
2989
+ fiberName: row.name,
2990
+ managed: true,
2991
+ recoveryReason: "interrupted",
2992
+ elapsedMs: Date.now() - row.created_at
2993
+ });
2994
+ await this._runFiberRecoveryHook(ctx, row);
2844
2995
  this._notifyManagedFiberTerminal(row.fiber_id);
2845
2996
  }
2846
2997
  } finally {
@@ -2998,6 +3149,8 @@ var Agent = class Agent extends Server {
2998
3149
  });
2999
3150
  return;
3000
3151
  }
3152
+ const isOneShotSchedule = row.type === "delayed" || row.type === "scheduled";
3153
+ const shouldDeferReset = (error) => isOneShotSchedule && isDurableObjectCodeUpdateReset(error);
3001
3154
  try {
3002
3155
  this._emit("schedule:execute", {
3003
3156
  callback: row.callback,
@@ -3013,9 +3166,14 @@ var Agent = class Agent extends Server {
3013
3166
  await callback.bind(this)(parsedPayload, row);
3014
3167
  }, {
3015
3168
  baseDelayMs,
3016
- maxDelayMs
3169
+ maxDelayMs,
3170
+ shouldRetry: (error) => !shouldDeferReset(error)
3017
3171
  });
3018
3172
  } catch (e) {
3173
+ if (shouldDeferReset(e)) {
3174
+ console.warn(`Deferring scheduled callback "${row.callback}" to a fresh invocation after a Durable Object code-update reset; the one-shot row is preserved and the alarm will re-run on new code.`);
3175
+ throw e;
3176
+ }
3019
3177
  console.error(`error executing callback "${row.callback}" after ${maxAttempts} attempts`, e);
3020
3178
  this._emit("schedule:error", {
3021
3179
  callback: row.callback,
@@ -3835,7 +3993,7 @@ var Agent = class Agent extends Server {
3835
3993
  const agentType = cls.name;
3836
3994
  const existing = this._readAgentToolRun(runId);
3837
3995
  if (existing) {
3838
- if (this._isAgentToolTerminal(existing.status)) {
3996
+ if (existing.status === "completed" || existing.status === "error" || existing.status === "aborted") {
3839
3997
  if (existing.status === "completed" && existing.output_json == null) try {
3840
3998
  const child = await this.subAgent(cls, runId);
3841
3999
  const inspection = await this._asAgentToolChildAdapter(child).inspectAgentToolRun(runId);
@@ -3847,7 +4005,19 @@ var Agent = class Agent extends Server {
3847
4005
  } catch {}
3848
4006
  return this._resultFromAgentToolRow(existing);
3849
4007
  }
3850
- return await this._replayAndInterruptAgentToolRun(existing, "Agent tool run was still running, but live-tail reattachment is not supported in this runtime.");
4008
+ try {
4009
+ const child = await this.subAgent(cls, runId);
4010
+ const adapter = this._asAgentToolChildAdapter(child);
4011
+ const reattach = await this._reattachAgentToolRunToTerminal(adapter, existing, 1);
4012
+ if (reattach.result) {
4013
+ await this._finishAgentToolRun(this._agentToolRunInfoFromRow(existing), reattach.result, {
4014
+ sequence: reattach.sequence,
4015
+ completedAt: reattach.completedAt
4016
+ });
4017
+ return reattach.result;
4018
+ }
4019
+ } catch {}
4020
+ return await this._replayAndInterruptAgentToolRun(existing, "Agent tool run was still running and could not be re-attached to a terminal result within the recovery budget.");
3851
4021
  }
3852
4022
  const displayOrder = options.displayOrder ?? 0;
3853
4023
  const inputPreview = options.inputPreview ?? this._defaultAgentToolPreview(options.input);
@@ -4127,7 +4297,7 @@ var Agent = class Agent extends Server {
4127
4297
  error_message = ${result.error ?? null},
4128
4298
  completed_at = ${completedAt}
4129
4299
  WHERE run_id = ${runId}
4130
- AND status NOT IN ('completed', 'error', 'aborted', 'interrupted')
4300
+ AND status NOT IN ('completed', 'error', 'aborted')
4131
4301
  `;
4132
4302
  if (result.status === "completed" && result.output !== void 0) this.sql`
4133
4303
  UPDATE cf_agent_tool_runs
@@ -4179,7 +4349,12 @@ var Agent = class Agent extends Server {
4179
4349
  }
4180
4350
  async _broadcastAgentToolStoredChunks(row, sequence, replay, connection) {
4181
4351
  const child = await this._cf_resolveSubAgent(row.agent_type, row.run_id);
4182
- const chunks = await this._asAgentToolChildAdapter(child).getAgentToolChunks(row.run_id);
4352
+ const adapter = this._asAgentToolChildAdapter(child);
4353
+ return this._broadcastAgentToolStoredChunksFromAdapter(adapter, row, sequence, replay, connection);
4354
+ }
4355
+ async _broadcastAgentToolStoredChunksFromAdapter(adapter, row, sequence, replay, connection, timeoutMs) {
4356
+ const chunks = await this._getAgentToolChunksForRecovery(adapter, row.run_id, timeoutMs);
4357
+ if (!chunks) return sequence;
4183
4358
  return this._broadcastAgentToolChunks(row.parent_tool_call_id ?? void 0, row.run_id, chunks, sequence, replay, connection);
4184
4359
  }
4185
4360
  async _forwardAgentToolStream(stream, parentToolCallId, runId, sequence, signal) {
@@ -4188,11 +4363,17 @@ var Agent = class Agent extends Server {
4188
4363
  const reader = stream.getReader();
4189
4364
  const decoder = new TextDecoder();
4190
4365
  let bufferedBytes = "";
4366
+ let aborted = false;
4367
+ let resolveAbort;
4368
+ const abortPromise = new Promise((resolve) => {
4369
+ resolveAbort = resolve;
4370
+ });
4191
4371
  let abortListener;
4192
4372
  if (signal) {
4193
- abortListener = () => {};
4373
+ abortListener = () => resolveAbort?.();
4194
4374
  signal.addEventListener("abort", abortListener, { once: true });
4195
4375
  }
4376
+ let forwardedSinceProgress = false;
4196
4377
  try {
4197
4378
  const forwardChunk = (chunk) => {
4198
4379
  this._broadcastAgentToolEvent(parentToolCallId, next++, {
@@ -4200,6 +4381,7 @@ var Agent = class Agent extends Server {
4200
4381
  runId,
4201
4382
  body: chunk.body
4202
4383
  });
4384
+ forwardedSinceProgress = true;
4203
4385
  };
4204
4386
  const forwardLine = (line) => {
4205
4387
  try {
@@ -4221,14 +4403,17 @@ var Agent = class Agent extends Server {
4221
4403
  }
4222
4404
  };
4223
4405
  while (true) {
4224
- let readResult;
4225
- try {
4226
- readResult = await reader.read();
4227
- } catch (error) {
4228
- if (signal?.aborted) break;
4229
- throw error;
4406
+ const readPromise = reader.read();
4407
+ readPromise.catch(() => {});
4408
+ const raced = await Promise.race([readPromise.then((result) => ({
4409
+ kind: "read",
4410
+ result
4411
+ })), abortPromise.then(() => ({ kind: "abort" }))]);
4412
+ if (raced.kind === "abort") {
4413
+ aborted = true;
4414
+ break;
4230
4415
  }
4231
- const { done, value } = readResult;
4416
+ const { done, value } = raced.result;
4232
4417
  if (done) {
4233
4418
  bufferedBytes += decoder.decode();
4234
4419
  flushBufferedBytes(true);
@@ -4238,13 +4423,42 @@ var Agent = class Agent extends Server {
4238
4423
  bufferedBytes += decoder.decode(value, { stream: true });
4239
4424
  flushBufferedBytes();
4240
4425
  } else forwardChunk(value);
4426
+ if (forwardedSinceProgress) {
4427
+ forwardedSinceProgress = false;
4428
+ try {
4429
+ await this._onAgentToolStreamProgress();
4430
+ } catch {}
4431
+ }
4241
4432
  }
4242
4433
  } finally {
4243
4434
  if (abortListener && signal) signal.removeEventListener("abort", abortListener);
4244
- reader.releaseLock();
4435
+ if (!aborted) try {
4436
+ reader.releaseLock();
4437
+ } catch {}
4245
4438
  }
4246
4439
  return next;
4247
4440
  }
4441
+ /**
4442
+ * Hook invoked by `_forwardAgentToolStream` after a child produces output that
4443
+ * was forwarded to the parent's connections. Forwarding a sub-agent's stream
4444
+ * is genuine forward progress for the *parent* turn (the parent is
4445
+ * orchestrating the child), so chat-recovery subclasses (Think / AIChatAgent)
4446
+ * override this to advance their recovery progress marker.
4447
+ *
4448
+ * Without it, a parent whose turn merely `await`s a sub-agent banks zero
4449
+ * progress of its own, so under deploy churn the parent's no-progress recovery
4450
+ * window exhausts and abandons the turn as `interrupted` — even though the
4451
+ * child is healthily streaming and ultimately completes (observed in the
4452
+ * `deploy-churn --mode subagent` harness: `attempt 6/6, stable_timeout,
4453
+ * progress: 1`).
4454
+ *
4455
+ * Called ONLY after at least one chunk was actually forwarded — never merely
4456
+ * because a child is attached — so a silent / hung child still lets the parent
4457
+ * exhaust on its own timer. The base Agent has no recovery budget, so this is
4458
+ * a no-op; subclasses should throttle the (durable) bump since this can be
4459
+ * called repeatedly while a child streams.
4460
+ */
4461
+ async _onAgentToolStreamProgress() {}
4248
4462
  _broadcastAgentToolTerminal(parentToolCallId, sequence, result, replay, connection) {
4249
4463
  if (result.status === "completed") this._broadcastAgentToolEvent(parentToolCallId, sequence, {
4250
4464
  kind: "finished",
@@ -4291,6 +4505,58 @@ var Agent = class Agent extends Server {
4291
4505
  await this._finishAgentToolRun(this._agentToolRunInfoFromRow(row), result, { sequence });
4292
4506
  return result;
4293
4507
  }
4508
+ /**
4509
+ * Re-attach to a still-running child agent-tool run and tail it to its real
4510
+ * terminal result, instead of abandoning it as `interrupted` (#1630). The
4511
+ * child is a separate facet with its own `chatRecovery`, so resolving it via
4512
+ * the adapter wakes it and lets it self-complete the interrupted turn; we tail
4513
+ * its live stream (forwarding chunks to the parent's connections) until it
4514
+ * reaches terminal, then inspect for the collected result.
4515
+ *
4516
+ * Bounded by {@link DEFAULT_AGENT_TOOL_REATTACH_TIMEOUT_MS}: a child that keeps
4517
+ * advancing toward terminal within the window is collected; a genuinely hung
4518
+ * child returns `{ result: undefined }` once the budget elapses so the caller
4519
+ * falls back to `interrupted` and recovery can never block forever.
4520
+ *
4521
+ * Returns the terminal `result` (and `completedAt`) when the child reaches a
4522
+ * terminal status within the budget, plus the advanced broadcast `sequence`.
4523
+ * Returns `{ result: undefined }` when there is no `tailAgentToolRun` adapter,
4524
+ * the budget is exhausted, or the child is still non-terminal.
4525
+ */
4526
+ async _reattachAgentToolRunToTerminal(adapter, row, sequence, budgetMs = DEFAULT_AGENT_TOOL_REATTACH_TIMEOUT_MS) {
4527
+ if (typeof adapter.tailAgentToolRun !== "function") return { sequence };
4528
+ const controller = new AbortController();
4529
+ let timeoutId;
4530
+ if (budgetMs > 0 && Number.isFinite(budgetMs)) timeoutId = setTimeout(() => controller.abort(), budgetMs);
4531
+ else if (budgetMs <= 0) controller.abort();
4532
+ this._emit("agent_tool:recovery:reattach", {
4533
+ runId: row.run_id,
4534
+ agentType: row.agent_type,
4535
+ budgetMs
4536
+ });
4537
+ let nextSequence = sequence;
4538
+ if (!controller.signal.aborted) try {
4539
+ let afterSequence = -1;
4540
+ try {
4541
+ const existing = await adapter.getAgentToolChunks(row.run_id);
4542
+ const last = existing[existing.length - 1];
4543
+ if (last) afterSequence = last.sequence;
4544
+ } catch {}
4545
+ const stream = await adapter.tailAgentToolRun(row.run_id, { afterSequence });
4546
+ nextSequence = await this._forwardAgentToolStream(stream, row.parent_tool_call_id ?? void 0, row.run_id, nextSequence, controller.signal);
4547
+ } catch {}
4548
+ if (timeoutId !== void 0) clearTimeout(timeoutId);
4549
+ let inspection = null;
4550
+ try {
4551
+ inspection = await adapter.inspectAgentToolRun(row.run_id);
4552
+ } catch {}
4553
+ if (inspection && inspection.status !== "running" && inspection.status !== "starting") return {
4554
+ sequence: nextSequence,
4555
+ result: this._terminalResultFromInspection(row.agent_type, inspection),
4556
+ completedAt: inspection.completedAt
4557
+ };
4558
+ return { sequence: nextSequence };
4559
+ }
4294
4560
  async _replayAgentToolRuns(connection) {
4295
4561
  const rows = this.sql`
4296
4562
  SELECT run_id, parent_tool_call_id, agent_type, input_preview, status,
@@ -4323,6 +4589,10 @@ var Agent = class Agent extends Server {
4323
4589
  }
4324
4590
  }
4325
4591
  async _reconcileAgentToolRuns(options) {
4592
+ const reattachTimeoutMs = options?.reattachTimeoutMs ?? DEFAULT_AGENT_TOOL_REATTACH_TIMEOUT_MS;
4593
+ const startedAt = Date.now();
4594
+ const totalTimeoutMs = options?.totalRecoveryTimeoutMs ?? DEFAULT_AGENT_TOOL_RECOVERY_TOTAL_TIMEOUT_MS;
4595
+ const deadlineAt = totalTimeoutMs > 0 ? startedAt + totalTimeoutMs : Number.POSITIVE_INFINITY;
4326
4596
  const deferredFinishes = [];
4327
4597
  const rows = this.sql`
4328
4598
  SELECT run_id, parent_tool_call_id, agent_type, input_preview, status,
@@ -4332,43 +4602,158 @@ var Agent = class Agent extends Server {
4332
4602
  WHERE status IN ('starting', 'running')
4333
4603
  ORDER BY started_at ASC
4334
4604
  `;
4335
- for (const row of rows) {
4336
- let sequence = 1;
4337
- let completedAt;
4338
- let result;
4339
- try {
4340
- const child = await this._cf_resolveSubAgent(row.agent_type, row.run_id);
4341
- const inspection = await this._asAgentToolChildAdapter(child).inspectAgentToolRun(row.run_id);
4342
- try {
4343
- sequence = await this._broadcastAgentToolStoredChunks(row, sequence);
4344
- } catch {}
4345
- if (!inspection || inspection.status === "running" || inspection.status === "starting") result = {
4605
+ const runIds = options?.runIds !== void 0 ? new Set(options.runIds) : void 0;
4606
+ const recoveryRows = rows.filter((row) => !runIds || runIds.has(row.run_id));
4607
+ this._emit("agent_tool:recovery:begin", {
4608
+ runCount: recoveryRows.length,
4609
+ totalTimeoutMs
4610
+ });
4611
+ const finalizeRow = async (row, result, sequence, completedAt) => {
4612
+ this._emit("agent_tool:recovery:row", {
4613
+ runId: row.run_id,
4614
+ agentType: row.agent_type,
4615
+ status: result.status,
4616
+ reason: result.error,
4617
+ elapsedMs: Date.now() - startedAt
4618
+ });
4619
+ const deferredFinish = await this._finishAgentToolRun(this._agentToolRunInfoFromRow(row), result, {
4620
+ sequence,
4621
+ completedAt,
4622
+ deferFinishHook: options?.deferFinishHooks
4623
+ });
4624
+ if (deferredFinish) deferredFinishes.push(deferredFinish);
4625
+ };
4626
+ const reattachQueue = [];
4627
+ for (const row of recoveryRows) {
4628
+ const sequence = 1;
4629
+ const remainingMs = deadlineAt - Date.now();
4630
+ if (remainingMs <= 0) {
4631
+ this._emit("agent_tool:recovery:deadline", {
4632
+ runId: row.run_id,
4633
+ agentType: row.agent_type,
4634
+ elapsedMs: Date.now() - startedAt
4635
+ });
4636
+ await finalizeRow(row, {
4346
4637
  runId: row.run_id,
4347
4638
  agentType: row.agent_type,
4348
4639
  status: "interrupted",
4349
- error: "Agent tool run was still running, but live-tail reattachment is not supported in this runtime."
4350
- };
4351
- else {
4352
- result = this._terminalResultFromInspection(row.agent_type, inspection);
4353
- completedAt = inspection.completedAt;
4354
- }
4355
- } catch {
4356
- result = {
4640
+ error: "Agent tool run recovery deadline exceeded."
4641
+ }, sequence, void 0);
4642
+ continue;
4643
+ }
4644
+ const childTimeout = options?.childInspectionTimeoutMs ?? DEFAULT_AGENT_TOOL_RECOVERY_TIMEOUT_MS;
4645
+ const boundedChildTimeout = childTimeout > 0 ? Math.min(childTimeout, remainingMs) : remainingMs;
4646
+ const recovery = await this._inspectAgentToolRunForRecovery(row, sequence, boundedChildTimeout);
4647
+ if (recovery.status !== "inspected") {
4648
+ await finalizeRow(row, {
4357
4649
  runId: row.run_id,
4358
4650
  agentType: row.agent_type,
4359
4651
  status: "interrupted",
4360
- error: "Agent tool run could not be inspected during parent recovery."
4361
- };
4652
+ error: recovery.status === "timed-out" ? "Agent tool run inspection timed out during parent recovery." : "Agent tool run could not be inspected during parent recovery."
4653
+ }, sequence, void 0);
4654
+ continue;
4362
4655
  }
4363
- const deferredFinish = await this._finishAgentToolRun(this._agentToolRunInfoFromRow(row), result, {
4364
- sequence,
4365
- completedAt,
4366
- deferFinishHook: options?.deferFinishHooks
4367
- });
4368
- if (deferredFinish) deferredFinishes.push(deferredFinish);
4656
+ const inspection = recovery.inspection;
4657
+ const stillRunning = !inspection || inspection.status === "running" || inspection.status === "starting";
4658
+ if (stillRunning && typeof recovery.adapter.tailAgentToolRun === "function") {
4659
+ reattachQueue.push({
4660
+ row,
4661
+ adapter: recovery.adapter
4662
+ });
4663
+ continue;
4664
+ }
4665
+ let sequenceAfterReplay = sequence;
4666
+ try {
4667
+ sequenceAfterReplay = await this._broadcastAgentToolStoredChunksFromAdapter(recovery.adapter, row, sequence, void 0, void 0, boundedChildTimeout);
4668
+ } catch {}
4669
+ if (stillRunning) await finalizeRow(row, {
4670
+ runId: row.run_id,
4671
+ agentType: row.agent_type,
4672
+ status: "interrupted",
4673
+ error: "Agent tool run was still running, but live-tail reattachment is not supported in this runtime."
4674
+ }, sequenceAfterReplay, void 0);
4675
+ else await finalizeRow(row, this._terminalResultFromInspection(row.agent_type, inspection), sequenceAfterReplay, inspection.completedAt);
4369
4676
  }
4677
+ await Promise.all(reattachQueue.map(async ({ row, adapter }) => {
4678
+ const reattach = await this._reattachAgentToolRunToTerminal(adapter, row, 1, reattachTimeoutMs);
4679
+ await finalizeRow(row, reattach.result ?? {
4680
+ runId: row.run_id,
4681
+ agentType: row.agent_type,
4682
+ status: "interrupted",
4683
+ error: "Agent tool run was still running and did not reach a terminal result within the re-attach budget."
4684
+ }, reattach.sequence, reattach.completedAt);
4685
+ }));
4686
+ this._emit("agent_tool:recovery:complete", {
4687
+ runCount: recoveryRows.length,
4688
+ elapsedMs: Date.now() - startedAt
4689
+ });
4370
4690
  return deferredFinishes;
4371
4691
  }
4692
+ async _inspectAgentToolRunForRecovery(row, _sequence, timeoutMs = DEFAULT_AGENT_TOOL_RECOVERY_TIMEOUT_MS) {
4693
+ const inspect = (async () => {
4694
+ const child = await this._cf_resolveSubAgent(row.agent_type, row.run_id);
4695
+ const adapter = this._asAgentToolChildAdapter(child);
4696
+ return {
4697
+ status: "inspected",
4698
+ adapter,
4699
+ inspection: await adapter.inspectAgentToolRun(row.run_id)
4700
+ };
4701
+ })().catch(() => ({ status: "failed" }));
4702
+ if (timeoutMs <= 0) return inspect;
4703
+ let timeoutId;
4704
+ const timeout = new Promise((resolve) => {
4705
+ timeoutId = setTimeout(() => {
4706
+ resolve({ status: "timed-out" });
4707
+ }, timeoutMs);
4708
+ });
4709
+ const result = await Promise.race([inspect, timeout]);
4710
+ if (timeoutId !== void 0) clearTimeout(timeoutId);
4711
+ return result;
4712
+ }
4713
+ _scheduleAgentToolRunRecovery(options) {
4714
+ if (this._agentToolRunRecoveryPromise) return this._agentToolRunRecoveryPromise;
4715
+ if (options?.runIds && options.runIds.length === 0) return Promise.resolve();
4716
+ const recovery = (async () => {
4717
+ await new Promise((resolve) => setTimeout(resolve, 0));
4718
+ const recoveredAgentToolFinishes = await this._reconcileAgentToolRuns({
4719
+ deferFinishHooks: true,
4720
+ childInspectionTimeoutMs: options?.childInspectionTimeoutMs,
4721
+ totalRecoveryTimeoutMs: options?.totalRecoveryTimeoutMs,
4722
+ reattachTimeoutMs: options?.reattachTimeoutMs,
4723
+ runIds: options?.runIds
4724
+ });
4725
+ await this._runDeferredAgentToolFinishHooks(recoveredAgentToolFinishes);
4726
+ })().catch(async (error) => {
4727
+ this._emit("agent_tool:recovery:failed", { error: error instanceof Error ? error.message : String(error) });
4728
+ try {
4729
+ await this.onError(error);
4730
+ } catch {}
4731
+ }).finally(() => {
4732
+ this._agentToolRunRecoveryPromise = void 0;
4733
+ });
4734
+ this._agentToolRunRecoveryPromise = recovery;
4735
+ this.ctx.waitUntil(recovery);
4736
+ return recovery;
4737
+ }
4738
+ _agentToolRunRecoveryRunIds() {
4739
+ return this.sql`
4740
+ SELECT run_id
4741
+ FROM cf_agent_tool_runs
4742
+ WHERE status IN ('starting', 'running')
4743
+ ORDER BY started_at ASC
4744
+ `.map((row) => row.run_id);
4745
+ }
4746
+ async _getAgentToolChunksForRecovery(adapter, runId, timeoutMs) {
4747
+ const chunks = adapter.getAgentToolChunks(runId).catch(() => void 0);
4748
+ if (timeoutMs === void 0 || timeoutMs <= 0) return chunks;
4749
+ let timeoutId;
4750
+ const timeout = new Promise((resolve) => {
4751
+ timeoutId = setTimeout(() => resolve(void 0), timeoutMs);
4752
+ });
4753
+ const result = await Promise.race([chunks, timeout]);
4754
+ if (timeoutId !== void 0) clearTimeout(timeoutId);
4755
+ return result;
4756
+ }
4372
4757
  /**
4373
4758
  * Shared facet resolution — takes a CamelCase class name string
4374
4759
  * (matching `ctx.exports`) rather than a class reference. Both
@@ -5360,7 +5745,26 @@ var Agent = class Agent extends Server {
5360
5745
  async addMcpServer(serverName, urlOrBinding, callbackHostOrOptions, agentsPrefix, options) {
5361
5746
  const isHttpTransport = typeof urlOrBinding === "string";
5362
5747
  const normalizedUrl = isHttpTransport ? new URL(urlOrBinding).href : void 0;
5363
- const existingServer = this.mcp.listServers().find((s) => s.name === serverName && (!isHttpTransport || new URL(s.server_url).href === normalizedUrl));
5748
+ let requestedId;
5749
+ if (typeof callbackHostOrOptions === "object" && callbackHostOrOptions !== null && typeof callbackHostOrOptions.id === "string") {
5750
+ const rawId = callbackHostOrOptions.id;
5751
+ requestedId = normalizeServerId(rawId);
5752
+ }
5753
+ const allServers = this.mcp.listServers();
5754
+ const existingServer = allServers.find((s) => s.name === serverName && (!isHttpTransport || new URL(s.server_url).href === normalizedUrl));
5755
+ if (requestedId) {
5756
+ const idConflict = allServers.find((s) => {
5757
+ if (s.id !== requestedId) return false;
5758
+ if (s.name !== serverName) return true;
5759
+ if (isHttpTransport) return new URL(s.server_url).href !== normalizedUrl;
5760
+ return false;
5761
+ });
5762
+ if (idConflict) throw new Error(`MCP server id "${requestedId}" is already in use by server "${idConflict.name}" (${idConflict.server_url}). Stable ids must be unique per (name, url).`);
5763
+ if (existingServer && existingServer.id !== requestedId) {
5764
+ await this.mcp.migrateServerId(existingServer.id, requestedId, this.name);
5765
+ existingServer.id = requestedId;
5766
+ }
5767
+ }
5364
5768
  if (existingServer && this.mcp.mcpConnections[existingServer.id]) {
5365
5769
  const conn = this.mcp.mcpConnections[existingServer.id];
5366
5770
  if (conn.connectionState === MCPConnectionState.AUTHENTICATING && conn.options.transport.authProvider?.authUrl) return {
@@ -5377,7 +5781,7 @@ var Agent = class Agent extends Server {
5377
5781
  if (typeof urlOrBinding !== "string") {
5378
5782
  const rpcOpts = callbackHostOrOptions;
5379
5783
  const normalizedName = serverName.toLowerCase().replace(/\s+/g, "-");
5380
- const reconnectId = existingServer?.id;
5784
+ const reconnectId = requestedId ?? existingServer?.id;
5381
5785
  const { id } = await this.mcp.connect(`${RPC_DO_PREFIX}${normalizedName}`, {
5382
5786
  reconnect: reconnectId ? { id: reconnectId } : void 0,
5383
5787
  transport: {
@@ -5434,7 +5838,7 @@ var Agent = class Agent extends Server {
5434
5838
  const normalizedHost = resolvedCallbackHost.replace(/\/$/, "");
5435
5839
  callbackUrl = resolvedCallbackPath ? `${normalizedHost}/${resolvedCallbackPath.replace(/^\//, "")}` : `${normalizedHost}/${resolvedAgentsPrefix}/${camelCaseToKebabCase(this._ParentClass.name)}/${this.name}/callback`;
5436
5840
  }
5437
- const id = nanoid(8);
5841
+ const id = requestedId ?? nanoid(8);
5438
5842
  let authProvider;
5439
5843
  if (callbackUrl) {
5440
5844
  authProvider = this.createMcpOAuthProvider(callbackUrl);
@@ -5766,6 +6170,6 @@ var StreamingResponse = class {
5766
6170
  }
5767
6171
  };
5768
6172
  //#endregion
5769
- 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 };
6173
+ export { Agent, DEFAULT_AGENT_STATIC_OPTIONS, DurableObjectOAuthClientProvider, MCP_SERVER_ID_MAX_LENGTH, MessageType, SUB_PREFIX, SqlError, StreamingResponse, __DO_NOT_USE_WILL_BREAK__agentContext, callable, createHeaderBasedEmailResolver, getAgentByName, getCurrentAgent, getSubAgentByName, normalizeServerId, parseSubAgentPath, routeAgentEmail, routeAgentRequest, routeSubAgentRequest, unstable_callable };
5770
6174
 
5771
6175
  //# sourceMappingURL=index.js.map