agents 0.14.0 → 0.14.2

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 (88) hide show
  1. package/dist/{agent-tool-types-LInzZfLo.d.ts → agent-tool-types-V25Z_HcX.d.ts} +275 -122
  2. package/dist/agent-tool-types.d.ts +13 -11
  3. package/dist/{agent-tools-BAdX1vdI.js → agent-tools-3zLG7MgA.js} +49 -7
  4. package/dist/agent-tools-3zLG7MgA.js.map +1 -0
  5. package/dist/{agent-tools-BE9xosUG.d.ts → agent-tools-C-9s151X.d.ts} +2 -2
  6. package/dist/agent-tools.d.ts +13 -11
  7. package/dist/agent-tools.js +8 -3
  8. package/dist/agent-tools.js.map +1 -1
  9. package/dist/browser/ai.d.ts +1 -1
  10. package/dist/browser/ai.js +1 -1
  11. package/dist/browser/ai.js.map +1 -1
  12. package/dist/browser/index.d.ts +1 -1
  13. package/dist/browser/index.js +1 -1
  14. package/dist/browser/tanstack-ai.d.ts +1 -1
  15. package/dist/browser/tanstack-ai.js +1 -1
  16. package/dist/browser/tanstack-ai.js.map +1 -1
  17. package/dist/chat/index.d.ts +115 -8
  18. package/dist/chat/index.js +10 -6
  19. package/dist/chat/index.js.map +1 -1
  20. package/dist/chat-sdk/index.d.ts +5 -5
  21. package/dist/chat-sdk/index.js +2 -2
  22. package/dist/chat-sdk/index.js.map +1 -1
  23. package/dist/{classPrivateFieldGet2-Evpt0SEr.js → classPrivateFieldGet2-Beqsfu2Z.js} +5 -5
  24. package/dist/{classPrivateMethodInitSpec-bG0tD96O.js → classPrivateMethodInitSpec-B5ko1s2R.js} +2 -2
  25. package/dist/cli/index.js.map +1 -1
  26. package/dist/{client-NradHZZz.js → client-FUizKzj2.js} +94 -21
  27. package/dist/client-FUizKzj2.js.map +1 -0
  28. package/dist/client.d.ts +1 -1
  29. package/dist/{compaction-helpers-DpP_XP9J.d.ts → compaction-helpers-BEUILPss.d.ts} +1 -1
  30. package/dist/{compaction-helpers-BjT2NKRZ.js → compaction-helpers-iiKMr2TQ.js} +1 -1
  31. package/dist/{compaction-helpers-BjT2NKRZ.js.map → compaction-helpers-iiKMr2TQ.js.map} +1 -1
  32. package/dist/{do-oauth-client-provider-CPm9rK5I.d.ts → do-oauth-client-provider-D4ZwyBDu.d.ts} +21 -1
  33. package/dist/{email-1fTSJwPm.d.ts → email-CL27preh.d.ts} +1 -1
  34. package/dist/email.d.ts +2 -2
  35. package/dist/email.js.map +1 -1
  36. package/dist/experimental/memory/session/index.d.ts +1 -1
  37. package/dist/experimental/memory/session/index.js +1 -1
  38. package/dist/experimental/memory/session/index.js.map +1 -1
  39. package/dist/experimental/memory/utils/index.d.ts +1 -1
  40. package/dist/experimental/memory/utils/index.js +2 -2
  41. package/dist/experimental/webmcp.js.map +1 -1
  42. package/dist/{index-Brdu5nMI.d.ts → index-CPe1OtI0.d.ts} +17 -1
  43. package/dist/index.d.ts +71 -69
  44. package/dist/index.js +288 -84
  45. package/dist/index.js.map +1 -1
  46. package/dist/{internal_context-CcZy2Em7.d.ts → internal_context-Dg4Cgjcu.d.ts} +1 -1
  47. package/dist/internal_context.d.ts +1 -1
  48. package/dist/mcp/client.d.ts +14 -14
  49. package/dist/mcp/client.js +1 -1
  50. package/dist/mcp/do-oauth-client-provider.d.ts +1 -1
  51. package/dist/mcp/do-oauth-client-provider.js +143 -17
  52. package/dist/mcp/do-oauth-client-provider.js.map +1 -1
  53. package/dist/mcp/index.d.ts +30 -30
  54. package/dist/mcp/index.js +1 -1
  55. package/dist/mcp/index.js.map +1 -1
  56. package/dist/mcp/x402.js.map +1 -1
  57. package/dist/observability/index.d.ts +1 -1
  58. package/dist/observability/index.js.map +1 -1
  59. package/dist/react.d.ts +3 -3
  60. package/dist/react.js +1 -1
  61. package/dist/react.js.map +1 -1
  62. package/dist/{retries-ClWwxADl.d.ts → retries-CF_HKSlJ.d.ts} +1 -1
  63. package/dist/retries.d.ts +1 -1
  64. package/dist/schedule.js.map +1 -1
  65. package/dist/serializable.d.ts +1 -1
  66. package/dist/{shared-CpY1FLvm.d.ts → shared-4CAYLCTO.d.ts} +1 -1
  67. package/dist/{shared-DdOn6sp4.js → shared-wyII629d.js} +3 -3
  68. package/dist/{shared-DdOn6sp4.js.map → shared-wyII629d.js.map} +1 -1
  69. package/dist/skills/index.js +4 -4
  70. package/dist/skills/index.js.map +1 -1
  71. package/dist/sub-routing.d.ts +6 -6
  72. package/dist/sub-routing.js.map +1 -1
  73. package/dist/{tool-output-truncation-BF4AZQlw.js → tool-output-truncation-CNnnGZQ3.js} +1 -1
  74. package/dist/{tool-output-truncation-BF4AZQlw.js.map → tool-output-truncation-CNnnGZQ3.js.map} +1 -1
  75. package/dist/{types-B0GymtN_.d.ts → types-6Zo2zfoO.d.ts} +1 -1
  76. package/dist/types.d.ts +1 -1
  77. package/dist/utils.js.map +1 -1
  78. package/dist/vite.d.ts +15 -4
  79. package/dist/vite.js +37 -7
  80. package/dist/vite.js.map +1 -1
  81. package/dist/{workflow-types-DPkuBi--.d.ts → workflow-types-SrZK_o9p.d.ts} +1 -1
  82. package/dist/workflow-types.d.ts +1 -1
  83. package/dist/workflows.d.ts +11 -3
  84. package/dist/workflows.js +48 -22
  85. package/dist/workflows.js.map +1 -1
  86. package/package.json +14 -23
  87. package/dist/agent-tools-BAdX1vdI.js.map +0 -1
  88. package/dist/client-NradHZZz.js.map +0 -1
