agents 0.13.3 → 0.14.1
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-BAJWu8s4.d.ts} +474 -117
- package/dist/agent-tool-types.d.ts +13 -11
- package/dist/{agent-tools-Bg5ilERh.d.ts → agent-tools-0R6KEert.d.ts} +2 -2
- package/dist/{agent-tools-BAdX1vdI.js → agent-tools-DYrkT-Kx.js} +46 -6
- package/dist/agent-tools-DYrkT-Kx.js.map +1 -0
- 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 +162 -19
- package/dist/chat/index.js +97 -13
- package/dist/chat/index.js.map +1 -1
- package/dist/chat-sdk/index.d.ts +5 -5
- package/dist/chat-sdk/index.js +2 -2
- package/dist/chat-sdk/index.js.map +1 -1
- package/dist/{classPrivateFieldGet2-Evpt0SEr.js → classPrivateFieldGet2-D_obpP6O.js} +5 -5
- package/dist/classPrivateMethodInitSpec-10iTYB7F.js +7 -0
- package/dist/{client-D1kFXo80.js → client-FUizKzj2.js} +299 -95
- package/dist/client-FUizKzj2.js.map +1 -0
- package/dist/client.d.ts +1 -1
- package/dist/{compaction-helpers-B-pG5J22.d.ts → compaction-helpers-BEUILPss.d.ts} +59 -33
- package/dist/{compaction-helpers-fJyf8j4m.js → compaction-helpers-iiKMr2TQ.js} +22 -3
- package/dist/compaction-helpers-iiKMr2TQ.js.map +1 -0
- package/dist/{do-oauth-client-provider-4OKQU9rT.d.ts → do-oauth-client-provider-D4ZwyBDu.d.ts} +21 -1
- package/dist/{email-J0GGS3sa.d.ts → email-CL27preh.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-RJ4OxMOe.d.ts} +270 -1
- package/dist/index.d.ts +74 -67
- package/dist/index.js +485 -64
- package/dist/index.js.map +1 -1
- package/dist/{internal_context-BZrMS0B5.d.ts → internal_context-Dg4Cgjcu.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/do-oauth-client-provider.js +143 -17
- package/dist/mcp/do-oauth-client-provider.js.map +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/react.js +1 -1
- package/dist/{retries-BVdRl5ZE.d.ts → retries-CF_HKSlJ.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-4CAYLCTO.d.ts} +1 -1
- package/dist/{shared-CiKaIK4h.js → shared-BIpUk4G5.js} +3 -7
- package/dist/{shared-CiKaIK4h.js.map → shared-BIpUk4G5.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-CNnnGZQ3.js} +1 -1
- package/dist/{tool-output-truncation-CH-khbZ3.js.map → tool-output-truncation-CNnnGZQ3.js.map} +1 -1
- package/dist/{types-_JjKmv-l.d.ts → types-6Zo2zfoO.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-SrZK_o9p.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 +31 -13
- package/skills-module.d.ts +22 -0
- package/dist/agent-tools-BAdX1vdI.js.map +0 -1
- package/dist/client-D1kFXo80.js.map +0 -1
- package/dist/compaction-helpers-fJyf8j4m.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-
|
|
5
|
+
import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-D_obpP6O.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-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";
|
|
@@ -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,36 @@ function resolveRetryConfig(taskRetry, defaults) {
|
|
|
285
298
|
maxDelayMs: taskRetry?.maxDelayMs ?? defaults.maxDelayMs
|
|
286
299
|
};
|
|
287
300
|
}
|
|
301
|
+
/**
|
|
302
|
+
* Whether an error is a transient "superseded isolate" failure — the invocation
|
|
303
|
+
* is running on an isolate the platform has replaced with a new version (a
|
|
304
|
+
* deploy / code update). For the rest of that invocation every operation throws
|
|
305
|
+
* the same error (code never reloads mid-invocation), so in-process retries are
|
|
306
|
+
* futile; but the next fresh invocation runs the new code and succeeds.
|
|
307
|
+
*
|
|
308
|
+
* workerd surfaces this as a plain `Error` with one of a few messages, all the
|
|
309
|
+
* same failure class — a message match is the only signal:
|
|
310
|
+
* - "Durable Object reset because its code was updated." (DO storage op on a
|
|
311
|
+
* superseded isolate / deploy bounce)
|
|
312
|
+
* - "This script has been upgraded. Please send a new request to connect to
|
|
313
|
+
* the new version." (a stub/connection to a superseded script; the message
|
|
314
|
+
* literally instructs the caller to retry on the new version)
|
|
315
|
+
*
|
|
316
|
+
* The match stays close to the verbatim platform strings (rather than a loose
|
|
317
|
+
* "upgraded"/"reset" substring) so an ordinary application error that happens
|
|
318
|
+
* to mention those words is NOT misclassified as a supersede — a false positive
|
|
319
|
+
* would defer + re-run a genuinely-failing callback on the platform's alarm
|
|
320
|
+
* retries instead of abandoning it.
|
|
321
|
+
*
|
|
322
|
+
* NOTE: "Network connection lost." is deliberately NOT included — it is a
|
|
323
|
+
* connection error, not an isolate replacement, and may succeed on in-process
|
|
324
|
+
* retry (it is gated by the CF `retryable` property via `isErrorRetryable`),
|
|
325
|
+
* so it stays on the normal retry path rather than the immediate-defer path.
|
|
326
|
+
*/
|
|
327
|
+
function isDurableObjectCodeUpdateReset(error) {
|
|
328
|
+
const message = error instanceof Error ? error.message : typeof error === "string" ? error : "";
|
|
329
|
+
return /reset because its code was updated|this script has been upgraded/i.test(message);
|
|
330
|
+
}
|
|
288
331
|
function getCurrentAgent() {
|
|
289
332
|
const store = __DO_NOT_USE_WILL_BREAK__agentContext.getStore();
|
|
290
333
|
if (!store) return {
|
|
@@ -382,7 +425,10 @@ var Agent = class Agent extends Server {
|
|
|
382
425
|
maxAttempts: userRetry?.maxAttempts ?? DEFAULT_AGENT_STATIC_OPTIONS.retry.maxAttempts,
|
|
383
426
|
baseDelayMs: userRetry?.baseDelayMs ?? DEFAULT_AGENT_STATIC_OPTIONS.retry.baseDelayMs,
|
|
384
427
|
maxDelayMs: userRetry?.maxDelayMs ?? DEFAULT_AGENT_STATIC_OPTIONS.retry.maxDelayMs
|
|
385
|
-
}
|
|
428
|
+
},
|
|
429
|
+
fiberRecoveryHookTimeoutMs: ctor.options?.fiberRecoveryHookTimeoutMs ?? DEFAULT_AGENT_STATIC_OPTIONS.fiberRecoveryHookTimeoutMs,
|
|
430
|
+
fiberRecoveryScanDeadlineMs: ctor.options?.fiberRecoveryScanDeadlineMs ?? DEFAULT_AGENT_STATIC_OPTIONS.fiberRecoveryScanDeadlineMs,
|
|
431
|
+
fiberRecoveryMaxAgeMs: ctor.options?.fiberRecoveryMaxAgeMs ?? DEFAULT_AGENT_STATIC_OPTIONS.fiberRecoveryMaxAgeMs
|
|
386
432
|
};
|
|
387
433
|
return this._cachedOptions;
|
|
388
434
|
}
|
|
@@ -885,7 +931,7 @@ var Agent = class Agent extends Server {
|
|
|
885
931
|
this.broadcastMcpServers();
|
|
886
932
|
this._checkOrphanedWorkflows();
|
|
887
933
|
await this._checkRunFibers();
|
|
888
|
-
const
|
|
934
|
+
const startupAgentToolRunIds = this._agentToolRunRecoveryRunIds();
|
|
889
935
|
this._insideOnStart = true;
|
|
890
936
|
this._warnedScheduleInOnStart.clear();
|
|
891
937
|
let result;
|
|
@@ -894,7 +940,7 @@ var Agent = class Agent extends Server {
|
|
|
894
940
|
} finally {
|
|
895
941
|
this._insideOnStart = false;
|
|
896
942
|
}
|
|
897
|
-
|
|
943
|
+
this._scheduleAgentToolRunRecovery({ runIds: startupAgentToolRunIds });
|
|
898
944
|
return result;
|
|
899
945
|
});
|
|
900
946
|
});
|
|
@@ -2386,20 +2432,67 @@ var Agent = class Agent extends Server {
|
|
|
2386
2432
|
return null;
|
|
2387
2433
|
}
|
|
2388
2434
|
}
|
|
2435
|
+
_fiberRecoveryPayload(ctx, managedRow, startedAt) {
|
|
2436
|
+
return {
|
|
2437
|
+
fiberId: ctx.id,
|
|
2438
|
+
fiberName: ctx.name,
|
|
2439
|
+
managed: managedRow !== null,
|
|
2440
|
+
recoveryReason: ctx.recoveryReason,
|
|
2441
|
+
elapsedMs: startedAt === void 0 ? void 0 : Date.now() - startedAt
|
|
2442
|
+
};
|
|
2443
|
+
}
|
|
2444
|
+
async _withFiberRecoveryTimeout(ctx, operation) {
|
|
2445
|
+
const timeoutMs = this._resolvedOptions.fiberRecoveryHookTimeoutMs;
|
|
2446
|
+
if (timeoutMs <= 0) return operation();
|
|
2447
|
+
let timer;
|
|
2448
|
+
try {
|
|
2449
|
+
return await Promise.race([operation(), new Promise((_, reject) => {
|
|
2450
|
+
timer = setTimeout(() => {
|
|
2451
|
+
reject(/* @__PURE__ */ new Error(`Fiber recovery hook timed out after ${timeoutMs}ms for "${ctx.name}" (${ctx.id})`));
|
|
2452
|
+
}, timeoutMs);
|
|
2453
|
+
})]);
|
|
2454
|
+
} finally {
|
|
2455
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
_recordFiberRecoveryFailure(ctx, managedRow, error, startedAt, reason = "handler_error") {
|
|
2459
|
+
const errorMessage = this._fiberErrorMessage(error);
|
|
2460
|
+
const completedAt = Date.now();
|
|
2461
|
+
if (managedRow) {
|
|
2462
|
+
this.sql`
|
|
2463
|
+
UPDATE cf_agents_fibers
|
|
2464
|
+
SET status = 'error',
|
|
2465
|
+
error_message = ${errorMessage},
|
|
2466
|
+
completed_at = ${completedAt}
|
|
2467
|
+
WHERE fiber_id = ${ctx.id}
|
|
2468
|
+
AND status = 'interrupted'
|
|
2469
|
+
`;
|
|
2470
|
+
this._notifyManagedFiberTerminal(ctx.id);
|
|
2471
|
+
}
|
|
2472
|
+
this._emit("fiber:recovery:failed", {
|
|
2473
|
+
...this._fiberRecoveryPayload(ctx, managedRow, startedAt),
|
|
2474
|
+
error: errorMessage,
|
|
2475
|
+
reason
|
|
2476
|
+
});
|
|
2477
|
+
}
|
|
2389
2478
|
async _runFiberRecoveryHook(ctx, managedRow) {
|
|
2479
|
+
const startedAt = Date.now();
|
|
2480
|
+
this._emit("fiber:recovery:attempt", this._fiberRecoveryPayload(ctx, managedRow));
|
|
2390
2481
|
try {
|
|
2391
|
-
|
|
2482
|
+
const handled = await this._withFiberRecoveryTimeout(ctx, () => this._handleInternalFiberRecovery(ctx));
|
|
2483
|
+
if (!handled) {
|
|
2392
2484
|
const recoveryResult = await this.onFiberRecovered(ctx);
|
|
2393
2485
|
if (managedRow && recoveryResult) this._applyManagedFiberRecoveryResult(ctx.id, recoveryResult);
|
|
2394
2486
|
}
|
|
2487
|
+
this._emit("fiber:recovery:handled", {
|
|
2488
|
+
...this._fiberRecoveryPayload(ctx, managedRow, startedAt),
|
|
2489
|
+
status: handled ? "internal" : managedRow ? "managed" : "user"
|
|
2490
|
+
});
|
|
2491
|
+
return true;
|
|
2395
2492
|
} 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
|
-
`;
|
|
2493
|
+
this._recordFiberRecoveryFailure(ctx, managedRow, e, startedAt);
|
|
2402
2494
|
console.error(`[Agent] Fiber recovery failed for "${ctx.name}" (${ctx.id}):`, e);
|
|
2495
|
+
return false;
|
|
2403
2496
|
}
|
|
2404
2497
|
}
|
|
2405
2498
|
_fiberInspectionFromRow(row) {
|
|
@@ -2684,6 +2777,12 @@ var Agent = class Agent extends Server {
|
|
|
2684
2777
|
INSERT INTO cf_agents_runs (id, name, snapshot, created_at)
|
|
2685
2778
|
VALUES (${id}, ${name}, NULL, ${Date.now()})
|
|
2686
2779
|
`;
|
|
2780
|
+
const startedAt = Date.now();
|
|
2781
|
+
this._emit("fiber:run:started", {
|
|
2782
|
+
fiberId: id,
|
|
2783
|
+
fiberName: name,
|
|
2784
|
+
managed: options?.managed === true
|
|
2785
|
+
});
|
|
2687
2786
|
this._runFiberActiveFibers.add(id);
|
|
2688
2787
|
const writeSnapshot = (data) => {
|
|
2689
2788
|
const snapshot = JSON.stringify(data);
|
|
@@ -2722,12 +2821,25 @@ var Agent = class Agent extends Server {
|
|
|
2722
2821
|
snapshot: null
|
|
2723
2822
|
}));
|
|
2724
2823
|
options?.beforeRunCleanup?.({ ok: true });
|
|
2824
|
+
this._emit("fiber:run:completed", {
|
|
2825
|
+
fiberId: id,
|
|
2826
|
+
fiberName: name,
|
|
2827
|
+
managed: options?.managed === true,
|
|
2828
|
+
elapsedMs: Date.now() - startedAt
|
|
2829
|
+
});
|
|
2725
2830
|
return result;
|
|
2726
2831
|
} catch (error) {
|
|
2727
2832
|
options?.beforeRunCleanup?.({
|
|
2728
2833
|
ok: false,
|
|
2729
2834
|
error
|
|
2730
2835
|
});
|
|
2836
|
+
this._emit("fiber:run:failed", {
|
|
2837
|
+
fiberId: id,
|
|
2838
|
+
fiberName: name,
|
|
2839
|
+
managed: options?.managed === true,
|
|
2840
|
+
error: this._fiberErrorMessage(error),
|
|
2841
|
+
elapsedMs: Date.now() - startedAt
|
|
2842
|
+
});
|
|
2731
2843
|
throw error;
|
|
2732
2844
|
}
|
|
2733
2845
|
} finally {
|
|
@@ -2777,18 +2889,42 @@ var Agent = class Agent extends Server {
|
|
|
2777
2889
|
async _checkRunFibers() {
|
|
2778
2890
|
if (this._runFiberRecoveryInProgress) return;
|
|
2779
2891
|
this._runFiberRecoveryInProgress = true;
|
|
2892
|
+
const scanStartedAt = Date.now();
|
|
2893
|
+
const scanDeadlineMs = this._resolvedOptions.fiberRecoveryScanDeadlineMs;
|
|
2894
|
+
const fiberRecoveryMaxAgeMs = this._resolvedOptions.fiberRecoveryMaxAgeMs;
|
|
2780
2895
|
try {
|
|
2781
2896
|
const rows = this.sql`SELECT id, name, snapshot, created_at FROM cf_agents_runs`;
|
|
2782
2897
|
for (const row of rows) {
|
|
2898
|
+
if (scanDeadlineMs > 0 && Date.now() - scanStartedAt > scanDeadlineMs) {
|
|
2899
|
+
this._emit("fiber:recovery:skipped", {
|
|
2900
|
+
fiberId: row.id,
|
|
2901
|
+
fiberName: row.name,
|
|
2902
|
+
reason: "scan_deadline_exceeded",
|
|
2903
|
+
elapsedMs: Date.now() - scanStartedAt
|
|
2904
|
+
});
|
|
2905
|
+
break;
|
|
2906
|
+
}
|
|
2783
2907
|
if (this._runFiberActiveFibers.has(row.id)) continue;
|
|
2784
2908
|
const snapshot = this._parseFiberRecoverySnapshot(row.id, row.snapshot);
|
|
2785
2909
|
const ctx = {
|
|
2786
2910
|
id: row.id,
|
|
2787
2911
|
name: row.name,
|
|
2788
2912
|
snapshot,
|
|
2789
|
-
createdAt: row.created_at
|
|
2913
|
+
createdAt: row.created_at,
|
|
2914
|
+
recoveryReason: "interrupted"
|
|
2790
2915
|
};
|
|
2791
2916
|
const managedRow = this._readFiber(row.id);
|
|
2917
|
+
this._emit("fiber:recovery:detected", {
|
|
2918
|
+
...this._fiberRecoveryPayload(ctx, managedRow),
|
|
2919
|
+
elapsedMs: Date.now() - row.created_at
|
|
2920
|
+
});
|
|
2921
|
+
this._emit("fiber:run:interrupted", {
|
|
2922
|
+
fiberId: row.id,
|
|
2923
|
+
fiberName: row.name,
|
|
2924
|
+
managed: managedRow !== null,
|
|
2925
|
+
recoveryReason: "interrupted",
|
|
2926
|
+
elapsedMs: Date.now() - row.created_at
|
|
2927
|
+
});
|
|
2792
2928
|
if (managedRow) {
|
|
2793
2929
|
if (this._isTerminalFiberStatus(managedRow.status)) {
|
|
2794
2930
|
this.sql`DELETE FROM cf_agents_runs WHERE id = ${row.id}`;
|
|
@@ -2808,8 +2944,17 @@ var Agent = class Agent extends Server {
|
|
|
2808
2944
|
ctx.metadata = this._parseFiberJsonObject(managedRow.metadata_json);
|
|
2809
2945
|
ctx.status = "interrupted";
|
|
2810
2946
|
}
|
|
2811
|
-
await this._runFiberRecoveryHook(ctx, managedRow);
|
|
2812
|
-
|
|
2947
|
+
const recovered = await this._runFiberRecoveryHook(ctx, managedRow);
|
|
2948
|
+
const tooOld = fiberRecoveryMaxAgeMs > 0 && Date.now() - row.created_at > fiberRecoveryMaxAgeMs;
|
|
2949
|
+
if (recovered || managedRow || tooOld) {
|
|
2950
|
+
if (!recovered && !managedRow && tooOld) this._emit("fiber:recovery:skipped", {
|
|
2951
|
+
fiberId: row.id,
|
|
2952
|
+
fiberName: row.name,
|
|
2953
|
+
reason: "max_age_exceeded",
|
|
2954
|
+
elapsedMs: Date.now() - row.created_at
|
|
2955
|
+
});
|
|
2956
|
+
this.sql`DELETE FROM cf_agents_runs WHERE id = ${row.id}`;
|
|
2957
|
+
}
|
|
2813
2958
|
if (managedRow) this._notifyManagedFiberTerminal(row.id);
|
|
2814
2959
|
}
|
|
2815
2960
|
const ledgerOnlyRows = this.sql`
|
|
@@ -2822,6 +2967,16 @@ var Agent = class Agent extends Server {
|
|
|
2822
2967
|
AND r.id IS NULL
|
|
2823
2968
|
`;
|
|
2824
2969
|
for (const row of ledgerOnlyRows) {
|
|
2970
|
+
if (scanDeadlineMs > 0 && Date.now() - scanStartedAt > scanDeadlineMs) {
|
|
2971
|
+
this._emit("fiber:recovery:skipped", {
|
|
2972
|
+
fiberId: row.fiber_id,
|
|
2973
|
+
fiberName: row.name,
|
|
2974
|
+
reason: "scan_deadline_exceeded",
|
|
2975
|
+
elapsedMs: Date.now() - scanStartedAt,
|
|
2976
|
+
managed: true
|
|
2977
|
+
});
|
|
2978
|
+
break;
|
|
2979
|
+
}
|
|
2825
2980
|
if (this._runFiberActiveFibers.has(row.fiber_id)) continue;
|
|
2826
2981
|
const snapshot = this._parseFiberRecoverySnapshot(row.fiber_id, row.snapshot);
|
|
2827
2982
|
const completedAt = Date.now();
|
|
@@ -2832,15 +2987,28 @@ var Agent = class Agent extends Server {
|
|
|
2832
2987
|
WHERE fiber_id = ${row.fiber_id}
|
|
2833
2988
|
AND status IN ('pending', 'running')
|
|
2834
2989
|
`;
|
|
2835
|
-
|
|
2990
|
+
const ctx = {
|
|
2836
2991
|
id: row.fiber_id,
|
|
2837
2992
|
name: row.name,
|
|
2838
2993
|
snapshot,
|
|
2839
2994
|
createdAt: row.created_at,
|
|
2840
2995
|
idempotencyKey: row.idempotency_key ?? void 0,
|
|
2841
2996
|
metadata: this._parseFiberJsonObject(row.metadata_json),
|
|
2842
|
-
status: "interrupted"
|
|
2843
|
-
|
|
2997
|
+
status: "interrupted",
|
|
2998
|
+
recoveryReason: "interrupted"
|
|
2999
|
+
};
|
|
3000
|
+
this._emit("fiber:recovery:detected", {
|
|
3001
|
+
...this._fiberRecoveryPayload(ctx, row),
|
|
3002
|
+
elapsedMs: Date.now() - row.created_at
|
|
3003
|
+
});
|
|
3004
|
+
this._emit("fiber:run:interrupted", {
|
|
3005
|
+
fiberId: row.fiber_id,
|
|
3006
|
+
fiberName: row.name,
|
|
3007
|
+
managed: true,
|
|
3008
|
+
recoveryReason: "interrupted",
|
|
3009
|
+
elapsedMs: Date.now() - row.created_at
|
|
3010
|
+
});
|
|
3011
|
+
await this._runFiberRecoveryHook(ctx, row);
|
|
2844
3012
|
this._notifyManagedFiberTerminal(row.fiber_id);
|
|
2845
3013
|
}
|
|
2846
3014
|
} finally {
|
|
@@ -2998,6 +3166,8 @@ var Agent = class Agent extends Server {
|
|
|
2998
3166
|
});
|
|
2999
3167
|
return;
|
|
3000
3168
|
}
|
|
3169
|
+
const isOneShotSchedule = row.type === "delayed" || row.type === "scheduled";
|
|
3170
|
+
const shouldDeferReset = (error) => isOneShotSchedule && isDurableObjectCodeUpdateReset(error);
|
|
3001
3171
|
try {
|
|
3002
3172
|
this._emit("schedule:execute", {
|
|
3003
3173
|
callback: row.callback,
|
|
@@ -3013,9 +3183,14 @@ var Agent = class Agent extends Server {
|
|
|
3013
3183
|
await callback.bind(this)(parsedPayload, row);
|
|
3014
3184
|
}, {
|
|
3015
3185
|
baseDelayMs,
|
|
3016
|
-
maxDelayMs
|
|
3186
|
+
maxDelayMs,
|
|
3187
|
+
shouldRetry: (error) => !shouldDeferReset(error)
|
|
3017
3188
|
});
|
|
3018
3189
|
} catch (e) {
|
|
3190
|
+
if (shouldDeferReset(e)) {
|
|
3191
|
+
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.`);
|
|
3192
|
+
throw e;
|
|
3193
|
+
}
|
|
3019
3194
|
console.error(`error executing callback "${row.callback}" after ${maxAttempts} attempts`, e);
|
|
3020
3195
|
this._emit("schedule:error", {
|
|
3021
3196
|
callback: row.callback,
|
|
@@ -3835,7 +4010,7 @@ var Agent = class Agent extends Server {
|
|
|
3835
4010
|
const agentType = cls.name;
|
|
3836
4011
|
const existing = this._readAgentToolRun(runId);
|
|
3837
4012
|
if (existing) {
|
|
3838
|
-
if (
|
|
4013
|
+
if (existing.status === "completed" || existing.status === "error" || existing.status === "aborted") {
|
|
3839
4014
|
if (existing.status === "completed" && existing.output_json == null) try {
|
|
3840
4015
|
const child = await this.subAgent(cls, runId);
|
|
3841
4016
|
const inspection = await this._asAgentToolChildAdapter(child).inspectAgentToolRun(runId);
|
|
@@ -3847,7 +4022,19 @@ var Agent = class Agent extends Server {
|
|
|
3847
4022
|
} catch {}
|
|
3848
4023
|
return this._resultFromAgentToolRow(existing);
|
|
3849
4024
|
}
|
|
3850
|
-
|
|
4025
|
+
try {
|
|
4026
|
+
const child = await this.subAgent(cls, runId);
|
|
4027
|
+
const adapter = this._asAgentToolChildAdapter(child);
|
|
4028
|
+
const reattach = await this._reattachAgentToolRunToTerminal(adapter, existing, 1);
|
|
4029
|
+
if (reattach.result) {
|
|
4030
|
+
await this._finishAgentToolRun(this._agentToolRunInfoFromRow(existing), reattach.result, {
|
|
4031
|
+
sequence: reattach.sequence,
|
|
4032
|
+
completedAt: reattach.completedAt
|
|
4033
|
+
});
|
|
4034
|
+
return reattach.result;
|
|
4035
|
+
}
|
|
4036
|
+
} catch {}
|
|
4037
|
+
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
4038
|
}
|
|
3852
4039
|
const displayOrder = options.displayOrder ?? 0;
|
|
3853
4040
|
const inputPreview = options.inputPreview ?? this._defaultAgentToolPreview(options.input);
|
|
@@ -4127,7 +4314,7 @@ var Agent = class Agent extends Server {
|
|
|
4127
4314
|
error_message = ${result.error ?? null},
|
|
4128
4315
|
completed_at = ${completedAt}
|
|
4129
4316
|
WHERE run_id = ${runId}
|
|
4130
|
-
AND status NOT IN ('completed', 'error', 'aborted'
|
|
4317
|
+
AND status NOT IN ('completed', 'error', 'aborted')
|
|
4131
4318
|
`;
|
|
4132
4319
|
if (result.status === "completed" && result.output !== void 0) this.sql`
|
|
4133
4320
|
UPDATE cf_agent_tool_runs
|
|
@@ -4179,7 +4366,12 @@ var Agent = class Agent extends Server {
|
|
|
4179
4366
|
}
|
|
4180
4367
|
async _broadcastAgentToolStoredChunks(row, sequence, replay, connection) {
|
|
4181
4368
|
const child = await this._cf_resolveSubAgent(row.agent_type, row.run_id);
|
|
4182
|
-
const
|
|
4369
|
+
const adapter = this._asAgentToolChildAdapter(child);
|
|
4370
|
+
return this._broadcastAgentToolStoredChunksFromAdapter(adapter, row, sequence, replay, connection);
|
|
4371
|
+
}
|
|
4372
|
+
async _broadcastAgentToolStoredChunksFromAdapter(adapter, row, sequence, replay, connection, timeoutMs) {
|
|
4373
|
+
const chunks = await this._getAgentToolChunksForRecovery(adapter, row.run_id, timeoutMs);
|
|
4374
|
+
if (!chunks) return sequence;
|
|
4183
4375
|
return this._broadcastAgentToolChunks(row.parent_tool_call_id ?? void 0, row.run_id, chunks, sequence, replay, connection);
|
|
4184
4376
|
}
|
|
4185
4377
|
async _forwardAgentToolStream(stream, parentToolCallId, runId, sequence, signal) {
|
|
@@ -4188,11 +4380,17 @@ var Agent = class Agent extends Server {
|
|
|
4188
4380
|
const reader = stream.getReader();
|
|
4189
4381
|
const decoder = new TextDecoder();
|
|
4190
4382
|
let bufferedBytes = "";
|
|
4383
|
+
let aborted = false;
|
|
4384
|
+
let resolveAbort;
|
|
4385
|
+
const abortPromise = new Promise((resolve) => {
|
|
4386
|
+
resolveAbort = resolve;
|
|
4387
|
+
});
|
|
4191
4388
|
let abortListener;
|
|
4192
4389
|
if (signal) {
|
|
4193
|
-
abortListener = () =>
|
|
4390
|
+
abortListener = () => resolveAbort?.();
|
|
4194
4391
|
signal.addEventListener("abort", abortListener, { once: true });
|
|
4195
4392
|
}
|
|
4393
|
+
let forwardedSinceProgress = false;
|
|
4196
4394
|
try {
|
|
4197
4395
|
const forwardChunk = (chunk) => {
|
|
4198
4396
|
this._broadcastAgentToolEvent(parentToolCallId, next++, {
|
|
@@ -4200,6 +4398,7 @@ var Agent = class Agent extends Server {
|
|
|
4200
4398
|
runId,
|
|
4201
4399
|
body: chunk.body
|
|
4202
4400
|
});
|
|
4401
|
+
forwardedSinceProgress = true;
|
|
4203
4402
|
};
|
|
4204
4403
|
const forwardLine = (line) => {
|
|
4205
4404
|
try {
|
|
@@ -4221,14 +4420,17 @@ var Agent = class Agent extends Server {
|
|
|
4221
4420
|
}
|
|
4222
4421
|
};
|
|
4223
4422
|
while (true) {
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4423
|
+
const readPromise = reader.read();
|
|
4424
|
+
readPromise.catch(() => {});
|
|
4425
|
+
const raced = await Promise.race([readPromise.then((result) => ({
|
|
4426
|
+
kind: "read",
|
|
4427
|
+
result
|
|
4428
|
+
})), abortPromise.then(() => ({ kind: "abort" }))]);
|
|
4429
|
+
if (raced.kind === "abort") {
|
|
4430
|
+
aborted = true;
|
|
4431
|
+
break;
|
|
4230
4432
|
}
|
|
4231
|
-
const { done, value } =
|
|
4433
|
+
const { done, value } = raced.result;
|
|
4232
4434
|
if (done) {
|
|
4233
4435
|
bufferedBytes += decoder.decode();
|
|
4234
4436
|
flushBufferedBytes(true);
|
|
@@ -4238,13 +4440,42 @@ var Agent = class Agent extends Server {
|
|
|
4238
4440
|
bufferedBytes += decoder.decode(value, { stream: true });
|
|
4239
4441
|
flushBufferedBytes();
|
|
4240
4442
|
} else forwardChunk(value);
|
|
4443
|
+
if (forwardedSinceProgress) {
|
|
4444
|
+
forwardedSinceProgress = false;
|
|
4445
|
+
try {
|
|
4446
|
+
await this._onAgentToolStreamProgress();
|
|
4447
|
+
} catch {}
|
|
4448
|
+
}
|
|
4241
4449
|
}
|
|
4242
4450
|
} finally {
|
|
4243
4451
|
if (abortListener && signal) signal.removeEventListener("abort", abortListener);
|
|
4244
|
-
|
|
4452
|
+
if (!aborted) try {
|
|
4453
|
+
reader.releaseLock();
|
|
4454
|
+
} catch {}
|
|
4245
4455
|
}
|
|
4246
4456
|
return next;
|
|
4247
4457
|
}
|
|
4458
|
+
/**
|
|
4459
|
+
* Hook invoked by `_forwardAgentToolStream` after a child produces output that
|
|
4460
|
+
* was forwarded to the parent's connections. Forwarding a sub-agent's stream
|
|
4461
|
+
* is genuine forward progress for the *parent* turn (the parent is
|
|
4462
|
+
* orchestrating the child), so chat-recovery subclasses (Think / AIChatAgent)
|
|
4463
|
+
* override this to advance their recovery progress marker.
|
|
4464
|
+
*
|
|
4465
|
+
* Without it, a parent whose turn merely `await`s a sub-agent banks zero
|
|
4466
|
+
* progress of its own, so under deploy churn the parent's no-progress recovery
|
|
4467
|
+
* window exhausts and abandons the turn as `interrupted` — even though the
|
|
4468
|
+
* child is healthily streaming and ultimately completes (observed in the
|
|
4469
|
+
* `deploy-churn --mode subagent` harness: `attempt 6/6, stable_timeout,
|
|
4470
|
+
* progress: 1`).
|
|
4471
|
+
*
|
|
4472
|
+
* Called ONLY after at least one chunk was actually forwarded — never merely
|
|
4473
|
+
* because a child is attached — so a silent / hung child still lets the parent
|
|
4474
|
+
* exhaust on its own timer. The base Agent has no recovery budget, so this is
|
|
4475
|
+
* a no-op; subclasses should throttle the (durable) bump since this can be
|
|
4476
|
+
* called repeatedly while a child streams.
|
|
4477
|
+
*/
|
|
4478
|
+
async _onAgentToolStreamProgress() {}
|
|
4248
4479
|
_broadcastAgentToolTerminal(parentToolCallId, sequence, result, replay, connection) {
|
|
4249
4480
|
if (result.status === "completed") this._broadcastAgentToolEvent(parentToolCallId, sequence, {
|
|
4250
4481
|
kind: "finished",
|
|
@@ -4291,6 +4522,58 @@ var Agent = class Agent extends Server {
|
|
|
4291
4522
|
await this._finishAgentToolRun(this._agentToolRunInfoFromRow(row), result, { sequence });
|
|
4292
4523
|
return result;
|
|
4293
4524
|
}
|
|
4525
|
+
/**
|
|
4526
|
+
* Re-attach to a still-running child agent-tool run and tail it to its real
|
|
4527
|
+
* terminal result, instead of abandoning it as `interrupted` (#1630). The
|
|
4528
|
+
* child is a separate facet with its own `chatRecovery`, so resolving it via
|
|
4529
|
+
* the adapter wakes it and lets it self-complete the interrupted turn; we tail
|
|
4530
|
+
* its live stream (forwarding chunks to the parent's connections) until it
|
|
4531
|
+
* reaches terminal, then inspect for the collected result.
|
|
4532
|
+
*
|
|
4533
|
+
* Bounded by {@link DEFAULT_AGENT_TOOL_REATTACH_TIMEOUT_MS}: a child that keeps
|
|
4534
|
+
* advancing toward terminal within the window is collected; a genuinely hung
|
|
4535
|
+
* child returns `{ result: undefined }` once the budget elapses so the caller
|
|
4536
|
+
* falls back to `interrupted` and recovery can never block forever.
|
|
4537
|
+
*
|
|
4538
|
+
* Returns the terminal `result` (and `completedAt`) when the child reaches a
|
|
4539
|
+
* terminal status within the budget, plus the advanced broadcast `sequence`.
|
|
4540
|
+
* Returns `{ result: undefined }` when there is no `tailAgentToolRun` adapter,
|
|
4541
|
+
* the budget is exhausted, or the child is still non-terminal.
|
|
4542
|
+
*/
|
|
4543
|
+
async _reattachAgentToolRunToTerminal(adapter, row, sequence, budgetMs = DEFAULT_AGENT_TOOL_REATTACH_TIMEOUT_MS) {
|
|
4544
|
+
if (typeof adapter.tailAgentToolRun !== "function") return { sequence };
|
|
4545
|
+
const controller = new AbortController();
|
|
4546
|
+
let timeoutId;
|
|
4547
|
+
if (budgetMs > 0 && Number.isFinite(budgetMs)) timeoutId = setTimeout(() => controller.abort(), budgetMs);
|
|
4548
|
+
else if (budgetMs <= 0) controller.abort();
|
|
4549
|
+
this._emit("agent_tool:recovery:reattach", {
|
|
4550
|
+
runId: row.run_id,
|
|
4551
|
+
agentType: row.agent_type,
|
|
4552
|
+
budgetMs
|
|
4553
|
+
});
|
|
4554
|
+
let nextSequence = sequence;
|
|
4555
|
+
if (!controller.signal.aborted) try {
|
|
4556
|
+
let afterSequence = -1;
|
|
4557
|
+
try {
|
|
4558
|
+
const existing = await adapter.getAgentToolChunks(row.run_id);
|
|
4559
|
+
const last = existing[existing.length - 1];
|
|
4560
|
+
if (last) afterSequence = last.sequence;
|
|
4561
|
+
} catch {}
|
|
4562
|
+
const stream = await adapter.tailAgentToolRun(row.run_id, { afterSequence });
|
|
4563
|
+
nextSequence = await this._forwardAgentToolStream(stream, row.parent_tool_call_id ?? void 0, row.run_id, nextSequence, controller.signal);
|
|
4564
|
+
} catch {}
|
|
4565
|
+
if (timeoutId !== void 0) clearTimeout(timeoutId);
|
|
4566
|
+
let inspection = null;
|
|
4567
|
+
try {
|
|
4568
|
+
inspection = await adapter.inspectAgentToolRun(row.run_id);
|
|
4569
|
+
} catch {}
|
|
4570
|
+
if (inspection && inspection.status !== "running" && inspection.status !== "starting") return {
|
|
4571
|
+
sequence: nextSequence,
|
|
4572
|
+
result: this._terminalResultFromInspection(row.agent_type, inspection),
|
|
4573
|
+
completedAt: inspection.completedAt
|
|
4574
|
+
};
|
|
4575
|
+
return { sequence: nextSequence };
|
|
4576
|
+
}
|
|
4294
4577
|
async _replayAgentToolRuns(connection) {
|
|
4295
4578
|
const rows = this.sql`
|
|
4296
4579
|
SELECT run_id, parent_tool_call_id, agent_type, input_preview, status,
|
|
@@ -4323,6 +4606,10 @@ var Agent = class Agent extends Server {
|
|
|
4323
4606
|
}
|
|
4324
4607
|
}
|
|
4325
4608
|
async _reconcileAgentToolRuns(options) {
|
|
4609
|
+
const reattachTimeoutMs = options?.reattachTimeoutMs ?? DEFAULT_AGENT_TOOL_REATTACH_TIMEOUT_MS;
|
|
4610
|
+
const startedAt = Date.now();
|
|
4611
|
+
const totalTimeoutMs = options?.totalRecoveryTimeoutMs ?? DEFAULT_AGENT_TOOL_RECOVERY_TOTAL_TIMEOUT_MS;
|
|
4612
|
+
const deadlineAt = totalTimeoutMs > 0 ? startedAt + totalTimeoutMs : Number.POSITIVE_INFINITY;
|
|
4326
4613
|
const deferredFinishes = [];
|
|
4327
4614
|
const rows = this.sql`
|
|
4328
4615
|
SELECT run_id, parent_tool_call_id, agent_type, input_preview, status,
|
|
@@ -4332,43 +4619,158 @@ var Agent = class Agent extends Server {
|
|
|
4332
4619
|
WHERE status IN ('starting', 'running')
|
|
4333
4620
|
ORDER BY started_at ASC
|
|
4334
4621
|
`;
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4622
|
+
const runIds = options?.runIds !== void 0 ? new Set(options.runIds) : void 0;
|
|
4623
|
+
const recoveryRows = rows.filter((row) => !runIds || runIds.has(row.run_id));
|
|
4624
|
+
this._emit("agent_tool:recovery:begin", {
|
|
4625
|
+
runCount: recoveryRows.length,
|
|
4626
|
+
totalTimeoutMs
|
|
4627
|
+
});
|
|
4628
|
+
const finalizeRow = async (row, result, sequence, completedAt) => {
|
|
4629
|
+
this._emit("agent_tool:recovery:row", {
|
|
4630
|
+
runId: row.run_id,
|
|
4631
|
+
agentType: row.agent_type,
|
|
4632
|
+
status: result.status,
|
|
4633
|
+
reason: result.error,
|
|
4634
|
+
elapsedMs: Date.now() - startedAt
|
|
4635
|
+
});
|
|
4636
|
+
const deferredFinish = await this._finishAgentToolRun(this._agentToolRunInfoFromRow(row), result, {
|
|
4637
|
+
sequence,
|
|
4638
|
+
completedAt,
|
|
4639
|
+
deferFinishHook: options?.deferFinishHooks
|
|
4640
|
+
});
|
|
4641
|
+
if (deferredFinish) deferredFinishes.push(deferredFinish);
|
|
4642
|
+
};
|
|
4643
|
+
const reattachQueue = [];
|
|
4644
|
+
for (const row of recoveryRows) {
|
|
4645
|
+
const sequence = 1;
|
|
4646
|
+
const remainingMs = deadlineAt - Date.now();
|
|
4647
|
+
if (remainingMs <= 0) {
|
|
4648
|
+
this._emit("agent_tool:recovery:deadline", {
|
|
4649
|
+
runId: row.run_id,
|
|
4650
|
+
agentType: row.agent_type,
|
|
4651
|
+
elapsedMs: Date.now() - startedAt
|
|
4652
|
+
});
|
|
4653
|
+
await finalizeRow(row, {
|
|
4346
4654
|
runId: row.run_id,
|
|
4347
4655
|
agentType: row.agent_type,
|
|
4348
4656
|
status: "interrupted",
|
|
4349
|
-
error: "Agent tool run
|
|
4350
|
-
};
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4657
|
+
error: "Agent tool run recovery deadline exceeded."
|
|
4658
|
+
}, sequence, void 0);
|
|
4659
|
+
continue;
|
|
4660
|
+
}
|
|
4661
|
+
const childTimeout = options?.childInspectionTimeoutMs ?? DEFAULT_AGENT_TOOL_RECOVERY_TIMEOUT_MS;
|
|
4662
|
+
const boundedChildTimeout = childTimeout > 0 ? Math.min(childTimeout, remainingMs) : remainingMs;
|
|
4663
|
+
const recovery = await this._inspectAgentToolRunForRecovery(row, sequence, boundedChildTimeout);
|
|
4664
|
+
if (recovery.status !== "inspected") {
|
|
4665
|
+
await finalizeRow(row, {
|
|
4357
4666
|
runId: row.run_id,
|
|
4358
4667
|
agentType: row.agent_type,
|
|
4359
4668
|
status: "interrupted",
|
|
4360
|
-
error: "Agent tool run could not be inspected during parent recovery."
|
|
4361
|
-
};
|
|
4669
|
+
error: recovery.status === "timed-out" ? "Agent tool run inspection timed out during parent recovery." : "Agent tool run could not be inspected during parent recovery."
|
|
4670
|
+
}, sequence, void 0);
|
|
4671
|
+
continue;
|
|
4362
4672
|
}
|
|
4363
|
-
const
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4673
|
+
const inspection = recovery.inspection;
|
|
4674
|
+
const stillRunning = !inspection || inspection.status === "running" || inspection.status === "starting";
|
|
4675
|
+
if (stillRunning && typeof recovery.adapter.tailAgentToolRun === "function") {
|
|
4676
|
+
reattachQueue.push({
|
|
4677
|
+
row,
|
|
4678
|
+
adapter: recovery.adapter
|
|
4679
|
+
});
|
|
4680
|
+
continue;
|
|
4681
|
+
}
|
|
4682
|
+
let sequenceAfterReplay = sequence;
|
|
4683
|
+
try {
|
|
4684
|
+
sequenceAfterReplay = await this._broadcastAgentToolStoredChunksFromAdapter(recovery.adapter, row, sequence, void 0, void 0, boundedChildTimeout);
|
|
4685
|
+
} catch {}
|
|
4686
|
+
if (stillRunning) await finalizeRow(row, {
|
|
4687
|
+
runId: row.run_id,
|
|
4688
|
+
agentType: row.agent_type,
|
|
4689
|
+
status: "interrupted",
|
|
4690
|
+
error: "Agent tool run was still running, but live-tail reattachment is not supported in this runtime."
|
|
4691
|
+
}, sequenceAfterReplay, void 0);
|
|
4692
|
+
else await finalizeRow(row, this._terminalResultFromInspection(row.agent_type, inspection), sequenceAfterReplay, inspection.completedAt);
|
|
4369
4693
|
}
|
|
4694
|
+
await Promise.all(reattachQueue.map(async ({ row, adapter }) => {
|
|
4695
|
+
const reattach = await this._reattachAgentToolRunToTerminal(adapter, row, 1, reattachTimeoutMs);
|
|
4696
|
+
await finalizeRow(row, reattach.result ?? {
|
|
4697
|
+
runId: row.run_id,
|
|
4698
|
+
agentType: row.agent_type,
|
|
4699
|
+
status: "interrupted",
|
|
4700
|
+
error: "Agent tool run was still running and did not reach a terminal result within the re-attach budget."
|
|
4701
|
+
}, reattach.sequence, reattach.completedAt);
|
|
4702
|
+
}));
|
|
4703
|
+
this._emit("agent_tool:recovery:complete", {
|
|
4704
|
+
runCount: recoveryRows.length,
|
|
4705
|
+
elapsedMs: Date.now() - startedAt
|
|
4706
|
+
});
|
|
4370
4707
|
return deferredFinishes;
|
|
4371
4708
|
}
|
|
4709
|
+
async _inspectAgentToolRunForRecovery(row, _sequence, timeoutMs = DEFAULT_AGENT_TOOL_RECOVERY_TIMEOUT_MS) {
|
|
4710
|
+
const inspect = (async () => {
|
|
4711
|
+
const child = await this._cf_resolveSubAgent(row.agent_type, row.run_id);
|
|
4712
|
+
const adapter = this._asAgentToolChildAdapter(child);
|
|
4713
|
+
return {
|
|
4714
|
+
status: "inspected",
|
|
4715
|
+
adapter,
|
|
4716
|
+
inspection: await adapter.inspectAgentToolRun(row.run_id)
|
|
4717
|
+
};
|
|
4718
|
+
})().catch(() => ({ status: "failed" }));
|
|
4719
|
+
if (timeoutMs <= 0) return inspect;
|
|
4720
|
+
let timeoutId;
|
|
4721
|
+
const timeout = new Promise((resolve) => {
|
|
4722
|
+
timeoutId = setTimeout(() => {
|
|
4723
|
+
resolve({ status: "timed-out" });
|
|
4724
|
+
}, timeoutMs);
|
|
4725
|
+
});
|
|
4726
|
+
const result = await Promise.race([inspect, timeout]);
|
|
4727
|
+
if (timeoutId !== void 0) clearTimeout(timeoutId);
|
|
4728
|
+
return result;
|
|
4729
|
+
}
|
|
4730
|
+
_scheduleAgentToolRunRecovery(options) {
|
|
4731
|
+
if (this._agentToolRunRecoveryPromise) return this._agentToolRunRecoveryPromise;
|
|
4732
|
+
if (options?.runIds && options.runIds.length === 0) return Promise.resolve();
|
|
4733
|
+
const recovery = (async () => {
|
|
4734
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
4735
|
+
const recoveredAgentToolFinishes = await this._reconcileAgentToolRuns({
|
|
4736
|
+
deferFinishHooks: true,
|
|
4737
|
+
childInspectionTimeoutMs: options?.childInspectionTimeoutMs,
|
|
4738
|
+
totalRecoveryTimeoutMs: options?.totalRecoveryTimeoutMs,
|
|
4739
|
+
reattachTimeoutMs: options?.reattachTimeoutMs,
|
|
4740
|
+
runIds: options?.runIds
|
|
4741
|
+
});
|
|
4742
|
+
await this._runDeferredAgentToolFinishHooks(recoveredAgentToolFinishes);
|
|
4743
|
+
})().catch(async (error) => {
|
|
4744
|
+
this._emit("agent_tool:recovery:failed", { error: error instanceof Error ? error.message : String(error) });
|
|
4745
|
+
try {
|
|
4746
|
+
await this.onError(error);
|
|
4747
|
+
} catch {}
|
|
4748
|
+
}).finally(() => {
|
|
4749
|
+
this._agentToolRunRecoveryPromise = void 0;
|
|
4750
|
+
});
|
|
4751
|
+
this._agentToolRunRecoveryPromise = recovery;
|
|
4752
|
+
this.ctx.waitUntil(recovery);
|
|
4753
|
+
return recovery;
|
|
4754
|
+
}
|
|
4755
|
+
_agentToolRunRecoveryRunIds() {
|
|
4756
|
+
return this.sql`
|
|
4757
|
+
SELECT run_id
|
|
4758
|
+
FROM cf_agent_tool_runs
|
|
4759
|
+
WHERE status IN ('starting', 'running')
|
|
4760
|
+
ORDER BY started_at ASC
|
|
4761
|
+
`.map((row) => row.run_id);
|
|
4762
|
+
}
|
|
4763
|
+
async _getAgentToolChunksForRecovery(adapter, runId, timeoutMs) {
|
|
4764
|
+
const chunks = adapter.getAgentToolChunks(runId).catch(() => void 0);
|
|
4765
|
+
if (timeoutMs === void 0 || timeoutMs <= 0) return chunks;
|
|
4766
|
+
let timeoutId;
|
|
4767
|
+
const timeout = new Promise((resolve) => {
|
|
4768
|
+
timeoutId = setTimeout(() => resolve(void 0), timeoutMs);
|
|
4769
|
+
});
|
|
4770
|
+
const result = await Promise.race([chunks, timeout]);
|
|
4771
|
+
if (timeoutId !== void 0) clearTimeout(timeoutId);
|
|
4772
|
+
return result;
|
|
4773
|
+
}
|
|
4372
4774
|
/**
|
|
4373
4775
|
* Shared facet resolution — takes a CamelCase class name string
|
|
4374
4776
|
* (matching `ctx.exports`) rather than a class reference. Both
|
|
@@ -5360,7 +5762,26 @@ var Agent = class Agent extends Server {
|
|
|
5360
5762
|
async addMcpServer(serverName, urlOrBinding, callbackHostOrOptions, agentsPrefix, options) {
|
|
5361
5763
|
const isHttpTransport = typeof urlOrBinding === "string";
|
|
5362
5764
|
const normalizedUrl = isHttpTransport ? new URL(urlOrBinding).href : void 0;
|
|
5363
|
-
|
|
5765
|
+
let requestedId;
|
|
5766
|
+
if (typeof callbackHostOrOptions === "object" && callbackHostOrOptions !== null && typeof callbackHostOrOptions.id === "string") {
|
|
5767
|
+
const rawId = callbackHostOrOptions.id;
|
|
5768
|
+
requestedId = normalizeServerId(rawId);
|
|
5769
|
+
}
|
|
5770
|
+
const allServers = this.mcp.listServers();
|
|
5771
|
+
const existingServer = allServers.find((s) => s.name === serverName && (!isHttpTransport || new URL(s.server_url).href === normalizedUrl));
|
|
5772
|
+
if (requestedId) {
|
|
5773
|
+
const idConflict = allServers.find((s) => {
|
|
5774
|
+
if (s.id !== requestedId) return false;
|
|
5775
|
+
if (s.name !== serverName) return true;
|
|
5776
|
+
if (isHttpTransport) return new URL(s.server_url).href !== normalizedUrl;
|
|
5777
|
+
return false;
|
|
5778
|
+
});
|
|
5779
|
+
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).`);
|
|
5780
|
+
if (existingServer && existingServer.id !== requestedId) {
|
|
5781
|
+
await this.mcp.migrateServerId(existingServer.id, requestedId, this.name);
|
|
5782
|
+
existingServer.id = requestedId;
|
|
5783
|
+
}
|
|
5784
|
+
}
|
|
5364
5785
|
if (existingServer && this.mcp.mcpConnections[existingServer.id]) {
|
|
5365
5786
|
const conn = this.mcp.mcpConnections[existingServer.id];
|
|
5366
5787
|
if (conn.connectionState === MCPConnectionState.AUTHENTICATING && conn.options.transport.authProvider?.authUrl) return {
|
|
@@ -5377,7 +5798,7 @@ var Agent = class Agent extends Server {
|
|
|
5377
5798
|
if (typeof urlOrBinding !== "string") {
|
|
5378
5799
|
const rpcOpts = callbackHostOrOptions;
|
|
5379
5800
|
const normalizedName = serverName.toLowerCase().replace(/\s+/g, "-");
|
|
5380
|
-
const reconnectId = existingServer?.id;
|
|
5801
|
+
const reconnectId = requestedId ?? existingServer?.id;
|
|
5381
5802
|
const { id } = await this.mcp.connect(`${RPC_DO_PREFIX}${normalizedName}`, {
|
|
5382
5803
|
reconnect: reconnectId ? { id: reconnectId } : void 0,
|
|
5383
5804
|
transport: {
|
|
@@ -5434,7 +5855,7 @@ var Agent = class Agent extends Server {
|
|
|
5434
5855
|
const normalizedHost = resolvedCallbackHost.replace(/\/$/, "");
|
|
5435
5856
|
callbackUrl = resolvedCallbackPath ? `${normalizedHost}/${resolvedCallbackPath.replace(/^\//, "")}` : `${normalizedHost}/${resolvedAgentsPrefix}/${camelCaseToKebabCase(this._ParentClass.name)}/${this.name}/callback`;
|
|
5436
5857
|
}
|
|
5437
|
-
const id = nanoid(8);
|
|
5858
|
+
const id = requestedId ?? nanoid(8);
|
|
5438
5859
|
let authProvider;
|
|
5439
5860
|
if (callbackUrl) {
|
|
5440
5861
|
authProvider = this.createMcpOAuthProvider(callbackUrl);
|
|
@@ -5766,6 +6187,6 @@ var StreamingResponse = class {
|
|
|
5766
6187
|
}
|
|
5767
6188
|
};
|
|
5768
6189
|
//#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 };
|
|
6190
|
+
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
6191
|
|
|
5771
6192
|
//# sourceMappingURL=index.js.map
|