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.
- package/README.md +6 -4
- package/dist/{agent-tool-types-l98LCbBl.d.ts → agent-tool-types-LInzZfLo.d.ts} +463 -116
- package/dist/agent-tool-types.d.ts +13 -11
- package/dist/{agent-tools-Bg5ilERh.d.ts → agent-tools-BE9xosUG.d.ts} +2 -2
- package/dist/agent-tools.d.ts +14 -20
- package/dist/agent-tools.js +10 -6
- package/dist/agent-tools.js.map +1 -1
- package/dist/browser/ai.d.ts +1 -1
- package/dist/browser/ai.js +1 -1
- package/dist/browser/index.d.ts +1 -1
- package/dist/browser/index.js +1 -1
- package/dist/browser/tanstack-ai.d.ts +1 -1
- package/dist/browser/tanstack-ai.js +1 -1
- package/dist/chat/index.d.ts +138 -19
- package/dist/chat/index.js +96 -12
- package/dist/chat/index.js.map +1 -1
- package/dist/chat-sdk/index.d.ts +4 -4
- package/dist/classPrivateMethodInitSpec-bG0tD96O.js +7 -0
- package/dist/{client-D1kFXo80.js → client-NradHZZz.js} +206 -75
- package/dist/client-NradHZZz.js.map +1 -0
- package/dist/client.d.ts +1 -1
- package/dist/{compaction-helpers-fJyf8j4m.js → compaction-helpers-BjT2NKRZ.js} +22 -3
- package/dist/compaction-helpers-BjT2NKRZ.js.map +1 -0
- package/dist/{compaction-helpers-B-pG5J22.d.ts → compaction-helpers-DpP_XP9J.d.ts} +59 -33
- package/dist/{do-oauth-client-provider-4OKQU9rT.d.ts → do-oauth-client-provider-CPm9rK5I.d.ts} +1 -1
- package/dist/{email-J0GGS3sa.d.ts → email-1fTSJwPm.d.ts} +1 -1
- package/dist/email.d.ts +2 -2
- package/dist/experimental/memory/session/index.d.ts +30 -25
- package/dist/experimental/memory/session/index.js +7 -2
- package/dist/experimental/memory/session/index.js.map +1 -1
- package/dist/experimental/memory/utils/index.d.ts +12 -10
- package/dist/experimental/memory/utils/index.js +2 -2
- package/dist/{index-DKey3P4s.d.ts → index-Brdu5nMI.d.ts} +270 -1
- package/dist/index.d.ts +74 -67
- package/dist/index.js +467 -63
- package/dist/index.js.map +1 -1
- package/dist/{internal_context-BZrMS0B5.d.ts → internal_context-CcZy2Em7.d.ts} +1 -1
- package/dist/internal_context.d.ts +1 -1
- package/dist/mcp/client.d.ts +17 -13
- package/dist/mcp/client.js +2 -2
- package/dist/mcp/do-oauth-client-provider.d.ts +1 -1
- package/dist/mcp/index.d.ts +35 -27
- package/dist/mcp/index.js +402 -69
- package/dist/mcp/index.js.map +1 -1
- package/dist/observability/index.d.ts +1 -1
- package/dist/observability/index.js +15 -1
- package/dist/observability/index.js.map +1 -1
- package/dist/react.d.ts +3 -3
- package/dist/{retries-BVdRl5ZE.d.ts → retries-ClWwxADl.d.ts} +1 -1
- package/dist/retries.d.ts +1 -1
- package/dist/serializable.d.ts +1 -1
- package/dist/{shared-Cvj92byG.d.ts → shared-CpY1FLvm.d.ts} +1 -1
- package/dist/{shared-CiKaIK4h.js → shared-DdOn6sp4.js} +3 -7
- package/dist/{shared-CiKaIK4h.js.map → shared-DdOn6sp4.js.map} +1 -1
- package/dist/skills/index.d.ts +236 -0
- package/dist/skills/index.js +1326 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/sub-routing.d.ts +6 -6
- package/dist/{tool-output-truncation-CH-khbZ3.js → tool-output-truncation-BF4AZQlw.js} +1 -1
- package/dist/{tool-output-truncation-CH-khbZ3.js.map → tool-output-truncation-BF4AZQlw.js.map} +1 -1
- package/dist/{types-_JjKmv-l.d.ts → types-B0GymtN_.d.ts} +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/vite.d.ts +1 -1
- package/dist/vite.js +248 -2
- package/dist/vite.js.map +1 -1
- package/dist/{workflow-types-Dkzg4hAx.d.ts → workflow-types-DPkuBi--.d.ts} +1 -1
- package/dist/workflow-types.d.ts +1 -1
- package/dist/workflows.d.ts +13 -3
- package/dist/workflows.js +10 -1
- package/dist/workflows.js.map +1 -1
- package/package.json +21 -3
- package/skills-module.d.ts +22 -0
- package/dist/client-D1kFXo80.js.map +0 -1
- 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 {
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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'
|
|
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
|
|
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
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
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 } =
|
|
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
|
-
|
|
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
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
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
|
|
4350
|
-
};
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
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
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
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
|
-
|
|
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
|