package/dist/index.js CHANGED
@@ -2,10 +2,10 @@ import { __DO_NOT_USE_WILL_BREAK__agentContext } from "./internal_context.js";
2
2
  import { MessageType } from "./types.js";
3
3
  import { camelCaseToKebabCase, isInternalJsStubProp } from "./utils.js";
4
4
  import { createHeaderBasedEmailResolver, signAgentHeaders } from "./email.js";
5
- import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-Evpt0SEr.js";
5
+ import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-Beqsfu2Z.js";
6
6
  import { SUB_PREFIX, getSubAgentByName, parseSubAgentPath, routeSubAgentRequest } from "./sub-routing.js";
7
7
  import { isErrorRetryable, tryN, validateRetryOptions } from "./retries.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";
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-FUizKzj2.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";
@@ -116,7 +116,8 @@ function getNextCronTime(cron) {
116
116
  const DEFAULT_KEEP_ALIVE_INTERVAL_MS = 3e4;
117
117
  const DEFAULT_AGENT_TOOL_RECOVERY_TIMEOUT_MS = 2e3;
118
118
  const DEFAULT_AGENT_TOOL_RECOVERY_TOTAL_TIMEOUT_MS = 5e3;
119
- const DEFAULT_AGENT_TOOL_REATTACH_TIMEOUT_MS = 12e4;
119
+ const DEFAULT_AGENT_TOOL_REATTACH_NO_PROGRESS_TIMEOUT_MS = 12e4;
120
+ const DEFAULT_AGENT_TOOL_REATTACH_MAX_WINDOW_MS = Number.POSITIVE_INFINITY;
120
121
  const SUB_AGENT_IDENTITY_VERSION_LEGACY = "legacy";
121
122
  const SUB_AGENT_IDENTITY_VERSION_PATH_V2 = "path-v2";
122
123
  const SUB_AGENT_IDENTITY_PATH_V2_PREFIX = "cf-agents:v2:";
@@ -126,7 +127,7 @@ const SUB_AGENT_IDENTITY_PATH_V2_PREFIX = "cf-agents:v2:";
126
127
  * The constructor stores this as a row in cf_agents_state and checks it
127
128
  * on wake to skip DDL on established DOs.
128
129
  */
129
- const CURRENT_SCHEMA_VERSION = 8;
130
+ const CURRENT_SCHEMA_VERSION = 9;
130
131
  const SCHEMA_VERSION_ROW_ID = "cf_schema_version";
131
132
  const STATE_ROW_ID = "cf_state_row_id";
132
133
  const STATE_WAS_CHANGED = "cf_state_was_changed";
@@ -275,7 +276,27 @@ const DEFAULT_AGENT_STATIC_OPTIONS = {
275
276
  * up. Bounds repeated retries of a `onFiberRecovered()` hook that keeps
276
277
  * throwing so a poison row cannot re-trigger forever across boots.
277
278
  */
278
- fiberRecoveryMaxAgeMs: 1440 * 60 * 1e3
279
+ fiberRecoveryMaxAgeMs: 1440 * 60 * 1e3,
280
+ /**
281
+ * No-progress budget (ms) for re-attaching to a still-running agent-tool
282
+ * child after a deploy / parent recovery (#1630). Bounds how long the parent
283
+ * waits with NO forward progress from the child; it resets on every forwarded
284
+ * chunk, so a child that keeps streaming is never abandoned mid-flight. Only a
285
+ * genuinely silent/hung child seals `interrupted` after a full window. Raise
286
+ * for children with long quiet stretches between outputs.
287
+ */
288
+ agentToolReattachNoProgressTimeoutMs: DEFAULT_AGENT_TOOL_REATTACH_NO_PROGRESS_TIMEOUT_MS,
289
+ /**
290
+ * Optional hard wall-clock ceiling (ms) on a single agent-tool re-attach
291
+ * (#1630). Caps the total wait even as the no-progress budget re-arms across
292
+ * stream-closes. Defaults to `Infinity` (no implicit cap), mirroring
293
+ * chat-recovery's `maxRecoveryWork` (#1672): a healthy, still-advancing child
294
+ * is followed for as long as it makes progress — a hung child is bounded by
295
+ * the no-progress budget, and a content-runaway by the child's own
296
+ * `maxRecoveryWork` / `shouldKeepRecovering`. Set a finite value to impose a
297
+ * wall-clock cap (which also tears the child down on `window-exceeded`).
298
+ */
299
+ agentToolReattachMaxWindowMs: DEFAULT_AGENT_TOOL_REATTACH_MAX_WINDOW_MS
279
300
  };
280
301
  /**
281
302
  * Parse the raw `retry_options` TEXT column from a SQLite row into a
@@ -299,17 +320,34 @@ function resolveRetryConfig(taskRetry, defaults) {
299
320
  };
300
321
  }
301
322
  /**
302
- * Whether an error is a Durable Object reset caused by a code update (deploy).
323
+ * Whether an error is a transient "superseded isolate" failure the invocation
324
+ * is running on an isolate the platform has replaced with a new version (a
325
+ * deploy / code update). For the rest of that invocation every operation throws
326
+ * the same error (code never reloads mid-invocation), so in-process retries are
327
+ * futile; but the next fresh invocation runs the new code and succeeds.
328
+ *
329
+ * workerd surfaces this as a plain `Error` with one of a few messages, all the
330
+ * same failure class — a message match is the only signal:
331
+ * - "Durable Object reset because its code was updated." (DO storage op on a
332
+ * superseded isolate / deploy bounce)
333
+ * - "This script has been upgraded. Please send a new request to connect to
334
+ * the new version." (a stub/connection to a superseded script; the message
335
+ * literally instructs the caller to retry on the new version)
303
336
  *
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.
337
+ * The match stays close to the verbatim platform strings (rather than a loose
338
+ * "upgraded"/"reset" substring) so an ordinary application error that happens
339
+ * to mention those words is NOT misclassified as a supersede a false positive
340
+ * would defer + re-run a genuinely-failing callback on the platform's alarm
341
+ * retries instead of abandoning it.
342
+ *
343
+ * NOTE: "Network connection lost." is deliberately NOT included — it is a
344
+ * connection error, not an isolate replacement, and may succeed on in-process
345
+ * retry (it is gated by the CF `retryable` property via `isErrorRetryable`),
346
+ * so it stays on the normal retry path rather than the immediate-defer path.
309
347
  */
310
348
  function isDurableObjectCodeUpdateReset(error) {
311
349
  const message = error instanceof Error ? error.message : typeof error === "string" ? error : "";
312
- return /reset because its code was updated/i.test(message);
350
+ return /reset because its code was updated|this script has been upgraded/i.test(message);
313
351
  }
314
352
  function getCurrentAgent() {
315
353
  const store = __DO_NOT_USE_WILL_BREAK__agentContext.getStore();
@@ -411,7 +449,9 @@ var Agent = class Agent extends Server {
411
449
  },
412
450
  fiberRecoveryHookTimeoutMs: ctor.options?.fiberRecoveryHookTimeoutMs ?? DEFAULT_AGENT_STATIC_OPTIONS.fiberRecoveryHookTimeoutMs,
413
451
  fiberRecoveryScanDeadlineMs: ctor.options?.fiberRecoveryScanDeadlineMs ?? DEFAULT_AGENT_STATIC_OPTIONS.fiberRecoveryScanDeadlineMs,
414
- fiberRecoveryMaxAgeMs: ctor.options?.fiberRecoveryMaxAgeMs ?? DEFAULT_AGENT_STATIC_OPTIONS.fiberRecoveryMaxAgeMs
452
+ fiberRecoveryMaxAgeMs: ctor.options?.fiberRecoveryMaxAgeMs ?? DEFAULT_AGENT_STATIC_OPTIONS.fiberRecoveryMaxAgeMs,
453
+ agentToolReattachNoProgressTimeoutMs: ctor.options?.agentToolReattachNoProgressTimeoutMs ?? DEFAULT_AGENT_STATIC_OPTIONS.agentToolReattachNoProgressTimeoutMs,
454
+ agentToolReattachMaxWindowMs: ctor.options?.agentToolReattachMaxWindowMs ?? DEFAULT_AGENT_STATIC_OPTIONS.agentToolReattachMaxWindowMs
415
455
  };
416
456
  return this._cachedOptions;
417
457
  }
