agents 0.12.4 → 0.13.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 +8 -8
- package/dist/{agent-tool-types-CM_50fcV.d.ts → agent-tool-types-Dn9n-3SI.d.ts} +234 -85
- package/dist/agent-tool-types.d.ts +1 -1
- package/dist/{agent-tools-BylX6WXG.d.ts → agent-tools-B1ttU-pq.d.ts} +2 -2
- package/dist/agent-tools-BAdX1vdI.js.map +1 -1
- package/dist/agent-tools.d.ts +1 -1
- package/dist/agent-tools.js.map +1 -1
- package/dist/ai-chat-agent.js.map +1 -1
- package/dist/ai-chat-v5-migration.js.map +1 -1
- package/dist/ai-react.js.map +1 -1
- package/dist/ai-types.js.map +1 -1
- package/dist/browser/ai.js +1 -1
- package/dist/browser/ai.js.map +1 -1
- package/dist/browser/index.js +1 -1
- package/dist/browser/tanstack-ai.js +1 -1
- package/dist/browser/tanstack-ai.js.map +1 -1
- package/dist/chat/index.d.ts +2 -2
- package/dist/chat/index.js.map +1 -1
- package/dist/{classPrivateFieldGet2-CS51BNGR.js → classPrivateFieldGet2-Evpt0SEr.js} +5 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/client-D1kFXo80.js.map +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js.map +1 -1
- package/dist/codemode/ai.js.map +1 -1
- package/dist/{compaction-helpers-bYvP1o2S.d.ts → compaction-helpers-DAe-xiVY.d.ts} +33 -15
- package/dist/compaction-helpers-DvcZnvQ1.js.map +1 -1
- package/dist/email.d.ts +1 -1
- package/dist/email.js.map +1 -1
- package/dist/experimental/memory/session/index.d.ts +247 -34
- package/dist/experimental/memory/session/index.js +540 -135
- package/dist/experimental/memory/session/index.js.map +1 -1
- package/dist/experimental/memory/utils/index.d.ts +1 -1
- package/dist/experimental/memory/utils/index.js.map +1 -1
- package/dist/experimental/webmcp.js.map +1 -1
- package/dist/index.d.ts +71 -57
- package/dist/index.js +583 -45
- package/dist/index.js.map +1 -1
- package/dist/internal_context.js.map +1 -1
- package/dist/mcp/client.d.ts +12 -12
- package/dist/mcp/do-oauth-client-provider.js.map +1 -1
- package/dist/mcp/index.d.ts +40 -40
- package/dist/mcp/index.js +21 -45
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/x402.js.map +1 -1
- package/dist/observability/index.js.map +1 -1
- package/dist/react.d.ts +3 -3
- package/dist/react.js.map +1 -1
- package/dist/retries.js.map +1 -1
- package/dist/schedule.js.map +1 -1
- package/dist/serializable.d.ts +1 -1
- package/dist/{shared-DzJYHisH.js → shared-CiKaIK4h.js} +4 -5
- package/dist/{shared-DzJYHisH.js.map → shared-CiKaIK4h.js.map} +1 -1
- package/dist/sub-routing.d.ts +6 -6
- package/dist/sub-routing.js.map +1 -1
- package/dist/tool-output-truncation-CH-khbZ3.js.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils.js.map +1 -1
- package/dist/vite.js.map +1 -1
- package/dist/workflow-types.js.map +1 -1
- package/dist/workflows.d.ts +1 -1
- package/dist/workflows.js.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { __DO_NOT_USE_WILL_BREAK__agentContext } from "./internal_context.js";
|
|
2
2
|
import { MessageType } from "./types.js";
|
|
3
|
-
import { camelCaseToKebabCase } from "./utils.js";
|
|
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-Evpt0SEr.js";
|
|
6
6
|
import { SUB_PREFIX, getSubAgentByName, parseSubAgentPath, routeSubAgentRequest } from "./sub-routing.js";
|
|
7
7
|
import { isErrorRetryable, tryN, validateRetryOptions } from "./retries.js";
|
|
8
8
|
import { o as RPC_DO_PREFIX, r as MCPConnectionState, s as DisposableStore, t as MCPClientManager } from "./client-D1kFXo80.js";
|
|
@@ -12,7 +12,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
12
12
|
import { parseCronExpression } from "cron-schedule";
|
|
13
13
|
import { nanoid } from "nanoid";
|
|
14
14
|
import { EmailMessage } from "cloudflare:email";
|
|
15
|
-
import { RpcTarget } from "cloudflare:workers";
|
|
15
|
+
import { RpcTarget, exports } from "cloudflare:workers";
|
|
16
16
|
import { Server, getServerByName, routePartykitRequest } from "partyserver";
|
|
17
17
|
//#region src/index.ts
|
|
18
18
|
let _Symbol$dispose;
|
|
@@ -120,7 +120,7 @@ const DEFAULT_KEEP_ALIVE_INTERVAL_MS = 3e4;
|
|
|
120
120
|
* The constructor stores this as a row in cf_agents_state and checks it
|
|
121
121
|
* on wake to skip DDL on established DOs.
|
|
122
122
|
*/
|
|
123
|
-
const CURRENT_SCHEMA_VERSION =
|
|
123
|
+
const CURRENT_SCHEMA_VERSION = 8;
|
|
124
124
|
const SCHEMA_VERSION_ROW_ID = "cf_schema_version";
|
|
125
125
|
const STATE_ROW_ID = "cf_state_row_id";
|
|
126
126
|
const STATE_WAS_CHANGED = "cf_state_was_changed";
|
|
@@ -549,6 +549,32 @@ var Agent = class Agent extends Server {
|
|
|
549
549
|
this.sql`
|
|
550
550
|
CREATE INDEX IF NOT EXISTS idx_facet_runs_owner_path_key
|
|
551
551
|
ON cf_agents_facet_runs(owner_path_key)
|
|
552
|
+
`;
|
|
553
|
+
this.sql`
|
|
554
|
+
CREATE TABLE IF NOT EXISTS cf_agents_fibers (
|
|
555
|
+
fiber_id TEXT PRIMARY KEY,
|
|
556
|
+
idempotency_key TEXT UNIQUE,
|
|
557
|
+
name TEXT NOT NULL,
|
|
558
|
+
status TEXT NOT NULL,
|
|
559
|
+
snapshot TEXT,
|
|
560
|
+
metadata_json TEXT,
|
|
561
|
+
error_message TEXT,
|
|
562
|
+
created_at INTEGER NOT NULL,
|
|
563
|
+
started_at INTEGER,
|
|
564
|
+
completed_at INTEGER
|
|
565
|
+
)
|
|
566
|
+
`;
|
|
567
|
+
this.sql`
|
|
568
|
+
CREATE INDEX IF NOT EXISTS idx_fibers_status_created
|
|
569
|
+
ON cf_agents_fibers(status, created_at, fiber_id)
|
|
570
|
+
`;
|
|
571
|
+
this.sql`
|
|
572
|
+
CREATE INDEX IF NOT EXISTS idx_fibers_name_status_created
|
|
573
|
+
ON cf_agents_fibers(name, status, created_at, fiber_id)
|
|
574
|
+
`;
|
|
575
|
+
this.sql`
|
|
576
|
+
CREATE INDEX IF NOT EXISTS idx_fibers_status_completed
|
|
577
|
+
ON cf_agents_fibers(status, completed_at, created_at)
|
|
552
578
|
`;
|
|
553
579
|
this.sql`
|
|
554
580
|
CREATE TABLE IF NOT EXISTS cf_agent_tool_runs (
|
|
@@ -595,6 +621,9 @@ var Agent = class Agent extends Server {
|
|
|
595
621
|
this._keepAliveRefs = 0;
|
|
596
622
|
this._facetKeepAliveTokens = /* @__PURE__ */ new Set();
|
|
597
623
|
this._runFiberActiveFibers = /* @__PURE__ */ new Set();
|
|
624
|
+
this._managedFiberAbortControllers = /* @__PURE__ */ new Map();
|
|
625
|
+
this._managedFiberExecutions = /* @__PURE__ */ new Map();
|
|
626
|
+
this._managedFiberTerminalWaiters = /* @__PURE__ */ new Map();
|
|
598
627
|
this._runFiberRecoveryInProgress = false;
|
|
599
628
|
this._ParentClass = Object.getPrototypeOf(this).constructor;
|
|
600
629
|
this.initialState = DEFAULT_STATE;
|
|
@@ -2211,6 +2240,295 @@ var Agent = class Agent extends Server {
|
|
|
2211
2240
|
dispose();
|
|
2212
2241
|
}
|
|
2213
2242
|
}
|
|
2243
|
+
_isTerminalFiberStatus(status) {
|
|
2244
|
+
return status === "completed" || status === "aborted" || status === "interrupted" || status === "error";
|
|
2245
|
+
}
|
|
2246
|
+
_notifyManagedFiberTerminal(fiberId) {
|
|
2247
|
+
const row = this._readFiber(fiberId);
|
|
2248
|
+
if (row && !this._isTerminalFiberStatus(row.status)) return;
|
|
2249
|
+
const waiters = this._managedFiberTerminalWaiters.get(fiberId);
|
|
2250
|
+
if (!waiters) return;
|
|
2251
|
+
this._managedFiberTerminalWaiters.delete(fiberId);
|
|
2252
|
+
for (const resolve of waiters) resolve();
|
|
2253
|
+
}
|
|
2254
|
+
_waitForManagedFiberTerminal(fiberId) {
|
|
2255
|
+
const row = this._readFiber(fiberId);
|
|
2256
|
+
if (!row || this._isTerminalFiberStatus(row.status)) return Promise.resolve();
|
|
2257
|
+
return new Promise((resolve) => {
|
|
2258
|
+
let waiters = this._managedFiberTerminalWaiters.get(fiberId);
|
|
2259
|
+
if (!waiters) {
|
|
2260
|
+
waiters = /* @__PURE__ */ new Set();
|
|
2261
|
+
this._managedFiberTerminalWaiters.set(fiberId, waiters);
|
|
2262
|
+
}
|
|
2263
|
+
waiters.add(resolve);
|
|
2264
|
+
});
|
|
2265
|
+
}
|
|
2266
|
+
_normalizeFiberStatusFilter(status) {
|
|
2267
|
+
if (!status) return null;
|
|
2268
|
+
return new Set(Array.isArray(status) ? status : [status]);
|
|
2269
|
+
}
|
|
2270
|
+
_parseFiberJsonObject(value) {
|
|
2271
|
+
if (value === null) return null;
|
|
2272
|
+
try {
|
|
2273
|
+
const parsed = JSON.parse(value);
|
|
2274
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
|
|
2275
|
+
} catch {}
|
|
2276
|
+
return null;
|
|
2277
|
+
}
|
|
2278
|
+
_parseFiberSnapshot(value) {
|
|
2279
|
+
if (value === null) return void 0;
|
|
2280
|
+
try {
|
|
2281
|
+
return JSON.parse(value);
|
|
2282
|
+
} catch {
|
|
2283
|
+
return;
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
_fiberErrorMessage(error) {
|
|
2287
|
+
return error instanceof Error ? error.message : String(error);
|
|
2288
|
+
}
|
|
2289
|
+
_stringifyFiberSnapshot(snapshot) {
|
|
2290
|
+
return snapshot === void 0 ? null : JSON.stringify(snapshot);
|
|
2291
|
+
}
|
|
2292
|
+
_fiberRecoveryErrorMessage(result) {
|
|
2293
|
+
if (result.status === "error") return result.error === void 0 ? null : this._fiberErrorMessage(result.error);
|
|
2294
|
+
if (result.status === "aborted" || result.status === "interrupted") return result.reason ?? null;
|
|
2295
|
+
return null;
|
|
2296
|
+
}
|
|
2297
|
+
_applyManagedFiberRecoveryResult(fiberId, result) {
|
|
2298
|
+
const completedAt = Date.now();
|
|
2299
|
+
const snapshot = this._stringifyFiberSnapshot(result.snapshot);
|
|
2300
|
+
const errorMessage = this._fiberRecoveryErrorMessage(result);
|
|
2301
|
+
const metadata = result.status === "completed" && result.metadata !== void 0 ? JSON.stringify(result.metadata) : void 0;
|
|
2302
|
+
if (metadata !== void 0) {
|
|
2303
|
+
this.sql`
|
|
2304
|
+
UPDATE cf_agents_fibers
|
|
2305
|
+
SET status = ${result.status},
|
|
2306
|
+
snapshot = COALESCE(${snapshot}, snapshot),
|
|
2307
|
+
metadata_json = ${metadata},
|
|
2308
|
+
error_message = ${errorMessage},
|
|
2309
|
+
completed_at = ${completedAt}
|
|
2310
|
+
WHERE fiber_id = ${fiberId}
|
|
2311
|
+
AND status = 'interrupted'
|
|
2312
|
+
`;
|
|
2313
|
+
this._notifyManagedFiberTerminal(fiberId);
|
|
2314
|
+
return;
|
|
2315
|
+
}
|
|
2316
|
+
this.sql`
|
|
2317
|
+
UPDATE cf_agents_fibers
|
|
2318
|
+
SET status = ${result.status},
|
|
2319
|
+
snapshot = COALESCE(${snapshot}, snapshot),
|
|
2320
|
+
error_message = ${errorMessage},
|
|
2321
|
+
completed_at = ${completedAt}
|
|
2322
|
+
WHERE fiber_id = ${fiberId}
|
|
2323
|
+
AND status = 'interrupted'
|
|
2324
|
+
`;
|
|
2325
|
+
this._notifyManagedFiberTerminal(fiberId);
|
|
2326
|
+
}
|
|
2327
|
+
_settleManagedFiberExecution(fiberId, outcome, signal) {
|
|
2328
|
+
const completedAt = Date.now();
|
|
2329
|
+
if (outcome.ok) {
|
|
2330
|
+
this.sql`
|
|
2331
|
+
UPDATE cf_agents_fibers
|
|
2332
|
+
SET status = 'completed', completed_at = ${completedAt}
|
|
2333
|
+
WHERE fiber_id = ${fiberId} AND status = 'running'
|
|
2334
|
+
`;
|
|
2335
|
+
this._notifyManagedFiberTerminal(fiberId);
|
|
2336
|
+
return;
|
|
2337
|
+
}
|
|
2338
|
+
const message = this._fiberErrorMessage(outcome.error);
|
|
2339
|
+
const status = signal.aborted ? "aborted" : "error";
|
|
2340
|
+
this.sql`
|
|
2341
|
+
UPDATE cf_agents_fibers
|
|
2342
|
+
SET status = ${status},
|
|
2343
|
+
error_message = ${message},
|
|
2344
|
+
completed_at = ${completedAt}
|
|
2345
|
+
WHERE fiber_id = ${fiberId} AND status = 'running'
|
|
2346
|
+
`;
|
|
2347
|
+
this._notifyManagedFiberTerminal(fiberId);
|
|
2348
|
+
}
|
|
2349
|
+
_parseFiberRecoverySnapshot(fiberId, snapshotText) {
|
|
2350
|
+
if (!snapshotText) return null;
|
|
2351
|
+
try {
|
|
2352
|
+
return JSON.parse(snapshotText);
|
|
2353
|
+
} catch {
|
|
2354
|
+
console.warn(`[Agent] Corrupted snapshot for fiber ${fiberId}, treating as null`);
|
|
2355
|
+
return null;
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
async _runFiberRecoveryHook(ctx, managedRow) {
|
|
2359
|
+
try {
|
|
2360
|
+
if (!await this._handleInternalFiberRecovery(ctx)) {
|
|
2361
|
+
const recoveryResult = await this.onFiberRecovered(ctx);
|
|
2362
|
+
if (managedRow && recoveryResult) this._applyManagedFiberRecoveryResult(ctx.id, recoveryResult);
|
|
2363
|
+
}
|
|
2364
|
+
} catch (e) {
|
|
2365
|
+
if (managedRow) this.sql`
|
|
2366
|
+
UPDATE cf_agents_fibers
|
|
2367
|
+
SET error_message = ${this._fiberErrorMessage(e)}
|
|
2368
|
+
WHERE fiber_id = ${ctx.id}
|
|
2369
|
+
AND status = 'interrupted'
|
|
2370
|
+
`;
|
|
2371
|
+
console.error(`[Agent] Fiber recovery failed for "${ctx.name}" (${ctx.id}):`, e);
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
_fiberInspectionFromRow(row) {
|
|
2375
|
+
const snapshot = this._parseFiberSnapshot(row.snapshot);
|
|
2376
|
+
const inspection = {
|
|
2377
|
+
fiberId: row.fiber_id,
|
|
2378
|
+
name: row.name,
|
|
2379
|
+
status: row.status,
|
|
2380
|
+
createdAt: row.created_at
|
|
2381
|
+
};
|
|
2382
|
+
if (row.idempotency_key !== null) inspection.idempotencyKey = row.idempotency_key;
|
|
2383
|
+
if (snapshot !== void 0) inspection.snapshot = snapshot;
|
|
2384
|
+
if (row.error_message !== null) inspection.error = row.error_message;
|
|
2385
|
+
const metadata = this._parseFiberJsonObject(row.metadata_json);
|
|
2386
|
+
if (metadata !== null) inspection.metadata = metadata;
|
|
2387
|
+
if (row.started_at !== null) inspection.startedAt = row.started_at;
|
|
2388
|
+
if (row.completed_at !== null) inspection.settledAt = row.completed_at;
|
|
2389
|
+
return inspection;
|
|
2390
|
+
}
|
|
2391
|
+
async _waitForManagedFiber(fiberId) {
|
|
2392
|
+
const row = this._readFiber(fiberId);
|
|
2393
|
+
if (!row || this._isTerminalFiberStatus(row.status)) return row ? this._fiberInspectionFromRow(row) : null;
|
|
2394
|
+
if (this._managedFiberExecutions.has(fiberId)) {
|
|
2395
|
+
await this._waitForManagedFiberTerminal(fiberId);
|
|
2396
|
+
return this.inspectFiber(fiberId);
|
|
2397
|
+
}
|
|
2398
|
+
await this._checkRunFibers();
|
|
2399
|
+
await this._waitForManagedFiberTerminal(fiberId);
|
|
2400
|
+
return this.inspectFiber(fiberId);
|
|
2401
|
+
}
|
|
2402
|
+
_readFiber(fiberId) {
|
|
2403
|
+
return this.sql`
|
|
2404
|
+
SELECT fiber_id, idempotency_key, name, status, snapshot, metadata_json,
|
|
2405
|
+
error_message, created_at, started_at, completed_at
|
|
2406
|
+
FROM cf_agents_fibers
|
|
2407
|
+
WHERE fiber_id = ${fiberId}
|
|
2408
|
+
LIMIT 1
|
|
2409
|
+
`[0] ?? null;
|
|
2410
|
+
}
|
|
2411
|
+
_readFiberByKey(idempotencyKey) {
|
|
2412
|
+
return this.sql`
|
|
2413
|
+
SELECT fiber_id, idempotency_key, name, status, snapshot, metadata_json,
|
|
2414
|
+
error_message, created_at, started_at, completed_at
|
|
2415
|
+
FROM cf_agents_fibers
|
|
2416
|
+
WHERE idempotency_key = ${idempotencyKey}
|
|
2417
|
+
LIMIT 1
|
|
2418
|
+
`[0] ?? null;
|
|
2419
|
+
}
|
|
2420
|
+
_listFiberRows(options) {
|
|
2421
|
+
const limit = Math.min(Math.max(options?.limit ?? 50, 1), 100);
|
|
2422
|
+
const statuses = this._normalizeFiberStatusFilter(options?.status);
|
|
2423
|
+
if (statuses) return [...statuses].flatMap((status) => this._listFiberRowsByStatus(status, limit, options?.name)).sort((a, b) => b.created_at === a.created_at ? b.fiber_id.localeCompare(a.fiber_id) : b.created_at - a.created_at).slice(0, limit);
|
|
2424
|
+
if (options?.name) return this.sql`
|
|
2425
|
+
SELECT fiber_id, idempotency_key, name, status, snapshot, metadata_json,
|
|
2426
|
+
error_message, created_at, started_at, completed_at
|
|
2427
|
+
FROM cf_agents_fibers
|
|
2428
|
+
WHERE name = ${options.name}
|
|
2429
|
+
ORDER BY created_at DESC, fiber_id DESC
|
|
2430
|
+
LIMIT ${limit}
|
|
2431
|
+
`;
|
|
2432
|
+
return this.sql`
|
|
2433
|
+
SELECT fiber_id, idempotency_key, name, status, snapshot, metadata_json,
|
|
2434
|
+
error_message, created_at, started_at, completed_at
|
|
2435
|
+
FROM cf_agents_fibers
|
|
2436
|
+
ORDER BY created_at DESC, fiber_id DESC
|
|
2437
|
+
LIMIT ${limit}
|
|
2438
|
+
`;
|
|
2439
|
+
}
|
|
2440
|
+
_listFiberRowsByStatus(status, limit, name) {
|
|
2441
|
+
if (name) return this.sql`
|
|
2442
|
+
SELECT fiber_id, idempotency_key, name, status, snapshot, metadata_json,
|
|
2443
|
+
error_message, created_at, started_at, completed_at
|
|
2444
|
+
FROM cf_agents_fibers
|
|
2445
|
+
WHERE status = ${status} AND name = ${name}
|
|
2446
|
+
ORDER BY created_at DESC, fiber_id DESC
|
|
2447
|
+
LIMIT ${limit}
|
|
2448
|
+
`;
|
|
2449
|
+
return this.sql`
|
|
2450
|
+
SELECT fiber_id, idempotency_key, name, status, snapshot, metadata_json,
|
|
2451
|
+
error_message, created_at, started_at, completed_at
|
|
2452
|
+
FROM cf_agents_fibers
|
|
2453
|
+
WHERE status = ${status}
|
|
2454
|
+
ORDER BY created_at DESC, fiber_id DESC
|
|
2455
|
+
LIMIT ${limit}
|
|
2456
|
+
`;
|
|
2457
|
+
}
|
|
2458
|
+
async inspectFiber(fiberId) {
|
|
2459
|
+
const row = this._readFiber(fiberId);
|
|
2460
|
+
return row ? this._fiberInspectionFromRow(row) : null;
|
|
2461
|
+
}
|
|
2462
|
+
async inspectFiberByKey(idempotencyKey) {
|
|
2463
|
+
const row = this._readFiberByKey(idempotencyKey);
|
|
2464
|
+
return row ? this._fiberInspectionFromRow(row) : null;
|
|
2465
|
+
}
|
|
2466
|
+
async listFibers(options) {
|
|
2467
|
+
return this._listFiberRows(options).map((row) => this._fiberInspectionFromRow(row));
|
|
2468
|
+
}
|
|
2469
|
+
async cancelFiber(fiberId, reason) {
|
|
2470
|
+
const row = this._readFiber(fiberId);
|
|
2471
|
+
if (!row || this._isTerminalFiberStatus(row.status)) return false;
|
|
2472
|
+
const now = Date.now();
|
|
2473
|
+
this.sql`
|
|
2474
|
+
UPDATE cf_agents_fibers
|
|
2475
|
+
SET status = 'aborted',
|
|
2476
|
+
error_message = ${reason ?? null},
|
|
2477
|
+
completed_at = ${now}
|
|
2478
|
+
WHERE fiber_id = ${fiberId}
|
|
2479
|
+
AND status IN ('pending', 'running')
|
|
2480
|
+
`;
|
|
2481
|
+
this._managedFiberAbortControllers.get(fiberId)?.abort(reason);
|
|
2482
|
+
this._notifyManagedFiberTerminal(fiberId);
|
|
2483
|
+
return true;
|
|
2484
|
+
}
|
|
2485
|
+
async cancelFiberByKey(idempotencyKey, reason) {
|
|
2486
|
+
const row = this._readFiberByKey(idempotencyKey);
|
|
2487
|
+
return row ? this.cancelFiber(row.fiber_id, reason) : false;
|
|
2488
|
+
}
|
|
2489
|
+
async resolveFiber(fiberId, result) {
|
|
2490
|
+
const row = this._readFiber(fiberId);
|
|
2491
|
+
if (!row || row.status !== "interrupted") return false;
|
|
2492
|
+
this._applyManagedFiberRecoveryResult(fiberId, result);
|
|
2493
|
+
return true;
|
|
2494
|
+
}
|
|
2495
|
+
async deleteFibers(options) {
|
|
2496
|
+
const terminalStatuses = [...this._normalizeFiberStatusFilter(options?.status) ?? new Set([
|
|
2497
|
+
"completed",
|
|
2498
|
+
"aborted",
|
|
2499
|
+
"error"
|
|
2500
|
+
])].filter((status) => this._isTerminalFiberStatus(status));
|
|
2501
|
+
if (terminalStatuses.length === 0) return 0;
|
|
2502
|
+
const limit = Math.min(Math.max(options?.limit ?? 100, 1), 500);
|
|
2503
|
+
const settledBefore = options?.settledBefore?.getTime();
|
|
2504
|
+
const rows = terminalStatuses.flatMap((status) => this._listTerminalFiberRowsForDelete(status, limit, settledBefore)).sort((a, b) => a.completed_at === b.completed_at ? a.created_at - b.created_at : (a.completed_at ?? 0) - (b.completed_at ?? 0)).slice(0, limit);
|
|
2505
|
+
for (const row of rows) this.sql`
|
|
2506
|
+
DELETE FROM cf_agents_fibers
|
|
2507
|
+
WHERE fiber_id = ${row.fiber_id}
|
|
2508
|
+
AND status IN ('completed', 'aborted', 'interrupted', 'error')
|
|
2509
|
+
`;
|
|
2510
|
+
return rows.length;
|
|
2511
|
+
}
|
|
2512
|
+
_listTerminalFiberRowsForDelete(status, limit, settledBefore) {
|
|
2513
|
+
if (settledBefore !== void 0) return this.sql`
|
|
2514
|
+
SELECT fiber_id, idempotency_key, name, status, snapshot, metadata_json,
|
|
2515
|
+
error_message, created_at, started_at, completed_at
|
|
2516
|
+
FROM cf_agents_fibers
|
|
2517
|
+
WHERE status = ${status}
|
|
2518
|
+
AND completed_at IS NOT NULL
|
|
2519
|
+
AND completed_at < ${settledBefore}
|
|
2520
|
+
ORDER BY completed_at ASC, created_at ASC
|
|
2521
|
+
LIMIT ${limit}
|
|
2522
|
+
`;
|
|
2523
|
+
return this.sql`
|
|
2524
|
+
SELECT fiber_id, idempotency_key, name, status, snapshot, metadata_json,
|
|
2525
|
+
error_message, created_at, started_at, completed_at
|
|
2526
|
+
FROM cf_agents_fibers
|
|
2527
|
+
WHERE status = ${status}
|
|
2528
|
+
ORDER BY completed_at ASC, created_at ASC
|
|
2529
|
+
LIMIT ${limit}
|
|
2530
|
+
`;
|
|
2531
|
+
}
|
|
2214
2532
|
/**
|
|
2215
2533
|
* Run a function as a durable fiber. The fiber is registered in SQLite
|
|
2216
2534
|
* before execution, checkpointable during execution via `ctx.stash()`,
|
|
@@ -2225,7 +2543,100 @@ var Agent = class Agent extends Server {
|
|
|
2225
2543
|
* @returns The return value of fn
|
|
2226
2544
|
*/
|
|
2227
2545
|
async runFiber(name, fn) {
|
|
2228
|
-
|
|
2546
|
+
return this._runFiberInternal(nanoid(), name, fn);
|
|
2547
|
+
}
|
|
2548
|
+
async startFiber(name, fn, options) {
|
|
2549
|
+
const fiberId = options?.fiberId ?? nanoid();
|
|
2550
|
+
const idempotencyKey = options?.idempotencyKey;
|
|
2551
|
+
if (options?.fiberId !== void 0 && options.fiberId.trim() === "") throw new Error("fiberId must not be blank");
|
|
2552
|
+
if (options?.idempotencyKey !== void 0 && options.idempotencyKey.trim() === "") throw new Error("idempotencyKey must not be blank");
|
|
2553
|
+
const existingById = this._readFiber(fiberId);
|
|
2554
|
+
const existingByKey = idempotencyKey ? this._readFiberByKey(idempotencyKey) : null;
|
|
2555
|
+
if (existingById && existingByKey && existingById.fiber_id !== existingByKey.fiber_id) throw new Error("fiberId and idempotencyKey refer to different fibers");
|
|
2556
|
+
if (existingByKey && options?.fiberId && existingByKey.fiber_id !== fiberId) throw new Error("fiberId and idempotencyKey refer to different fibers");
|
|
2557
|
+
const existing = existingById ?? existingByKey;
|
|
2558
|
+
if (existing) {
|
|
2559
|
+
if (options?.waitForCompletion && !this._isTerminalFiberStatus(existing.status)) {
|
|
2560
|
+
const waited = await this._waitForManagedFiber(existing.fiber_id);
|
|
2561
|
+
if (waited) return {
|
|
2562
|
+
...waited,
|
|
2563
|
+
accepted: false
|
|
2564
|
+
};
|
|
2565
|
+
throw new Error(`Fiber ${existing.fiber_id} no longer exists`);
|
|
2566
|
+
}
|
|
2567
|
+
return {
|
|
2568
|
+
...this._fiberInspectionFromRow(existing),
|
|
2569
|
+
accepted: false
|
|
2570
|
+
};
|
|
2571
|
+
}
|
|
2572
|
+
const now = Date.now();
|
|
2573
|
+
this.sql`
|
|
2574
|
+
INSERT INTO cf_agents_fibers
|
|
2575
|
+
(fiber_id, idempotency_key, name, status, snapshot, metadata_json,
|
|
2576
|
+
error_message, created_at, started_at, completed_at)
|
|
2577
|
+
VALUES
|
|
2578
|
+
(${fiberId}, ${idempotencyKey ?? null}, ${name}, 'pending', NULL,
|
|
2579
|
+
${options?.metadata ? JSON.stringify(options.metadata) : null}, NULL,
|
|
2580
|
+
${now}, NULL, NULL)
|
|
2581
|
+
`;
|
|
2582
|
+
const row = this._readFiber(fiberId);
|
|
2583
|
+
if (!row) throw new Error(`Failed to create fiber ${fiberId}`);
|
|
2584
|
+
const execution = this._executeManagedFiber(fiberId, name, fn).catch((error) => {
|
|
2585
|
+
console.error(`[Agent] Managed fiber "${name}" (${fiberId}) failed:`, error);
|
|
2586
|
+
}).finally(() => {
|
|
2587
|
+
if (this._managedFiberExecutions.get(fiberId) === execution) this._managedFiberExecutions.delete(fiberId);
|
|
2588
|
+
});
|
|
2589
|
+
this._managedFiberExecutions.set(fiberId, execution);
|
|
2590
|
+
if (options?.waitForCompletion) {
|
|
2591
|
+
const completed = await this._waitForManagedFiber(fiberId);
|
|
2592
|
+
if (!completed) throw new Error(`Fiber ${fiberId} no longer exists`);
|
|
2593
|
+
return {
|
|
2594
|
+
...completed,
|
|
2595
|
+
accepted: true
|
|
2596
|
+
};
|
|
2597
|
+
}
|
|
2598
|
+
return {
|
|
2599
|
+
...this._fiberInspectionFromRow(row),
|
|
2600
|
+
accepted: true
|
|
2601
|
+
};
|
|
2602
|
+
}
|
|
2603
|
+
async _executeManagedFiber(fiberId, name, fn) {
|
|
2604
|
+
const row = this._readFiber(fiberId);
|
|
2605
|
+
if (!row || row.status !== "pending") return;
|
|
2606
|
+
const controller = new AbortController();
|
|
2607
|
+
this._managedFiberAbortControllers.set(fiberId, controller);
|
|
2608
|
+
const now = Date.now();
|
|
2609
|
+
this.sql`
|
|
2610
|
+
UPDATE cf_agents_fibers
|
|
2611
|
+
SET status = 'running', started_at = ${now}
|
|
2612
|
+
WHERE fiber_id = ${fiberId} AND status = 'pending'
|
|
2613
|
+
`;
|
|
2614
|
+
const updated = this._readFiber(fiberId);
|
|
2615
|
+
if (!updated || updated.status !== "running") {
|
|
2616
|
+
this._managedFiberAbortControllers.delete(fiberId);
|
|
2617
|
+
return;
|
|
2618
|
+
}
|
|
2619
|
+
let settled = false;
|
|
2620
|
+
try {
|
|
2621
|
+
await this._runFiberInternal(fiberId, name, fn, {
|
|
2622
|
+
signal: controller.signal,
|
|
2623
|
+
managed: true,
|
|
2624
|
+
beforeRunCleanup: (outcome) => {
|
|
2625
|
+
settled = true;
|
|
2626
|
+
this._settleManagedFiberExecution(fiberId, outcome, controller.signal);
|
|
2627
|
+
}
|
|
2628
|
+
});
|
|
2629
|
+
} catch (error) {
|
|
2630
|
+
if (!settled) this._settleManagedFiberExecution(fiberId, {
|
|
2631
|
+
ok: false,
|
|
2632
|
+
error
|
|
2633
|
+
}, controller.signal);
|
|
2634
|
+
} finally {
|
|
2635
|
+
this._managedFiberAbortControllers.delete(fiberId);
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
async _runFiberInternal(id, name, fn, options) {
|
|
2639
|
+
const signal = options?.signal ?? new AbortController().signal;
|
|
2229
2640
|
this.sql`
|
|
2230
2641
|
INSERT INTO cf_agents_runs (id, name, snapshot, created_at)
|
|
2231
2642
|
VALUES (${id}, ${name}, NULL, ${Date.now()})
|
|
@@ -2242,19 +2653,36 @@ var Agent = class Agent extends Server {
|
|
|
2242
2653
|
}
|
|
2243
2654
|
dispose = await this.keepAlive();
|
|
2244
2655
|
const stash = (data) => {
|
|
2656
|
+
const snapshot = JSON.stringify(data);
|
|
2245
2657
|
this.sql`
|
|
2246
|
-
UPDATE cf_agents_runs SET snapshot = ${
|
|
2658
|
+
UPDATE cf_agents_runs SET snapshot = ${snapshot}
|
|
2247
2659
|
WHERE id = ${id}
|
|
2248
2660
|
`;
|
|
2661
|
+
if (options?.managed) this.sql`
|
|
2662
|
+
UPDATE cf_agents_fibers SET snapshot = ${snapshot}
|
|
2663
|
+
WHERE fiber_id = ${id}
|
|
2664
|
+
`;
|
|
2249
2665
|
};
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2666
|
+
try {
|
|
2667
|
+
const result = await _fiberALS.run({
|
|
2668
|
+
id,
|
|
2669
|
+
signal,
|
|
2670
|
+
stash
|
|
2671
|
+
}, () => fn({
|
|
2672
|
+
id,
|
|
2673
|
+
signal,
|
|
2674
|
+
stash,
|
|
2675
|
+
snapshot: null
|
|
2676
|
+
}));
|
|
2677
|
+
options?.beforeRunCleanup?.({ ok: true });
|
|
2678
|
+
return result;
|
|
2679
|
+
} catch (error) {
|
|
2680
|
+
options?.beforeRunCleanup?.({
|
|
2681
|
+
ok: false,
|
|
2682
|
+
error
|
|
2683
|
+
});
|
|
2684
|
+
throw error;
|
|
2685
|
+
}
|
|
2258
2686
|
} finally {
|
|
2259
2687
|
this._runFiberActiveFibers.delete(id);
|
|
2260
2688
|
this.sql`DELETE FROM cf_agents_runs WHERE id = ${id}`;
|
|
@@ -2306,24 +2734,67 @@ var Agent = class Agent extends Server {
|
|
|
2306
2734
|
const rows = this.sql`SELECT id, name, snapshot, created_at FROM cf_agents_runs`;
|
|
2307
2735
|
for (const row of rows) {
|
|
2308
2736
|
if (this._runFiberActiveFibers.has(row.id)) continue;
|
|
2309
|
-
|
|
2310
|
-
if (row.snapshot) try {
|
|
2311
|
-
snapshot = JSON.parse(row.snapshot);
|
|
2312
|
-
} catch {
|
|
2313
|
-
console.warn(`[Agent] Corrupted snapshot for fiber ${row.id}, treating as null`);
|
|
2314
|
-
}
|
|
2737
|
+
const snapshot = this._parseFiberRecoverySnapshot(row.id, row.snapshot);
|
|
2315
2738
|
const ctx = {
|
|
2316
2739
|
id: row.id,
|
|
2317
2740
|
name: row.name,
|
|
2318
2741
|
snapshot,
|
|
2319
2742
|
createdAt: row.created_at
|
|
2320
2743
|
};
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2744
|
+
const managedRow = this._readFiber(row.id);
|
|
2745
|
+
if (managedRow) {
|
|
2746
|
+
if (this._isTerminalFiberStatus(managedRow.status)) {
|
|
2747
|
+
this.sql`DELETE FROM cf_agents_runs WHERE id = ${row.id}`;
|
|
2748
|
+
this._notifyManagedFiberTerminal(row.id);
|
|
2749
|
+
continue;
|
|
2750
|
+
}
|
|
2751
|
+
const completedAt = Date.now();
|
|
2752
|
+
this.sql`
|
|
2753
|
+
UPDATE cf_agents_fibers
|
|
2754
|
+
SET status = 'interrupted',
|
|
2755
|
+
snapshot = ${row.snapshot},
|
|
2756
|
+
completed_at = ${completedAt}
|
|
2757
|
+
WHERE fiber_id = ${row.id}
|
|
2758
|
+
AND status IN ('pending', 'running')
|
|
2759
|
+
`;
|
|
2760
|
+
ctx.idempotencyKey = managedRow.idempotency_key ?? void 0;
|
|
2761
|
+
ctx.metadata = this._parseFiberJsonObject(managedRow.metadata_json);
|
|
2762
|
+
ctx.status = "interrupted";
|
|
2325
2763
|
}
|
|
2764
|
+
await this._runFiberRecoveryHook(ctx, managedRow);
|
|
2326
2765
|
this.sql`DELETE FROM cf_agents_runs WHERE id = ${row.id}`;
|
|
2766
|
+
if (managedRow) this._notifyManagedFiberTerminal(row.id);
|
|
2767
|
+
}
|
|
2768
|
+
const ledgerOnlyRows = this.sql`
|
|
2769
|
+
SELECT f.fiber_id, f.idempotency_key, f.name, f.status, f.snapshot,
|
|
2770
|
+
f.metadata_json, f.error_message, f.created_at, f.started_at,
|
|
2771
|
+
f.completed_at
|
|
2772
|
+
FROM cf_agents_fibers f
|
|
2773
|
+
LEFT JOIN cf_agents_runs r ON r.id = f.fiber_id
|
|
2774
|
+
WHERE f.status IN ('pending', 'running')
|
|
2775
|
+
AND r.id IS NULL
|
|
2776
|
+
`;
|
|
2777
|
+
for (const row of ledgerOnlyRows) {
|
|
2778
|
+
if (this._runFiberActiveFibers.has(row.fiber_id)) continue;
|
|
2779
|
+
const snapshot = this._parseFiberRecoverySnapshot(row.fiber_id, row.snapshot);
|
|
2780
|
+
const completedAt = Date.now();
|
|
2781
|
+
this.sql`
|
|
2782
|
+
UPDATE cf_agents_fibers
|
|
2783
|
+
SET status = 'interrupted',
|
|
2784
|
+
completed_at = ${completedAt}
|
|
2785
|
+
WHERE fiber_id = ${row.fiber_id}
|
|
2786
|
+
AND status IN ('pending', 'running')
|
|
2787
|
+
`;
|
|
2788
|
+
await this._runFiberRecoveryHook({
|
|
2789
|
+
id: row.fiber_id,
|
|
2790
|
+
name: row.name,
|
|
2791
|
+
snapshot,
|
|
2792
|
+
createdAt: row.created_at,
|
|
2793
|
+
idempotencyKey: row.idempotency_key ?? void 0,
|
|
2794
|
+
metadata: this._parseFiberJsonObject(row.metadata_json),
|
|
2795
|
+
status: "interrupted"
|
|
2796
|
+
}, row);
|
|
2797
|
+
this._notifyManagedFiberTerminal(row.fiber_id);
|
|
2327
2798
|
}
|
|
2328
2799
|
} finally {
|
|
2329
2800
|
this._runFiberRecoveryInProgress = false;
|
|
@@ -3092,7 +3563,29 @@ var Agent = class Agent extends Server {
|
|
|
3092
3563
|
* @internal
|
|
3093
3564
|
*/
|
|
3094
3565
|
async _cf_invokeSubAgent(className, name, method, args) {
|
|
3095
|
-
const
|
|
3566
|
+
const stub = await this._cf_resolveSubAgent(className, name);
|
|
3567
|
+
return await this._cf_invokeStubMethod(stub, className, method, args);
|
|
3568
|
+
}
|
|
3569
|
+
/**
|
|
3570
|
+
* Bridge method used by `parentAgent()` when the requested parent is
|
|
3571
|
+
* itself a facet (and therefore has no top-level env namespace).
|
|
3572
|
+
* The root receives the full root-first target path, then each hop
|
|
3573
|
+
* delegates to the next facet using that facet's own `ctx.facets`.
|
|
3574
|
+
*
|
|
3575
|
+
* @internal
|
|
3576
|
+
*/
|
|
3577
|
+
async _cf_invokeSubAgentPath(path, method, args) {
|
|
3578
|
+
const [self, next, ...rest] = path;
|
|
3579
|
+
if (!self) throw new Error(`Sub-agent path invocation requires a non-empty path.`);
|
|
3580
|
+
const ownClassName = this.constructor.name;
|
|
3581
|
+
if (self.className !== ownClassName || self.name !== this.name) throw new Error(`Sub-agent path invocation reached ${ownClassName}("${this.name}") but expected ${self.className}("${self.name}").`);
|
|
3582
|
+
if (!next) return await this._cf_invokeStubMethod(this, this.constructor.name, method, args);
|
|
3583
|
+
const child = await this._cf_resolveSubAgent(next.className, next.name);
|
|
3584
|
+
if (rest.length === 0) return await this._cf_invokeStubMethod(child, next.className, method, args);
|
|
3585
|
+
return await child._cf_invokeSubAgentPath([next, ...rest], method, args);
|
|
3586
|
+
}
|
|
3587
|
+
async _cf_invokeStubMethod(stub, className, method, args) {
|
|
3588
|
+
const handle = stub;
|
|
3096
3589
|
if (typeof handle[method] !== "function") throw new Error(`Method "${method}" not found on ${className}.`);
|
|
3097
3590
|
return await handle[method](...args);
|
|
3098
3591
|
}
|
|
@@ -3116,11 +3609,11 @@ var Agent = class Agent extends Server {
|
|
|
3116
3609
|
*
|
|
3117
3610
|
* The facet's name (and `this.name` getter) is handled entirely by
|
|
3118
3611
|
* partyserver via `ctx.id.name`, which is populated because the
|
|
3119
|
-
* parent passed an explicit
|
|
3612
|
+
* parent passed an explicit named Durable Object id to
|
|
3120
3613
|
* `ctx.facets.get()` — see {@link _cf_resolveSubAgent}. No
|
|
3121
3614
|
* `setName()` call or `__ps_name` storage write is needed; the
|
|
3122
|
-
* facet's name survives cold wake automatically because the
|
|
3123
|
-
*
|
|
3615
|
+
* facet's name survives cold wake automatically because the factory
|
|
3616
|
+
* re-runs and `idFromName` is deterministic.
|
|
3124
3617
|
*
|
|
3125
3618
|
* @internal Called by {@link subAgent}.
|
|
3126
3619
|
*/
|
|
@@ -3167,26 +3660,33 @@ var Agent = class Agent extends Server {
|
|
|
3167
3660
|
}];
|
|
3168
3661
|
}
|
|
3169
3662
|
/**
|
|
3170
|
-
* Resolve a typed
|
|
3663
|
+
* Resolve a typed parent stub for this facet's **immediate** parent
|
|
3171
3664
|
* agent.
|
|
3172
3665
|
*
|
|
3173
3666
|
* Symmetric with `subAgent(Cls, name)`: while `subAgent` opens a
|
|
3174
3667
|
* stub from parent to child, `parentAgent` opens one from child
|
|
3175
3668
|
* to parent. Pass the direct parent's class reference — the
|
|
3176
3669
|
* framework verifies it matches the last entry of
|
|
3177
|
-
* `this.parentPath` at runtime
|
|
3178
|
-
*
|
|
3670
|
+
* `this.parentPath` at runtime. If the parent is a top-level
|
|
3671
|
+
* Durable Object, the framework returns the normal namespace stub.
|
|
3672
|
+
* If the parent is itself a facet, the framework returns a bridge
|
|
3673
|
+
* proxy that routes method calls through the root/supervisor and
|
|
3674
|
+
* then down the recorded facet path.
|
|
3179
3675
|
*
|
|
3180
3676
|
* `this.parentPath` is root-first, so the direct parent is the
|
|
3181
3677
|
* **last** entry: `this.parentPath.at(-1)`. For grandparents and
|
|
3182
3678
|
* further ancestors, iterate `this.parentPath` and use
|
|
3183
3679
|
* `getAgentByName(env.X, this.parentPath[i].name)` directly.
|
|
3184
3680
|
*
|
|
3185
|
-
*
|
|
3186
|
-
*
|
|
3187
|
-
*
|
|
3188
|
-
*
|
|
3189
|
-
*
|
|
3681
|
+
* For top-level parents, the framework first checks `env[Cls.name]`,
|
|
3682
|
+
* then falls back to the Worker `exports` object. This supports
|
|
3683
|
+
* custom binding names as long as the parent class is exported under
|
|
3684
|
+
* its class name.
|
|
3685
|
+
*
|
|
3686
|
+
* Facet-parent stubs route normal HTTP `.fetch()` calls through the
|
|
3687
|
+
* same root bridge as RPC methods. WebSocket upgrade requests are
|
|
3688
|
+
* not supported yet because WebSocket handles cannot be serialized
|
|
3689
|
+
* over RPC.
|
|
3190
3690
|
*
|
|
3191
3691
|
* @experimental The API surface may change before stabilizing.
|
|
3192
3692
|
*
|
|
@@ -3194,7 +3694,8 @@ var Agent = class Agent extends Server {
|
|
|
3194
3694
|
* @throws If `Cls.name` doesn't match the recorded direct-parent
|
|
3195
3695
|
* class (guards against accidentally reaching the wrong
|
|
3196
3696
|
* DO, especially in nested Root → Mid → Leaf chains).
|
|
3197
|
-
* @throws If no
|
|
3697
|
+
* @throws If no namespace is found for a top-level parent, or no
|
|
3698
|
+
* root namespace is available for a facet parent bridge.
|
|
3198
3699
|
*
|
|
3199
3700
|
* @example
|
|
3200
3701
|
* ```ts
|
|
@@ -3211,10 +3712,46 @@ var Agent = class Agent extends Server {
|
|
|
3211
3712
|
const parent = this._parentPath[this._parentPath.length - 1];
|
|
3212
3713
|
if (!parent) throw new Error(`parentAgent(): ${this.constructor.name} is not a facet — only sub-agents (spawned via \`subAgent()\`) have a parent.`);
|
|
3213
3714
|
if (cls.name !== parent.className) throw new Error(`parentAgent(${cls.name}): this facet's recorded parent class is "${parent.className}", not "${cls.name}". Pass the class whose constructor actually spawned this facet.`);
|
|
3214
|
-
|
|
3215
|
-
|
|
3715
|
+
if (this._parentPath.length > 1) return await this._cf_parentAgentFacetProxy(cls.name, this._parentPath);
|
|
3716
|
+
const binding = this._cf_getTopLevelNamespaceByClassName(cls.name);
|
|
3717
|
+
if (!binding) throw new Error(`parentAgent(${cls.name}): no top-level namespace for "${cls.name}" was found in env or worker exports. Make sure the parent class is exported under that class name and registered as a Durable Object binding.`);
|
|
3216
3718
|
return await getServerByName(binding, parent.name);
|
|
3217
3719
|
}
|
|
3720
|
+
_cf_getTopLevelNamespaceByClassName(className) {
|
|
3721
|
+
return this._cf_asDurableObjectNamespace(this.env[className]) ?? this._cf_asDurableObjectNamespace(exports[className]);
|
|
3722
|
+
}
|
|
3723
|
+
_cf_asDurableObjectNamespace(candidate) {
|
|
3724
|
+
const binding = candidate;
|
|
3725
|
+
return binding?.idFromName ? binding : void 0;
|
|
3726
|
+
}
|
|
3727
|
+
async _cf_parentAgentFacetProxy(className, parentPath) {
|
|
3728
|
+
const [root] = parentPath;
|
|
3729
|
+
if (!root) throw new Error(`parentAgent(${className}): parent path is empty.`);
|
|
3730
|
+
const rootBinding = this._cf_getTopLevelNamespaceByClassName(root.className);
|
|
3731
|
+
if (!rootBinding) throw new Error(`parentAgent(${className}): direct parent is a facet, but no top-level root namespace "${root.className}" was found in env or worker exports to bridge the call.`);
|
|
3732
|
+
const rootStubPromise = getServerByName(rootBinding, root.name);
|
|
3733
|
+
const targetPath = parentPath.map((step) => ({ ...step }));
|
|
3734
|
+
const invokeBridge = async (method, args) => {
|
|
3735
|
+
return await (await rootStubPromise)._cf_invokeSubAgentPath(targetPath, method, args);
|
|
3736
|
+
};
|
|
3737
|
+
const owner = this;
|
|
3738
|
+
return new Proxy({}, { get(_target, prop) {
|
|
3739
|
+
if (isInternalJsStubProp(prop)) return void 0;
|
|
3740
|
+
if (typeof prop !== "string") return void 0;
|
|
3741
|
+
if (prop === "fetch") return async (input, init) => {
|
|
3742
|
+
if (owner._cf_isWebSocketUpgradeRequest(input, init)) throw new Error(`parentAgent(${className}).fetch() does not support WebSocket upgrade requests yet. Use externally routed sub-agent URLs for WebSocket connections.`);
|
|
3743
|
+
return await invokeBridge(prop, [input, init]);
|
|
3744
|
+
};
|
|
3745
|
+
return async (...args) => {
|
|
3746
|
+
return await invokeBridge(prop, args);
|
|
3747
|
+
};
|
|
3748
|
+
} });
|
|
3749
|
+
}
|
|
3750
|
+
_cf_isWebSocketUpgradeRequest(input, init) {
|
|
3751
|
+
const initHeaders = init?.headers ? new Headers(init.headers) : void 0;
|
|
3752
|
+
const requestHeaders = input instanceof Request ? new Headers(input.headers) : void 0;
|
|
3753
|
+
return initHeaders?.get("Upgrade")?.toLowerCase() === "websocket" || requestHeaders?.get("Upgrade")?.toLowerCase() === "websocket";
|
|
3754
|
+
}
|
|
3218
3755
|
/**
|
|
3219
3756
|
* Get or create a named sub-agent — a child Durable Object (facet)
|
|
3220
3757
|
* with its own isolated SQLite storage running on the same machine.
|
|
@@ -3796,13 +4333,13 @@ var Agent = class Agent extends Server {
|
|
|
3796
4333
|
if (!Cls) throw new Error(`Sub-agent class "${className}" not found in worker exports. Make sure the class is exported from your worker entry point and that the export name matches the class name.`);
|
|
3797
4334
|
if (name.includes("\0")) throw new Error(`Sub-agent name contains null character (\\0), which is reserved.`);
|
|
3798
4335
|
const facetKey = `${className}\0${name}`;
|
|
3799
|
-
const
|
|
3800
|
-
const
|
|
3801
|
-
if (!
|
|
3802
|
-
const minificationHint = /^_*[a-z][a-z0-9]{0,2}$/.test(
|
|
3803
|
-
throw new Error(`Sub-agent bootstrap requires the
|
|
4336
|
+
const rootClassName = this._parentPath[0]?.className ?? this.constructor.name;
|
|
4337
|
+
const rootNs = ctx.exports[rootClassName];
|
|
4338
|
+
if (!rootNs?.idFromName) {
|
|
4339
|
+
const minificationHint = /^_*[a-z][a-z0-9]{0,2}$/.test(rootClassName) ? ` The class name "${rootClassName}" looks minified — make sure your bundler preserves class names (e.g. esbuild's \`keepNames: true\`).` : "";
|
|
4340
|
+
throw new Error(`Sub-agent bootstrap requires the root agent class "${rootClassName}" to be available as a Durable Object namespace, but ctx.exports["${rootClassName}"] is missing or doesn't expose idFromName.${minificationHint} Make sure the root agent class is exported under that class name and registered in your wrangler.jsonc durable_objects.bindings.`);
|
|
3804
4341
|
}
|
|
3805
|
-
const facetId =
|
|
4342
|
+
const facetId = rootNs.idFromName(name);
|
|
3806
4343
|
const stub = ctx.facets.get(facetKey, () => ({
|
|
3807
4344
|
class: Cls,
|
|
3808
4345
|
id: facetId
|
|
@@ -3955,6 +4492,7 @@ var Agent = class Agent extends Server {
|
|
|
3955
4492
|
this.sql`DROP TABLE IF EXISTS cf_agents_workflows`;
|
|
3956
4493
|
this.sql`DROP TABLE IF EXISTS cf_agents_sub_agents`;
|
|
3957
4494
|
this.sql`DROP TABLE IF EXISTS cf_agents_runs`;
|
|
4495
|
+
this.sql`DROP TABLE IF EXISTS cf_agents_fibers`;
|
|
3958
4496
|
this.sql`DROP TABLE IF EXISTS cf_agents_facet_runs`;
|
|
3959
4497
|
this.sql`DROP TABLE IF EXISTS cf_agent_tool_runs`;
|
|
3960
4498
|
}
|