@@ -638,6 +678,8 @@ var Agent = class Agent extends Server {
638
678
  summary TEXT,
639
679
  output_json TEXT,
640
680
  error_message TEXT,
681
+ interrupted_reason TEXT,
682
+ child_still_running INTEGER,
641
683
  display_metadata TEXT,
642
684
  display_order INTEGER NOT NULL DEFAULT 0,
643
685
  started_at INTEGER NOT NULL,
@@ -649,6 +691,8 @@ var Agent = class Agent extends Server {
649
691
  ON cf_agent_tool_runs(parent_tool_call_id, display_order)
650
692
  `;
651
693
  addColumnIfNotExists("ALTER TABLE cf_agent_tool_runs ADD COLUMN output_json TEXT");
694
+ addColumnIfNotExists("ALTER TABLE cf_agent_tool_runs ADD COLUMN interrupted_reason TEXT");
695
+ addColumnIfNotExists("ALTER TABLE cf_agent_tool_runs ADD COLUMN child_still_running INTEGER");
652
696
  this.sql`
653
697
  INSERT OR REPLACE INTO cf_agents_state (id, state)
654
698
  VALUES (${SCHEMA_VERSION_ROW_ID}, ${String(CURRENT_SCHEMA_VERSION)})
@@ -663,12 +707,12 @@ var Agent = class Agent extends Server {
663
707
  this._rawStateAccessors = /* @__PURE__ */ new WeakMap();
664
708
  this._persistenceHookMode = "none";
665
709
  this._isFacet = false;
666
- this._suppressProtocolBroadcasts = false;
667
710
  this._protocolBroadcastExcludeIds = /* @__PURE__ */ new Set();
668
711
  this._cf_virtualSubAgentConnections = /* @__PURE__ */ new Map();
669
712
  this._parentPath = [];
670
713
  this._insideOnStart = false;
671
714
  this._warnedScheduleInOnStart = /* @__PURE__ */ new Set();
715
+ this._warnedChatRecoveryInOnStart = false;
672
716
  this._keepAliveRefs = 0;
673
717
  this._facetKeepAliveTokens = /* @__PURE__ */ new Set();
674
718
  this._runFiberActiveFibers = /* @__PURE__ */ new Set();
@@ -915,6 +959,7 @@ var Agent = class Agent extends Server {
915
959
  this._checkOrphanedWorkflows();
916
960
  await this._checkRunFibers();
917
961
  const startupAgentToolRunIds = this._agentToolRunRecoveryRunIds();
962
+ const chatRecoveryBefore = this.chatRecovery;
918
963
  this._insideOnStart = true;
919
964
  this._warnedScheduleInOnStart.clear();
920
965
  let result;
@@ -923,6 +968,12 @@ var Agent = class Agent extends Server {
923
968
  } finally {
924
969
  this._insideOnStart = false;
925
970
  }
971
+ const chatRecoveryAfter = this.chatRecovery;
972
+ const chatRecoveryAfterMatters = typeof chatRecoveryAfter === "object" && chatRecoveryAfter !== null || chatRecoveryAfter === true;
973
+ if (!this._warnedChatRecoveryInOnStart && chatRecoveryBefore !== chatRecoveryAfter && chatRecoveryAfterMatters) {
974
+ this._warnedChatRecoveryInOnStart = true;
975
+ console.warn("[Agent] `chatRecovery` was assigned during onStart(). Chat recovery evaluates its budgets (and may seal an interrupted turn, firing onExhausted) on wake BEFORE onStart() runs, so a config set here is applied too late and the built-in defaults are used for the recovery that matters. Assign `chatRecovery` as a class field or in the constructor instead.");
976
+ }
926
977
  this._scheduleAgentToolRunRecovery({ runIds: startupAgentToolRunIds });
927
978
  return result;
928
979
  });
@@ -959,7 +1010,6 @@ var Agent = class Agent extends Server {
959
1010
  * @param excludeIds Additional connection IDs to exclude (e.g. the source)
960
1011
  */
961
1012
  _broadcastProtocol(msg, excludeIds = []) {
962
- if (this._suppressProtocolBroadcasts) return;
963
1013
  const exclude = [...excludeIds, ...this._protocolBroadcastExcludeIds];
964
1014
  for (const conn of this.getConnections()) if (!this.isConnectionProtocolEnabled(conn)) exclude.push(conn.id);
965
1015
  this.broadcast(msg, exclude);
@@ -3370,6 +3420,7 @@ var Agent = class Agent extends Server {
3370
3420
  if (this._isFacet) {
3371
3421
  const stored = this._cf_virtualSubAgentConnections.get(id);
3372
3422
  if (stored) return this._cf_createSubAgentBridgeConnection(stored.bridge, stored.meta);
3423
+ return;
3373
3424
  }
3374
3425
  const connection = super.getConnection(id);
3375
3426
  if (!connection || this._cf_connectionHasSubAgentTarget(connection)) return;
@@ -3378,6 +3429,7 @@ var Agent = class Agent extends Server {
3378
3429
  *getConnections(tag) {
3379
3430
  if (this._isFacet) {
3380
3431
  for (const stored of this._cf_virtualSubAgentConnections.values()) if (!tag || stored.meta.tags.includes(tag)) yield this._cf_createSubAgentBridgeConnection(stored.bridge, stored.meta);
3432
+ return;
3381
3433
  }
3382
3434
  for (const connection of super.getConnections(tag)) {
3383
3435
  if (this._cf_connectionHasSubAgentTarget(connection)) continue;
@@ -3830,12 +3882,7 @@ var Agent = class Agent extends Server {
3830
3882
  this.ctx.storage.put("cf_agents_facet_name", name),
3831
3883
  this.ctx.storage.put("cf_agents_parent_path", parentPath)
3832
3884
  ]);
3833
- this._suppressProtocolBroadcasts = true;
3834
- try {
3835
- await this.__unsafe_ensureInitialized();
3836
- } finally {
3837
- this._suppressProtocolBroadcasts = false;
3838
- }
3885
+ await this.__unsafe_ensureInitialized();
3839
3886
  }
3840
3887
  get name() {
3841
3888
  return this._facetName ?? logicalNameFromPathV2Identity(super.name) ?? super.name;
@@ -4005,10 +4052,12 @@ var Agent = class Agent extends Server {
4005
4052
  } catch {}
4006
4053
  return this._resultFromAgentToolRow(existing);
4007
4054
  }
4055
+ let reattachReason;
4056
+ let childTornDown = false;
4008
4057
  try {
4009
4058
  const child = await this.subAgent(cls, runId);
4010
4059
  const adapter = this._asAgentToolChildAdapter(child);
4011
- const reattach = await this._reattachAgentToolRunToTerminal(adapter, existing, 1);
4060
+ const reattach = await this._reattachAgentToolRunToTerminal(adapter, existing, 1, this._resolvedOptions.agentToolReattachNoProgressTimeoutMs, this._resolvedOptions.agentToolReattachMaxWindowMs);
4012
4061
  if (reattach.result) {
4013
4062
  await this._finishAgentToolRun(this._agentToolRunInfoFromRow(existing), reattach.result, {
4014
4063
  sequence: reattach.sequence,
@@ -4016,8 +4065,13 @@ var Agent = class Agent extends Server {
4016
4065
  });
4017
4066
  return reattach.result;
4018
4067
  }
4068
+ reattachReason = reattach.reason;
4069
+ childTornDown = await this._teardownGivenUpAgentToolChild(adapter, runId, reattach.reason);
4019
4070
  } 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.");
4071
+ return await this._replayAndInterruptAgentToolRun(existing, this._interruptedMessageForReason(reattachReason), {
4072
+ reason: reattachReason,
4073
+ childStillRunning: !childTornDown
4074
+ });
4021
4075
  }
4022
4076
  const displayOrder = options.displayOrder ?? 0;
4023
4077
  const inputPreview = options.inputPreview ?? this._defaultAgentToolPreview(options.input);
@@ -4111,7 +4165,7 @@ var Agent = class Agent extends Server {
4111
4165
  try {
4112
4166
  if (adapter.tailAgentToolRun) {
4113
4167
  const stream = await adapter.tailAgentToolRun(runId, { afterSequence: -1 });
4114
- sequence = await this._forwardAgentToolStream(stream, options.parentToolCallId, runId, sequence, options.signal);
4168
+ sequence = (await this._forwardAgentToolStream(stream, options.parentToolCallId, runId, sequence, options.signal)).next;
4115
4169
  } else {
4116
4170
  const chunks = await adapter.getAgentToolChunks(runId);
4117
4171
  sequence = this._broadcastAgentToolChunks(options.parentToolCallId, runId, chunks, sequence);
@@ -4215,13 +4269,28 @@ var Agent = class Agent extends Server {
4215
4269
  _readAgentToolRun(runId) {
4216
4270
  return this.sql`
4217
4271
  SELECT run_id, parent_tool_call_id, agent_type, input_preview, status,
4218
- summary, output_json, error_message, display_metadata, display_order,
4272
+ summary, output_json, error_message, interrupted_reason,
4273
+ child_still_running, display_metadata, display_order,
4219
4274
  started_at, completed_at
4220
4275
  FROM cf_agent_tool_runs
4221
4276
  WHERE run_id = ${runId}
4222
4277
  LIMIT 1
4223
4278
  `[0] ?? null;
4224
4279
  }
4280
+ /**
4281
+ * Reconstruct the typed interrupted cause (`reason` / `childStillRunning`,
4282
+ * #1630 follow-up) from a stored row so a row→result/event rebuild — e.g. a
4283
+ * reconnect replay — carries the same fields a live client saw. Only
4284
+ * `interrupted` rows store a cause; everything else yields `{}` (the columns
4285
+ * are cleared whenever a row settles to a hard terminal).
4286
+ */
4287
+ _agentToolInterruptedExtrasFromRow(row) {
4288
+ if (row.status !== "interrupted") return {};
4289
+ return {
4290
+ ...row.interrupted_reason !== null ? { reason: row.interrupted_reason } : {},
4291
+ ...row.child_still_running !== null ? { childStillRunning: row.child_still_running !== 0 } : {}
4292
+ };
4293
+ }
4225
4294
  _resultFromAgentToolRow(row) {
4226
4295
  const output = this._parseAgentToolJson(row.output_json);
4227
4296
  return {
@@ -4230,7 +4299,8 @@ var Agent = class Agent extends Server {
4230
4299
  status: row.status,
4231
4300
  ...output !== void 0 ? { output } : {},
4232
4301
  ...row.summary !== null ? { summary: row.summary } : {},
4233
- ...row.error_message !== null ? { error: row.error_message } : {}
4302
+ ...row.error_message !== null ? { error: row.error_message } : {},
4303
+ ...this._agentToolInterruptedExtrasFromRow(row)
4234
4304
  };
4235
4305
  }
4236
4306
  _agentToolRunInfoFromRow(row, status = row.status, completedAt = row.completed_at ?? void 0) {
@@ -4289,12 +4359,15 @@ var Agent = class Agent extends Server {
4289
4359
  }
4290
4360
  }
4291
4361
  _updateAgentToolTerminal(runId, result, completedAt = Date.now()) {
4362
+ const childStillRunning = result.childStillRunning === void 0 ? null : result.childStillRunning ? 1 : 0;
4292
4363
  this.sql`
4293
4364
  UPDATE cf_agent_tool_runs
4294
4365
  SET status = ${result.status},
4295
4366
  summary = ${result.summary ?? null},
4296
4367
  output_json = ${this._stringifyAgentToolOutput(result.output)},
4297
4368
  error_message = ${result.error ?? null},
4369
+ interrupted_reason = ${result.reason ?? null},
4370
+ child_still_running = ${childStillRunning},
4298
4371
  completed_at = ${completedAt}
4299
4372
  WHERE run_id = ${runId}
4300
4373
  AND status NOT IN ('completed', 'error', 'aborted')
@@ -4357,9 +4430,13 @@ var Agent = class Agent extends Server {
4357
4430
  if (!chunks) return sequence;
4358
4431
  return this._broadcastAgentToolChunks(row.parent_tool_call_id ?? void 0, row.run_id, chunks, sequence, replay, connection);
4359
4432
  }
4360
- async _forwardAgentToolStream(stream, parentToolCallId, runId, sequence, signal) {
4433
+ async _forwardAgentToolStream(stream, parentToolCallId, runId, sequence, signal, idleTimeoutMs) {
4361
4434
  let next = sequence;
4362
- if (signal?.aborted) return next;
4435
+ if (signal?.aborted) return {
4436
+ next,
4437
+ ended: "aborted"
4438
+ };
4439
+ let ended = "done";
4363
4440
  const reader = stream.getReader();
4364
4441
  const decoder = new TextDecoder();
4365
4442
  let bufferedBytes = "";
@@ -4373,6 +4450,17 @@ var Agent = class Agent extends Server {
4373
4450
  abortListener = () => resolveAbort?.();
4374
4451
  signal.addEventListener("abort", abortListener, { once: true });
4375
4452
  }
4453
+ const idleEnabled = typeof idleTimeoutMs === "number" && idleTimeoutMs > 0 && Number.isFinite(idleTimeoutMs);
4454
+ let resolveIdle;
4455
+ let idleTimer;
4456
+ const idlePromise = new Promise((resolve) => {
4457
+ resolveIdle = resolve;
4458
+ });
4459
+ const armIdle = () => {
4460
+ if (!idleEnabled) return;
4461
+ if (idleTimer !== void 0) clearTimeout(idleTimer);
4462
+ idleTimer = setTimeout(() => resolveIdle?.(), idleTimeoutMs);
4463
+ };
4376
4464
  let forwardedSinceProgress = false;
4377
4465
  try {
4378
4466
  const forwardChunk = (chunk) => {
@@ -4382,6 +4470,7 @@ var Agent = class Agent extends Server {
4382
4470
  body: chunk.body
4383
4471
  });
4384
4472
  forwardedSinceProgress = true;
4473
+ armIdle();
4385
4474
  };
4386
4475
  const forwardLine = (line) => {
4387
4476
  try {
@@ -4402,15 +4491,21 @@ var Agent = class Agent extends Server {
4402
4491
  bufferedBytes = "";
4403
4492
  }
4404
4493
  };
4494
+ armIdle();
4405
4495
  while (true) {
4406
4496
  const readPromise = reader.read();
4407
4497
  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") {
4498
+ const raced = await Promise.race([
4499
+ readPromise.then((result) => ({
4500
+ kind: "read",
4501
+ result
4502
+ })),
4503
+ abortPromise.then(() => ({ kind: "abort" })),
4504
+ idlePromise.then(() => ({ kind: "idle" }))
4505
+ ]);
4506
+ if (raced.kind === "abort" || raced.kind === "idle") {
4413
4507
  aborted = true;
4508
+ ended = raced.kind === "idle" ? "idle" : "aborted";
4414
4509
  break;
4415
4510
  }
4416
4511
  const { done, value } = raced.result;
@@ -4431,12 +4526,16 @@ var Agent = class Agent extends Server {
4431
4526
  }
4432
4527
  }
4433
4528
  } finally {
4529
+ if (idleTimer !== void 0) clearTimeout(idleTimer);
4434
4530
  if (abortListener && signal) signal.removeEventListener("abort", abortListener);
4435
4531
  if (!aborted) try {
4436
4532
  reader.releaseLock();
4437
4533
  } catch {}
4438
4534
  }
4439
- return next;
4535
+ return {
4536
+ next,
4537
+ ended
4538
+ };
4440
4539
  }
4441
4540
  /**
4442
4541
  * Hook invoked by `_forwardAgentToolStream` after a child produces output that
@@ -4473,7 +4572,9 @@ var Agent = class Agent extends Server {
4473
4572
  else if (result.status === "interrupted") this._broadcastAgentToolEvent(parentToolCallId, sequence, {
4474
4573
  kind: "interrupted",
4475
4574
  runId: result.runId,
4476
- error: result.error ?? "Agent tool run was interrupted"
4575
+ error: result.error ?? "Agent tool run was interrupted",
4576
+ ...result.reason !== void 0 ? { reason: result.reason } : {},
4577
+ ...result.childStillRunning !== void 0 ? { childStillRunning: result.childStillRunning } : {}
4477
4578
  }, replay, connection);
4478
4579
  else this._broadcastAgentToolEvent(parentToolCallId, sequence, {
4479
4580
  kind: "error",
@@ -4491,7 +4592,7 @@ var Agent = class Agent extends Server {
4491
4592
  if (!cls) throw new Error(`Agent tool class "${className}" is not exported.`);
4492
4593
  return cls;
4493
4594
  }
4494
- async _replayAndInterruptAgentToolRun(row, message) {
4595
+ async _replayAndInterruptAgentToolRun(row, message, extra) {
4495
4596
  let sequence = 1;
4496
4597
  try {
4497
4598
  sequence = await this._broadcastAgentToolStoredChunks(row, sequence);
@@ -4500,12 +4601,53 @@ var Agent = class Agent extends Server {
4500
4601
  runId: row.run_id,
4501
4602
  agentType: row.agent_type,
4502
4603
  status: "interrupted",
4503
- error: message
4604
+ error: message,
4605
+ ...extra?.reason !== void 0 ? { reason: extra.reason } : {},
4606
+ ...extra?.childStillRunning !== void 0 ? { childStillRunning: extra.childStillRunning } : {}
4504
4607
  };
4505
4608
  await this._finishAgentToolRun(this._agentToolRunInfoFromRow(row), result, { sequence });
4506
4609
  return result;
4507
4610
  }
4508
4611
  /**
4612
+ * Human-readable prose for an `interrupted` seal. Kept in sync with
4613
+ * {@link AgentToolInterruptedReason}; callers branch on the typed `reason`
4614
+ * field, not this string.
4615
+ */
4616
+ _interruptedMessageForReason(reason) {
4617
+ switch (reason) {
4618
+ case "no-progress": return "Agent tool run was still running but made no forward progress within the re-attach no-progress budget; the parent gave up.";
4619
+ case "window-exceeded": return "Agent tool run did not reach a terminal result within the maximum re-attach window; the parent gave up.";
4620
+ case "not-tailable": return "Agent tool run was still running, but live-tail reattachment is not supported in this runtime.";
4621
+ case "inspect-timeout": return "Agent tool run inspection timed out during parent recovery.";
4622
+ case "inspect-failed": return "Agent tool run could not be inspected during parent recovery.";
4623
+ case "recovery-deadline": return "Agent tool run recovery deadline exceeded.";
4624
+ default: return "Agent tool run was still running and did not reach a terminal result.";
4625
+ }
4626
+ }
4627
+ /**
4628
+ * Tear down a child agent-tool run the parent has genuinely given up on
4629
+ * (#1630 follow-up). Teardown is scoped to `window-exceeded` ONLY — the hard
4630
+ * ceiling, where the child has had its full recovery window and is therefore
4631
+ * truly exhausted, so cancelling it reclaims its fiber / keep-alive. Every
4632
+ * other give-up is deliberately left repairable: `no-progress` seals stay
4633
+ * SOFT (`interrupted`, `childStillRunning: true`) so a re-issue can still
4634
+ * re-attach and collect the child if it self-heals — tearing those down would
4635
+ * defeat the repair-on-re-issue path and convert a retryable interrupt into a
4636
+ * non-retryable `aborted`. Reasons where the child's state is unknown
4637
+ * (`inspect-*`, `recovery-deadline`, `not-tailable`) are also left alone.
4638
+ * Returns whether the child was torn down (so the caller reports
4639
+ * `childStillRunning: false`).
4640
+ */
4641
+ async _teardownGivenUpAgentToolChild(adapter, runId, reason) {
4642
+ if (reason !== "window-exceeded") return false;
4643
+ try {
4644
+ await adapter.cancelAgentToolRun(runId, `agent tool run given up by parent recovery: ${reason}`);
4645
+ return true;
4646
+ } catch {
4647
+ return false;
4648
+ }
4649
+ }
4650
+ /**
4509
4651
  * Re-attach to a still-running child agent-tool run and tail it to its real
4510
4652
  * terminal result, instead of abandoning it as `interrupted` (#1630). The
4511
4653
  * child is a separate facet with its own `chatRecovery`, so resolving it via
@@ -4513,54 +4655,98 @@ var Agent = class Agent extends Server {
4513
4655
  * its live stream (forwarding chunks to the parent's connections) until it
4514
4656
  * reaches terminal, then inspect for the collected result.
4515
4657
  *
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.
4658
+ * The wait is PROGRESS-KEYED, not a flat wall clock (which previously abandoned
4659
+ * healthy, still-advancing children whose recovery simply outran a fixed
4660
+ * budget). `noProgressTimeoutMs` bounds how long the parent waits with NO
4661
+ * forward progress; it is reset on every forwarded chunk. As long as the child
4662
+ * keeps streaming it is followed through to terminal. The loop also RE-ARMS
4663
+ * across stream-closes (a child re-evicted mid-recovery, or a tail that ends
4664
+ * before terminal) as long as the prior attempt made progress, so a child that
4665
+ * dies and recovers again during deploy churn is still collected. A genuinely
4666
+ * silent/hung child can never block recovery forever: it seals `interrupted`
4667
+ * after one `noProgressTimeoutMs` window. `maxWindowMs` is an OPTIONAL hard
4668
+ * wall-clock ceiling (default `Infinity` — uncapped, mirroring #1672's
4669
+ * `maxRecoveryWork`); set it finite to also bound a child that keeps
4670
+ * progressing, which seals `window-exceeded` and tears the child down.
4520
4671
  *
4521
4672
  * 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.
4673
+ * terminal status, plus the advanced broadcast `sequence`. Returns
4674
+ * `{ result: undefined }` when there is no `tailAgentToolRun` adapter, the
4675
+ * child makes no progress within a full no-progress window, or the ceiling is
4676
+ * reached while the child is still non-terminal — the caller then seals
4677
+ * `interrupted`.
4525
4678
  */
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();
4679
+ async _reattachAgentToolRunToTerminal(adapter, row, sequence, noProgressTimeoutMs = DEFAULT_AGENT_TOOL_REATTACH_NO_PROGRESS_TIMEOUT_MS, maxWindowMs = DEFAULT_AGENT_TOOL_REATTACH_MAX_WINDOW_MS) {
4680
+ if (typeof adapter.tailAgentToolRun !== "function") return {
4681
+ sequence,
4682
+ reason: "not-tailable"
4683
+ };
4532
4684
  this._emit("agent_tool:recovery:reattach", {
4533
4685
  runId: row.run_id,
4534
4686
  agentType: row.agent_type,
4535
- budgetMs
4687
+ budgetMs: noProgressTimeoutMs
4536
4688
  });
4537
- let nextSequence = sequence;
4538
- if (!controller.signal.aborted) try {
4539
- let afterSequence = -1;
4689
+ const collectTerminal = async (seq) => {
4690
+ let inspection = null;
4540
4691
  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;
4692
+ inspection = await adapter.inspectAgentToolRun(row.run_id);
4693
+ } catch {
4694
+ return null;
4695
+ }
4696
+ if (inspection && inspection.status !== "running" && inspection.status !== "starting") return {
4697
+ sequence: seq,
4698
+ result: this._terminalResultFromInspection(row.agent_type, inspection),
4699
+ completedAt: inspection.completedAt
4700
+ };
4701
+ return null;
4702
+ };
4703
+ let nextSequence = sequence;
4704
+ if (!(noProgressTimeoutMs > 0)) return await collectTerminal(nextSequence) ?? {
4705
+ sequence: nextSequence,
4706
+ reason: "no-progress"
4707
+ };
4708
+ const ceilingController = new AbortController();
4709
+ let ceilingTimer;
4710
+ if (maxWindowMs > 0 && Number.isFinite(maxWindowMs)) ceilingTimer = setTimeout(() => ceilingController.abort(), maxWindowMs);
4711
+ let reason = "no-progress";
4550
4712
  try {
4551
- inspection = await adapter.inspectAgentToolRun(row.run_id);
4552
- } catch {}
4553
- if (inspection && inspection.status !== "running" && inspection.status !== "starting") return {
4713
+ while (!ceilingController.signal.aborted) {
4714
+ let afterSequence = -1;
4715
+ try {
4716
+ const existing = await adapter.getAgentToolChunks(row.run_id);
4717
+ const last = existing[existing.length - 1];
4718
+ if (last) afterSequence = last.sequence;
4719
+ } catch {}
4720
+ const beforeSequence = nextSequence;
4721
+ let streamEnded = "idle";
4722
+ try {
4723
+ const stream = await adapter.tailAgentToolRun(row.run_id, { afterSequence });
4724
+ const forwarded = await this._forwardAgentToolStream(stream, row.parent_tool_call_id ?? void 0, row.run_id, nextSequence, ceilingController.signal, noProgressTimeoutMs);
4725
+ nextSequence = forwarded.next;
4726
+ streamEnded = forwarded.ended;
4727
+ } catch {}
4728
+ const terminal = await collectTerminal(nextSequence);
4729
+ if (terminal) return terminal;
4730
+ if (ceilingController.signal.aborted) {
4731
+ reason = "window-exceeded";
4732
+ break;
4733
+ }
4734
+ if (streamEnded !== "done") break;
4735
+ if (nextSequence <= beforeSequence) break;
4736
+ }
4737
+ } finally {
4738
+ if (ceilingTimer !== void 0) clearTimeout(ceilingTimer);
4739
+ }
4740
+ return {
4554
4741
  sequence: nextSequence,
4555
- result: this._terminalResultFromInspection(row.agent_type, inspection),
4556
- completedAt: inspection.completedAt
4742
+ reason
4557
4743
  };
4558
- return { sequence: nextSequence };
4559
4744
  }
4560
4745
  async _replayAgentToolRuns(connection) {
4561
4746
  const rows = this.sql`
4562
4747
  SELECT run_id, parent_tool_call_id, agent_type, input_preview, status,
4563
- summary, output_json, error_message, display_metadata, display_order
4748
+ summary, output_json, error_message, interrupted_reason,
4749
+ child_still_running, display_metadata, display_order
4564
4750
  FROM cf_agent_tool_runs
4565
4751
  ORDER BY started_at ASC
4566
4752
  `;
@@ -4584,19 +4770,22 @@ var Agent = class Agent extends Server {
4584
4770
  status: row.status,
4585
4771
  output: this._parseAgentToolJson(row.output_json),
4586
4772
  summary: row.summary ?? void 0,
4587
- error: row.error_message ?? void 0
4773
+ error: row.error_message ?? void 0,
4774
+ ...this._agentToolInterruptedExtrasFromRow(row)
4588
4775
  }, true, connection);
4589
4776
  }
4590
4777
  }
4591
4778
  async _reconcileAgentToolRuns(options) {
4592
- const reattachTimeoutMs = options?.reattachTimeoutMs ?? DEFAULT_AGENT_TOOL_REATTACH_TIMEOUT_MS;
4779
+ const reattachTimeoutMs = options?.reattachTimeoutMs ?? this._resolvedOptions.agentToolReattachNoProgressTimeoutMs;
4780
+ const reattachMaxWindowMs = options?.reattachMaxWindowMs ?? this._resolvedOptions.agentToolReattachMaxWindowMs;
4593
4781
  const startedAt = Date.now();
4594
4782
  const totalTimeoutMs = options?.totalRecoveryTimeoutMs ?? DEFAULT_AGENT_TOOL_RECOVERY_TOTAL_TIMEOUT_MS;
4595
4783
  const deadlineAt = totalTimeoutMs > 0 ? startedAt + totalTimeoutMs : Number.POSITIVE_INFINITY;
4596
4784
  const deferredFinishes = [];
4597
4785
  const rows = this.sql`
4598
4786
  SELECT run_id, parent_tool_call_id, agent_type, input_preview, status,
4599
- summary, output_json, error_message, display_metadata, display_order,
4787
+ summary, output_json, error_message, interrupted_reason,
4788
+ child_still_running, display_metadata, display_order,
4600
4789
  started_at, completed_at
4601
4790
  FROM cf_agent_tool_runs
4602
4791
  WHERE status IN ('starting', 'running')
@@ -4637,7 +4826,8 @@ var Agent = class Agent extends Server {
4637
4826
  runId: row.run_id,
4638
4827
  agentType: row.agent_type,
4639
4828
  status: "interrupted",
4640
- error: "Agent tool run recovery deadline exceeded."
4829
+ reason: "recovery-deadline",
4830
+ error: this._interruptedMessageForReason("recovery-deadline")
4641
4831
  }, sequence, void 0);
4642
4832
  continue;
4643
4833
  }
@@ -4645,12 +4835,16 @@ var Agent = class Agent extends Server {
4645
4835
  const boundedChildTimeout = childTimeout > 0 ? Math.min(childTimeout, remainingMs) : remainingMs;
4646
4836
  const recovery = await this._inspectAgentToolRunForRecovery(row, sequence, boundedChildTimeout);
4647
4837
  if (recovery.status !== "inspected") {
4648
- await finalizeRow(row, {
4649
- runId: row.run_id,
4650
- agentType: row.agent_type,
4651
- status: "interrupted",
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);
4838
+ await finalizeRow(row, (() => {
4839
+ const reason = recovery.status === "timed-out" ? "inspect-timeout" : "inspect-failed";
4840
+ return {
4841
+ runId: row.run_id,
4842
+ agentType: row.agent_type,
4843
+ status: "interrupted",
4844
+ reason,
4845
+ error: this._interruptedMessageForReason(reason)
4846
+ };
4847
+ })(), sequence, void 0);
4654
4848
  continue;
4655
4849
  }
4656
4850
  const inspection = recovery.inspection;
@@ -4670,17 +4864,26 @@ var Agent = class Agent extends Server {
4670
4864
  runId: row.run_id,
4671
4865
  agentType: row.agent_type,
4672
4866
  status: "interrupted",
4673
- error: "Agent tool run was still running, but live-tail reattachment is not supported in this runtime."
4867
+ reason: "not-tailable",
4868
+ childStillRunning: true,
4869
+ error: this._interruptedMessageForReason("not-tailable")
4674
4870
  }, sequenceAfterReplay, void 0);
4675
4871
  else await finalizeRow(row, this._terminalResultFromInspection(row.agent_type, inspection), sequenceAfterReplay, inspection.completedAt);
4676
4872
  }
4677
4873
  await Promise.all(reattachQueue.map(async ({ row, adapter }) => {
4678
- const reattach = await this._reattachAgentToolRunToTerminal(adapter, row, 1, reattachTimeoutMs);
4679
- await finalizeRow(row, reattach.result ?? {
4874
+ const reattach = await this._reattachAgentToolRunToTerminal(adapter, row, 1, reattachTimeoutMs, reattachMaxWindowMs);
4875
+ if (reattach.result) {
4876
+ await finalizeRow(row, reattach.result, reattach.sequence, reattach.completedAt);
4877
+ return;
4878
+ }
4879
+ const tornDown = await this._teardownGivenUpAgentToolChild(adapter, row.run_id, reattach.reason);
4880
+ await finalizeRow(row, {
4680
4881
  runId: row.run_id,
4681
4882
  agentType: row.agent_type,
4682
4883
  status: "interrupted",
4683
- error: "Agent tool run was still running and did not reach a terminal result within the re-attach budget."
4884
+ reason: reattach.reason,
4885
+ childStillRunning: !tornDown,
4886
+ error: this._interruptedMessageForReason(reattach.reason)
4684
4887
  }, reattach.sequence, reattach.completedAt);
4685
4888
  }));
4686
4889
  this._emit("agent_tool:recovery:complete", {
@@ -4720,6 +4923,7 @@ var Agent = class Agent extends Server {
4720
4923
  childInspectionTimeoutMs: options?.childInspectionTimeoutMs,
4721
4924
  totalRecoveryTimeoutMs: options?.totalRecoveryTimeoutMs,
4722
4925
  reattachTimeoutMs: options?.reattachTimeoutMs,
4926
+ reattachMaxWindowMs: options?.reattachMaxWindowMs,
4723
4927
  runIds: options?.runIds
4724
4928
  });
4725
4929
  await this._runDeferredAgentToolFinishHooks(recoveredAgentToolFinishes);