@noetaris/harness 0.11.0 → 0.13.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 +215 -10
- package/dist/index.d.ts +303 -11
- package/dist/index.js +1035 -300
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -564,6 +564,9 @@ function composeObservers(observers, onObserverError) {
|
|
|
564
564
|
onStepSettled: (ctx, event) => {
|
|
565
565
|
for (const o of observers) safeInvoke("onStepSettled", () => o.onStepSettled?.(ctx, event), onObserverError);
|
|
566
566
|
},
|
|
567
|
+
onCheckpoint: (ctx, event) => {
|
|
568
|
+
for (const o of observers) safeInvoke("onCheckpoint", () => o.onCheckpoint?.(ctx, event), onObserverError);
|
|
569
|
+
},
|
|
567
570
|
// Each observer isolated on its own; later keys win.
|
|
568
571
|
carry: (ctx) => {
|
|
569
572
|
let merged;
|
|
@@ -634,16 +637,14 @@ function extractRunListeners(resources) {
|
|
|
634
637
|
// src/agent/ctx-within.ts
|
|
635
638
|
function createWithinFn(observer, currentStep, onObserverError) {
|
|
636
639
|
return (type, payload, fn) => {
|
|
637
|
-
const wrap = observer?.wrap;
|
|
638
640
|
const stepCtx = currentStep();
|
|
639
|
-
if (wrap === void 0 || stepCtx === null) return fn();
|
|
640
|
-
return runWrapped((inner) => wrap(stepCtx, { kind: "event", type, payload }, inner), fn, onObserverError);
|
|
641
|
+
if (observer?.wrap === void 0 || stepCtx === null) return fn();
|
|
642
|
+
return runWrapped((inner) => observer.wrap?.(stepCtx, { kind: "event", type, payload }, inner), fn, onObserverError);
|
|
641
643
|
};
|
|
642
644
|
}
|
|
643
645
|
function runStepScope(observer, stepCtx, fn, onObserverError) {
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
return runWrapped((inner) => wrap(stepCtx, { kind: "step" }, inner), fn, onObserverError);
|
|
646
|
+
if (observer?.wrap === void 0) return fn();
|
|
647
|
+
return runWrapped((inner) => observer.wrap?.(stepCtx, { kind: "step" }, inner), fn, onObserverError);
|
|
647
648
|
}
|
|
648
649
|
|
|
649
650
|
// src/loop/loop-executor.ts
|
|
@@ -707,10 +708,9 @@ function applyUpdate(state, update, schema, touchedKeys) {
|
|
|
707
708
|
}
|
|
708
709
|
}
|
|
709
710
|
function fireSettled(obs, stepCtx, stepStart, fields, onObserverError) {
|
|
710
|
-
|
|
711
|
-
if (hook === void 0) return;
|
|
711
|
+
if (obs?.onStepSettled === void 0) return;
|
|
712
712
|
const event = { ...fields, durationMs: Date.now() - stepStart };
|
|
713
|
-
safeInvoke("onStepSettled", () =>
|
|
713
|
+
safeInvoke("onStepSettled", () => obs.onStepSettled?.(stepCtx, event), onObserverError);
|
|
714
714
|
}
|
|
715
715
|
function stepContextOf(runCtx, stepName) {
|
|
716
716
|
return {
|
|
@@ -769,12 +769,39 @@ async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callb
|
|
|
769
769
|
ctx["within"] = createWithinFn(obs, () => stepCtxRef.current, onObserverError);
|
|
770
770
|
const telemetryObserver = obs !== void 0 ? composeObservers([obs], onObserverError) : NOOP_OBSERVER;
|
|
771
771
|
let cursor = startCursor ?? graph.entryStep;
|
|
772
|
+
let first = true;
|
|
772
773
|
try {
|
|
773
|
-
|
|
774
|
+
for (; ; first = false) {
|
|
774
775
|
if (shouldStop?.()) {
|
|
775
776
|
endRun("$stopped");
|
|
776
777
|
return withCarrier({ state, signal: null, cursor, paused: true });
|
|
777
778
|
}
|
|
779
|
+
if (callbacks?.debugGate !== void 0) {
|
|
780
|
+
const next = isForkCursor(cursor) ? cursor.fork : cursor;
|
|
781
|
+
const fork = isForkCursor(cursor) || graph.steps.some((s) => s.name === next && isForkDef(s));
|
|
782
|
+
const { patches, halt } = await callbacks.debugGate(next, state, {
|
|
783
|
+
branchPath: branchPath ?? [],
|
|
784
|
+
fork,
|
|
785
|
+
...callbacks.touchedKeys !== void 0 ? { touchedKeys: callbacks.touchedKeys } : {},
|
|
786
|
+
...ctx.signal !== void 0 ? { signal: ctx.signal } : {}
|
|
787
|
+
});
|
|
788
|
+
const nextCtx = stepContextOf(runCtx, next);
|
|
789
|
+
for (const patch of patches) {
|
|
790
|
+
safeInvoke("onEvent", () => obs?.onEvent?.(nextCtx, "debug.setState", { patch }), onObserverError);
|
|
791
|
+
}
|
|
792
|
+
if (halt || shouldStop?.()) {
|
|
793
|
+
endRun("$stopped");
|
|
794
|
+
return withCarrier({ state, signal: null, cursor, paused: true });
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
if (!first && callbacks?.checkpoint !== void 0 && typeof cursor === "string") {
|
|
798
|
+
const started = Date.now();
|
|
799
|
+
const saved = await callbacks.checkpoint(cursor, state, carrier);
|
|
800
|
+
if (saved !== void 0) {
|
|
801
|
+
const event = { step: cursor, version: saved.version, durationMs: Date.now() - started };
|
|
802
|
+
safeInvoke("onCheckpoint", () => obs?.onCheckpoint?.(runCtx, event), onObserverError);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
778
805
|
if (isForkCursor(cursor)) {
|
|
779
806
|
const forkCursor = cursor;
|
|
780
807
|
const forkNode = graph.steps.find((s) => s.name === forkCursor.fork);
|
|
@@ -961,7 +988,7 @@ async function dispatchFork(forkNode, priorBranches, outerState, outerCtx, schem
|
|
|
961
988
|
const branchSignals = forkNode.branches.map(
|
|
962
989
|
(_, i) => outerSignal !== void 0 ? AbortSignal.any([outerSignal, localAborts[i].signal]) : localAborts[i].signal
|
|
963
990
|
);
|
|
964
|
-
const { onBeforeStep: _outerOnBeforeStep, onAfterStep: _outerOnAfterStep, onComplete: _outerOnComplete, ...branchCallbacksRest } = callbacks ?? {};
|
|
991
|
+
const { onBeforeStep: _outerOnBeforeStep, onAfterStep: _outerOnAfterStep, onComplete: _outerOnComplete, checkpoint: _outerCheckpoint, ...branchCallbacksRest } = callbacks ?? {};
|
|
965
992
|
const branchCallbacksBase = callbacks !== void 0 ? branchCallbacksRest : void 0;
|
|
966
993
|
const settled = await Promise.allSettled(
|
|
967
994
|
forkNode.branches.map(async (branch, i) => {
|
|
@@ -1028,6 +1055,7 @@ async function dispatchFork(forkNode, priorBranches, outerState, outerCtx, schem
|
|
|
1028
1055
|
} else {
|
|
1029
1056
|
outerState[key] = outcome.result.state[key];
|
|
1030
1057
|
}
|
|
1058
|
+
callbacks?.touchedKeys?.add(key);
|
|
1031
1059
|
}
|
|
1032
1060
|
}
|
|
1033
1061
|
return {
|
|
@@ -1093,6 +1121,13 @@ function defaultClaimTtlMs() {
|
|
|
1093
1121
|
const ttlMs = Number(process.env["HARNESS_CLAIM_TTL_MS"]);
|
|
1094
1122
|
return isValidTtlMs(ttlMs) ? ttlMs : 3e4;
|
|
1095
1123
|
}
|
|
1124
|
+
function isCheckpointMode(value) {
|
|
1125
|
+
return value === "off" || value === "step";
|
|
1126
|
+
}
|
|
1127
|
+
async function saveRun(store, agentId, sessionId, run, lease) {
|
|
1128
|
+
if (lease !== null && lease !== void 0) await store.save(agentId, sessionId, run, { lease });
|
|
1129
|
+
else await store.save(agentId, sessionId, run);
|
|
1130
|
+
}
|
|
1096
1131
|
function collectPendingInterrupts(cursor, state) {
|
|
1097
1132
|
if (cursor !== void 0 && isForkCursor(cursor)) {
|
|
1098
1133
|
const result = [];
|
|
@@ -1114,14 +1149,23 @@ function storedSessionToPhase(loaded) {
|
|
|
1114
1149
|
if (loaded === null) {
|
|
1115
1150
|
return { phase: "fresh" };
|
|
1116
1151
|
}
|
|
1152
|
+
const step = loaded.step === void 0 ? "" : isForkCursor(loaded.step) ? loaded.step.fork : loaded.step;
|
|
1117
1153
|
if (loaded.phase === "paused") {
|
|
1118
|
-
const step = loaded.step === void 0 ? "" : isForkCursor(loaded.step) ? loaded.step.fork : loaded.step;
|
|
1119
1154
|
const pendingInterrupts = collectPendingInterrupts(loaded.step, loaded.finalState);
|
|
1120
1155
|
if (loaded.signal !== void 0) {
|
|
1121
1156
|
return { phase: "paused", step, signal: loaded.signal, pendingInterrupts };
|
|
1122
1157
|
}
|
|
1123
1158
|
return { phase: "paused", step, pendingInterrupts };
|
|
1124
1159
|
}
|
|
1160
|
+
if (loaded.phase === "running") {
|
|
1161
|
+
const instanceId = loaded.metadata?.instanceId;
|
|
1162
|
+
return {
|
|
1163
|
+
phase: "running",
|
|
1164
|
+
step,
|
|
1165
|
+
...instanceId !== void 0 ? { instanceId } : {},
|
|
1166
|
+
checkpointedAt: loaded.settledAt
|
|
1167
|
+
};
|
|
1168
|
+
}
|
|
1125
1169
|
if (loaded.signal !== void 0) {
|
|
1126
1170
|
return { phase: "completed", signal: loaded.signal };
|
|
1127
1171
|
}
|
|
@@ -1183,6 +1227,15 @@ var SessionInFlightError = class extends Error {
|
|
|
1183
1227
|
this.sessionId = sessionId;
|
|
1184
1228
|
}
|
|
1185
1229
|
};
|
|
1230
|
+
var SessionInDebugError = class extends Error {
|
|
1231
|
+
/** The session being debugged. */
|
|
1232
|
+
sessionId;
|
|
1233
|
+
constructor(sessionId) {
|
|
1234
|
+
super(`session "${sessionId}" is being debugged`);
|
|
1235
|
+
this.name = "SessionInDebugError";
|
|
1236
|
+
this.sessionId = sessionId;
|
|
1237
|
+
}
|
|
1238
|
+
};
|
|
1186
1239
|
var SessionPendingInterruptError = class extends Error {
|
|
1187
1240
|
/** The session that is awaiting an interrupt response. */
|
|
1188
1241
|
sessionId;
|
|
@@ -1213,12 +1266,19 @@ var SessionBusyError = class extends Error {
|
|
|
1213
1266
|
var LeaseExpiredError = class extends Error {
|
|
1214
1267
|
/** The session whose lease expired. */
|
|
1215
1268
|
sessionId;
|
|
1216
|
-
constructor(sessionId) {
|
|
1269
|
+
constructor(sessionId, cause) {
|
|
1217
1270
|
super(`lease for session "${sessionId}" has expired \u2014 run stopped without saving`);
|
|
1218
1271
|
this.name = "LeaseExpiredError";
|
|
1219
1272
|
this.sessionId = sessionId;
|
|
1273
|
+
if (cause !== void 0) this.cause = cause;
|
|
1220
1274
|
}
|
|
1221
1275
|
};
|
|
1276
|
+
function isLeaseLost(error) {
|
|
1277
|
+
return error instanceof Error && error.name === "LeaseLostError";
|
|
1278
|
+
}
|
|
1279
|
+
function isConflict(error) {
|
|
1280
|
+
return error instanceof Error && error.name === "ConcurrentModificationError";
|
|
1281
|
+
}
|
|
1222
1282
|
|
|
1223
1283
|
// src/agent/ctx-keep-alive.ts
|
|
1224
1284
|
function createKeepAliveFn(leaseRef, store, originalTtlMs) {
|
|
@@ -1328,7 +1388,7 @@ async function runWithSession(store, agentId, sessionId, runId, graph, initialSt
|
|
|
1328
1388
|
}
|
|
1329
1389
|
return false;
|
|
1330
1390
|
};
|
|
1331
|
-
const { mode = "run", requestId: callerRequestId, ...loopOptions } = options ?? {};
|
|
1391
|
+
const { mode = "run", requestId: callerRequestId, checkpoint: checkpointMode = "off", ...loopOptions } = options ?? {};
|
|
1332
1392
|
if (store === void 0) {
|
|
1333
1393
|
const state2 = initializeState(null, initialStateArg, schema);
|
|
1334
1394
|
const result2 = await runLoop(graph, state2, ctx, schema, composedShouldStop, void 0, {
|
|
@@ -1369,7 +1429,7 @@ async function runWithSession(store, agentId, sessionId, runId, graph, initialSt
|
|
|
1369
1429
|
state: row.finalState,
|
|
1370
1430
|
signal: row.signal ?? null,
|
|
1371
1431
|
cursor: row.step ?? null,
|
|
1372
|
-
paused: row.phase
|
|
1432
|
+
paused: row.phase !== "completed"
|
|
1373
1433
|
};
|
|
1374
1434
|
}
|
|
1375
1435
|
const segmentRequestId = mode === "run" ? requestId : loaded?.requestId;
|
|
@@ -1378,8 +1438,47 @@ async function runWithSession(store, agentId, sessionId, runId, graph, initialSt
|
|
|
1378
1438
|
const state = initializeState(base, input, schema);
|
|
1379
1439
|
const initialStateSnapshot = { ...state };
|
|
1380
1440
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1441
|
+
const metadata = options?.instanceId !== void 0 ? { instanceId: options.instanceId } : void 0;
|
|
1442
|
+
let nextVersion = (loaded?.version ?? -1) + 1;
|
|
1443
|
+
const baseRow = (carrier) => ({
|
|
1444
|
+
agentId,
|
|
1445
|
+
runId,
|
|
1446
|
+
sessionId,
|
|
1447
|
+
version: nextVersion,
|
|
1448
|
+
startedAt,
|
|
1449
|
+
settledAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1450
|
+
initialState: initialStateSnapshot,
|
|
1451
|
+
...metadata !== void 0 ? { metadata } : {},
|
|
1452
|
+
...carrier !== void 0 ? { carrier } : {},
|
|
1453
|
+
...segmentRequestId !== void 0 ? { requestId: segmentRequestId } : {}
|
|
1454
|
+
});
|
|
1455
|
+
const checkpoint = async (next, live, carrier) => {
|
|
1456
|
+
let finalState;
|
|
1457
|
+
try {
|
|
1458
|
+
finalState = structuredClone(live);
|
|
1459
|
+
} catch (error) {
|
|
1460
|
+
options?.onStoreError?.(error, "checkpoint");
|
|
1461
|
+
return void 0;
|
|
1462
|
+
}
|
|
1463
|
+
const row = { ...baseRow(carrier), phase: "running", step: next, finalState };
|
|
1464
|
+
try {
|
|
1465
|
+
await saveRun(store, agentId, sessionId, row, leaseRef.current);
|
|
1466
|
+
} catch (error) {
|
|
1467
|
+
if (isLeaseLost(error) || isConflict(error)) {
|
|
1468
|
+
const expiredError = new LeaseExpiredError(sessionId, error);
|
|
1469
|
+
options?.onStoreError?.(expiredError, "claim");
|
|
1470
|
+
throw expiredError;
|
|
1471
|
+
}
|
|
1472
|
+
options?.onStoreError?.(error, "checkpoint");
|
|
1473
|
+
return void 0;
|
|
1474
|
+
}
|
|
1475
|
+
nextVersion++;
|
|
1476
|
+
await extendLease(store, leaseRef, options?.claimTtlMs ?? defaultClaimTtlMs());
|
|
1477
|
+
return { version: row.version };
|
|
1478
|
+
};
|
|
1381
1479
|
const result = await runLoop(graph, state, ctx, schema, composedShouldStop, loaded?.step, {
|
|
1382
1480
|
...loopOptions,
|
|
1481
|
+
...checkpointMode === "step" ? { checkpoint } : {},
|
|
1383
1482
|
runId,
|
|
1384
1483
|
...options?.parentRunId !== void 0 ? { parentRunId: options.parentRunId } : {},
|
|
1385
1484
|
...segmentRequestId !== void 0 ? { requestId: segmentRequestId } : {},
|
|
@@ -1390,53 +1489,39 @@ async function runWithSession(store, agentId, sessionId, runId, graph, initialSt
|
|
|
1390
1489
|
options?.onStoreError?.(expiredError, "claim");
|
|
1391
1490
|
throw expiredError;
|
|
1392
1491
|
}
|
|
1393
|
-
const
|
|
1394
|
-
|
|
1492
|
+
const saved = {
|
|
1493
|
+
...baseRow(result.carrier),
|
|
1494
|
+
phase: result.paused ? "paused" : "completed",
|
|
1495
|
+
finalState: result.state,
|
|
1496
|
+
...result.paused ? { step: result.cursor } : {},
|
|
1497
|
+
...result.signal !== null ? { signal: result.signal } : {}
|
|
1498
|
+
};
|
|
1395
1499
|
try {
|
|
1396
|
-
|
|
1397
|
-
if (result.paused) {
|
|
1398
|
-
const saved = {
|
|
1399
|
-
agentId,
|
|
1400
|
-
runId,
|
|
1401
|
-
sessionId,
|
|
1402
|
-
version,
|
|
1403
|
-
startedAt,
|
|
1404
|
-
settledAt,
|
|
1405
|
-
phase: "paused",
|
|
1406
|
-
initialState: initialStateSnapshot,
|
|
1407
|
-
finalState: result.state,
|
|
1408
|
-
step: result.cursor,
|
|
1409
|
-
...result.signal !== null ? { signal: result.signal } : {},
|
|
1410
|
-
...metadata !== void 0 ? { metadata } : {},
|
|
1411
|
-
...result.carrier !== void 0 ? { carrier: result.carrier } : {},
|
|
1412
|
-
...segmentRequestId !== void 0 ? { requestId: segmentRequestId } : {}
|
|
1413
|
-
};
|
|
1414
|
-
await store.save(agentId, sessionId, saved);
|
|
1415
|
-
} else {
|
|
1416
|
-
const saved = {
|
|
1417
|
-
agentId,
|
|
1418
|
-
runId,
|
|
1419
|
-
sessionId,
|
|
1420
|
-
version,
|
|
1421
|
-
startedAt,
|
|
1422
|
-
settledAt,
|
|
1423
|
-
phase: "completed",
|
|
1424
|
-
initialState: initialStateSnapshot,
|
|
1425
|
-
finalState: result.state,
|
|
1426
|
-
...result.signal !== null ? { signal: result.signal } : {},
|
|
1427
|
-
...metadata !== void 0 ? { metadata } : {},
|
|
1428
|
-
...result.carrier !== void 0 ? { carrier: result.carrier } : {},
|
|
1429
|
-
...segmentRequestId !== void 0 ? { requestId: segmentRequestId } : {}
|
|
1430
|
-
};
|
|
1431
|
-
await store.save(agentId, sessionId, saved);
|
|
1432
|
-
}
|
|
1500
|
+
await saveRun(store, agentId, sessionId, saved, leaseRef.current);
|
|
1433
1501
|
} catch (error) {
|
|
1502
|
+
if (isLeaseLost(error)) {
|
|
1503
|
+
const expiredError = new LeaseExpiredError(sessionId, error);
|
|
1504
|
+
options?.onStoreError?.(expiredError, "claim");
|
|
1505
|
+
throw expiredError;
|
|
1506
|
+
}
|
|
1434
1507
|
options?.onStoreError?.(error, "persist");
|
|
1435
1508
|
}
|
|
1436
1509
|
return result;
|
|
1437
1510
|
}
|
|
1511
|
+
async function extendLease(store, leaseRef, ttlMs) {
|
|
1512
|
+
const held = leaseRef.current;
|
|
1513
|
+
if (held === null || typeof store.extendClaim !== "function") return;
|
|
1514
|
+
try {
|
|
1515
|
+
leaseRef.current = await store.extendClaim(held, { ttlMs });
|
|
1516
|
+
} catch {
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1438
1519
|
|
|
1439
1520
|
// src/agent/claim-scope.ts
|
|
1521
|
+
function heldLease(options) {
|
|
1522
|
+
const lease = options.leaseRef?.current ?? null;
|
|
1523
|
+
return lease !== null ? { lease } : {};
|
|
1524
|
+
}
|
|
1440
1525
|
function failOutcome(failState, error) {
|
|
1441
1526
|
const state = failState();
|
|
1442
1527
|
state["$error"] = error;
|
|
@@ -1530,7 +1615,7 @@ function injectInterruptResponseIntoState(cursor, state, interruptId, response)
|
|
|
1530
1615
|
found: true
|
|
1531
1616
|
};
|
|
1532
1617
|
}
|
|
1533
|
-
async function injectInterruptResponse(store, agentId, sessionId, interruptId, response) {
|
|
1618
|
+
async function injectInterruptResponse(store, agentId, sessionId, interruptId, response, lease) {
|
|
1534
1619
|
const loaded = await store.load(agentId, sessionId);
|
|
1535
1620
|
if (loaded === null) throw new NoInterruptError();
|
|
1536
1621
|
if (loaded.phase !== "paused") throw new NoInterruptError();
|
|
@@ -1553,14 +1638,19 @@ async function injectInterruptResponse(store, agentId, sessionId, interruptId, r
|
|
|
1553
1638
|
...loaded.branchedFrom !== void 0 ? { branchedFrom: loaded.branchedFrom } : {},
|
|
1554
1639
|
...loaded.requestId !== void 0 ? { requestId: loaded.requestId } : {}
|
|
1555
1640
|
};
|
|
1556
|
-
await store
|
|
1641
|
+
await saveRun(store, agentId, sessionId, updated, lease);
|
|
1557
1642
|
}
|
|
1558
1643
|
async function injectThenRun(args) {
|
|
1559
|
-
const { store, agentId, sessionId, interruptId, response, onStoreError, failState, run } = args;
|
|
1644
|
+
const { store, agentId, sessionId, interruptId, response, lease, onStoreError, failState, run } = args;
|
|
1560
1645
|
try {
|
|
1561
|
-
await injectInterruptResponse(store, agentId, sessionId, interruptId, response);
|
|
1646
|
+
await injectInterruptResponse(store, agentId, sessionId, interruptId, response, lease);
|
|
1562
1647
|
} catch (injectError) {
|
|
1563
1648
|
if (injectError instanceof NoInterruptError) throw injectError;
|
|
1649
|
+
if (isLeaseLost(injectError)) {
|
|
1650
|
+
const expiredError = new LeaseExpiredError(sessionId, injectError);
|
|
1651
|
+
onStoreError?.(expiredError, "claim");
|
|
1652
|
+
return failOutcome(failState, expiredError);
|
|
1653
|
+
}
|
|
1564
1654
|
const storeError = new StoreLoadError(injectError);
|
|
1565
1655
|
onStoreError?.(storeError, "claim");
|
|
1566
1656
|
return failOutcome(failState, storeError);
|
|
@@ -1617,6 +1707,572 @@ function createRunHandle(sessionId, runId, execution, stopFlag, stepRef, resumeF
|
|
|
1617
1707
|
return handle;
|
|
1618
1708
|
}
|
|
1619
1709
|
|
|
1710
|
+
// src/agent/debug-session.ts
|
|
1711
|
+
var DebugCommandError = class extends Error {
|
|
1712
|
+
/**
|
|
1713
|
+
* - `busy`: another command is still running
|
|
1714
|
+
* - `not-held`: the focused frame is not held at a step stop (or, for `answer`, the session is not at
|
|
1715
|
+
* an interrupt stop; for `focus`, the frame is not held)
|
|
1716
|
+
* - `not-a-fork`: `stepInto` where the next step is not a fork node
|
|
1717
|
+
* - `reserved-key`: `setState` was given a framework key (`$…`)
|
|
1718
|
+
* - `ended`: the session is over
|
|
1719
|
+
*/
|
|
1720
|
+
reason;
|
|
1721
|
+
/** The command that was refused. */
|
|
1722
|
+
command;
|
|
1723
|
+
constructor(reason, command, detail) {
|
|
1724
|
+
super(`${command}(): ${detail ?? describeReason(reason)}`);
|
|
1725
|
+
this.name = "DebugCommandError";
|
|
1726
|
+
this.reason = reason;
|
|
1727
|
+
this.command = command;
|
|
1728
|
+
}
|
|
1729
|
+
};
|
|
1730
|
+
function describeReason(reason) {
|
|
1731
|
+
switch (reason) {
|
|
1732
|
+
case "busy":
|
|
1733
|
+
return "another debug command is still running";
|
|
1734
|
+
case "not-held":
|
|
1735
|
+
return "the debug session is not stopped where this command applies";
|
|
1736
|
+
case "not-a-fork":
|
|
1737
|
+
return "the next step is not a fork";
|
|
1738
|
+
case "reserved-key":
|
|
1739
|
+
return "framework keys ($\u2026) cannot be set";
|
|
1740
|
+
case "ended":
|
|
1741
|
+
return "the debug session has ended";
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
function cloneOrShare(value) {
|
|
1745
|
+
try {
|
|
1746
|
+
return { value: structuredClone(value), shared: false };
|
|
1747
|
+
} catch {
|
|
1748
|
+
return { value, shared: true };
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
function snapshotOf(source) {
|
|
1752
|
+
const values = {};
|
|
1753
|
+
const shared = [];
|
|
1754
|
+
for (const [key, raw] of Object.entries(source)) {
|
|
1755
|
+
const copy = cloneOrShare(raw);
|
|
1756
|
+
values[key] = copy.value;
|
|
1757
|
+
if (copy.shared) shared.push(key);
|
|
1758
|
+
}
|
|
1759
|
+
return { values, shared };
|
|
1760
|
+
}
|
|
1761
|
+
function deferred() {
|
|
1762
|
+
let resolve;
|
|
1763
|
+
let reject;
|
|
1764
|
+
const promise = new Promise((res, rej) => {
|
|
1765
|
+
resolve = res;
|
|
1766
|
+
reject = rej;
|
|
1767
|
+
});
|
|
1768
|
+
promise.catch(() => {
|
|
1769
|
+
});
|
|
1770
|
+
return { promise, resolve, reject };
|
|
1771
|
+
}
|
|
1772
|
+
var keyOf = (path) => JSON.stringify(path);
|
|
1773
|
+
var samePath = (a, b) => a.length === b.length && a.every((s, i) => s === b[i]);
|
|
1774
|
+
var within = (path, root) => path.length >= root.length && root.every((s, i) => s === path[i]);
|
|
1775
|
+
function branchStart(parentForkMode) {
|
|
1776
|
+
return {
|
|
1777
|
+
mode: parentForkMode === "fork-entry" ? "fork-entry" : "continue",
|
|
1778
|
+
lastRelease: parentForkMode === "continue" || parentForkMode === void 0 ? "continue" : "hold"
|
|
1779
|
+
};
|
|
1780
|
+
}
|
|
1781
|
+
function createDebugSession(args) {
|
|
1782
|
+
const { store, signal, onEnd } = args;
|
|
1783
|
+
let status = "starting";
|
|
1784
|
+
let handle;
|
|
1785
|
+
let topMode = "start";
|
|
1786
|
+
let frames = /* @__PURE__ */ new Map();
|
|
1787
|
+
let seq = 0;
|
|
1788
|
+
let focus = [];
|
|
1789
|
+
let lastByFrame = /* @__PURE__ */ new Map();
|
|
1790
|
+
let topInterrupt;
|
|
1791
|
+
let interrupts = [];
|
|
1792
|
+
let pausedForks = /* @__PURE__ */ new Map();
|
|
1793
|
+
let paused;
|
|
1794
|
+
let stopRequested = false;
|
|
1795
|
+
let lease = {};
|
|
1796
|
+
let renewal;
|
|
1797
|
+
const breakpoints = /* @__PURE__ */ new Map();
|
|
1798
|
+
let command;
|
|
1799
|
+
let idle;
|
|
1800
|
+
const first = deferred();
|
|
1801
|
+
let current = first.promise;
|
|
1802
|
+
let started = false;
|
|
1803
|
+
const ended = deferred();
|
|
1804
|
+
const done = deferred();
|
|
1805
|
+
const anyHeld = () => [...frames.values()].some((f) => f.status === "held");
|
|
1806
|
+
const updateRenewal = () => {
|
|
1807
|
+
if (!anyHeld()) {
|
|
1808
|
+
if (renewal !== void 0) clearInterval(renewal);
|
|
1809
|
+
renewal = void 0;
|
|
1810
|
+
return;
|
|
1811
|
+
}
|
|
1812
|
+
if (renewal !== void 0) return;
|
|
1813
|
+
const { leaseRef, claimTtlMs } = lease;
|
|
1814
|
+
const extendClaim = store?.extendClaim;
|
|
1815
|
+
if (leaseRef === void 0 || claimTtlMs === void 0 || typeof extendClaim !== "function") return;
|
|
1816
|
+
const renew = () => {
|
|
1817
|
+
const held = leaseRef.current;
|
|
1818
|
+
if (held === null) return;
|
|
1819
|
+
extendClaim.call(store, held, { ttlMs: claimTtlMs }).then((renewed) => {
|
|
1820
|
+
leaseRef.current = renewed;
|
|
1821
|
+
}, () => {
|
|
1822
|
+
});
|
|
1823
|
+
};
|
|
1824
|
+
renew();
|
|
1825
|
+
renewal = setInterval(renew, claimTtlMs / 3);
|
|
1826
|
+
renewal.unref?.();
|
|
1827
|
+
};
|
|
1828
|
+
const frameOf = (path) => frames.get(keyOf(path));
|
|
1829
|
+
const addFrame = (path, init) => {
|
|
1830
|
+
const frame = { path, seq: seq++, patches: [], ...init };
|
|
1831
|
+
frames.set(keyOf(path), frame);
|
|
1832
|
+
return frame;
|
|
1833
|
+
};
|
|
1834
|
+
const treeOrder = (a, b) => {
|
|
1835
|
+
const n = Math.min(a.path.length, b.path.length);
|
|
1836
|
+
for (let i = 0; i < n; i++) {
|
|
1837
|
+
if (a.path[i] !== b.path[i]) {
|
|
1838
|
+
const sa = frameOf(a.path.slice(0, i + 1))?.seq ?? 0;
|
|
1839
|
+
const sb = frameOf(b.path.slice(0, i + 1))?.seq ?? 0;
|
|
1840
|
+
return sa - sb;
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
return a.path.length - b.path.length;
|
|
1844
|
+
};
|
|
1845
|
+
const sortedFrames = () => [...frames.values()].sort(treeOrder);
|
|
1846
|
+
const dropDescendants = (root) => {
|
|
1847
|
+
for (const [key, frame] of frames) {
|
|
1848
|
+
if (frame.path.length > root.length && within(frame.path, root)) frames.delete(key);
|
|
1849
|
+
}
|
|
1850
|
+
};
|
|
1851
|
+
const seedPausedFork = (frame, forkName) => {
|
|
1852
|
+
const settled = pausedForks.get(keyOf(frame.path));
|
|
1853
|
+
if (settled?.fork === void 0 || settled.name !== forkName) return;
|
|
1854
|
+
pausedForks.delete(keyOf(frame.path));
|
|
1855
|
+
for (const branch of settled.fork.branches) {
|
|
1856
|
+
addFrame([...frame.path, branch.name], { status: branch.status === "done" ? "ended" : "running", ...branchStart(frame.forkMode) });
|
|
1857
|
+
}
|
|
1858
|
+
};
|
|
1859
|
+
const setCurrent = (stop) => {
|
|
1860
|
+
current = Promise.resolve(stop);
|
|
1861
|
+
if (!started) {
|
|
1862
|
+
started = true;
|
|
1863
|
+
first.resolve(stop);
|
|
1864
|
+
}
|
|
1865
|
+
};
|
|
1866
|
+
const deliver = (stop, frame) => {
|
|
1867
|
+
focus = frame;
|
|
1868
|
+
setCurrent(stop);
|
|
1869
|
+
const c = command;
|
|
1870
|
+
command = void 0;
|
|
1871
|
+
c?.result.resolve(stop);
|
|
1872
|
+
const w = idle;
|
|
1873
|
+
idle = void 0;
|
|
1874
|
+
w?.resolve(stop);
|
|
1875
|
+
};
|
|
1876
|
+
const offerStop = (stop) => {
|
|
1877
|
+
if (!started) return deliver(stop, stop.frame);
|
|
1878
|
+
if (command !== void 0) {
|
|
1879
|
+
const { target, orphan } = command;
|
|
1880
|
+
if (target === "any" || orphan !== void 0 || within(stop.frame, target)) deliver(stop, stop.frame);
|
|
1881
|
+
return;
|
|
1882
|
+
}
|
|
1883
|
+
if (idle !== void 0) deliver(stop, stop.frame);
|
|
1884
|
+
};
|
|
1885
|
+
const branchEndStop = (frame, endedFrame) => {
|
|
1886
|
+
const held = frame.stop;
|
|
1887
|
+
const stop = {
|
|
1888
|
+
kind: "step",
|
|
1889
|
+
frame: frame.path,
|
|
1890
|
+
reason: "branch-end",
|
|
1891
|
+
ended: endedFrame,
|
|
1892
|
+
runId: held.runId,
|
|
1893
|
+
next: held.next,
|
|
1894
|
+
...held.last !== void 0 ? { last: held.last } : {},
|
|
1895
|
+
state: held.state
|
|
1896
|
+
};
|
|
1897
|
+
frame.stop = stop;
|
|
1898
|
+
return stop;
|
|
1899
|
+
};
|
|
1900
|
+
const afterFrameEnd = (endedFrame) => {
|
|
1901
|
+
setTimeout(() => {
|
|
1902
|
+
if (status !== "active") return;
|
|
1903
|
+
const heldFrame = sortedFrames().find((f) => f.status === "held");
|
|
1904
|
+
if (command !== void 0) {
|
|
1905
|
+
if (command.orphan !== endedFrame) return;
|
|
1906
|
+
if (heldFrame !== void 0) deliver(branchEndStop(heldFrame, endedFrame), heldFrame.path);
|
|
1907
|
+
return;
|
|
1908
|
+
}
|
|
1909
|
+
if (!samePath(focus, endedFrame.frame)) return;
|
|
1910
|
+
if (heldFrame !== void 0) {
|
|
1911
|
+
focus = heldFrame.path;
|
|
1912
|
+
setCurrent(branchEndStop(heldFrame, endedFrame));
|
|
1913
|
+
} else {
|
|
1914
|
+
idle ??= deferred();
|
|
1915
|
+
current = idle.promise;
|
|
1916
|
+
}
|
|
1917
|
+
}, 0);
|
|
1918
|
+
};
|
|
1919
|
+
const onFrameEnd = (path, signal2) => {
|
|
1920
|
+
const frame = frameOf(path);
|
|
1921
|
+
if (frame === void 0) return;
|
|
1922
|
+
frame.status = "ended";
|
|
1923
|
+
const parent = frameOf(path.slice(0, -1));
|
|
1924
|
+
if (parent !== void 0) parent.joinHold = frame.lastRelease === "hold";
|
|
1925
|
+
const outcome = signal2 === "$interrupt" ? "interrupt" : signal2 === "$error" ? "error" : signal2 === "$stopped" ? "stopped" : "done";
|
|
1926
|
+
const last = lastByFrame.get(keyOf(path));
|
|
1927
|
+
const endedFrame = { frame: path, outcome, ...last !== void 0 ? { last } : {} };
|
|
1928
|
+
updateRenewal();
|
|
1929
|
+
if (command !== void 0) {
|
|
1930
|
+
if (command.target !== "any" && samePath(command.target, path)) {
|
|
1931
|
+
command.orphan = endedFrame;
|
|
1932
|
+
afterFrameEnd(endedFrame);
|
|
1933
|
+
}
|
|
1934
|
+
return;
|
|
1935
|
+
}
|
|
1936
|
+
if (samePath(focus, path)) afterFrameEnd(endedFrame);
|
|
1937
|
+
};
|
|
1938
|
+
const onAbort = () => {
|
|
1939
|
+
void session.stop();
|
|
1940
|
+
};
|
|
1941
|
+
const cleanup = () => {
|
|
1942
|
+
for (const frame of frames.values()) if (frame.status === "held") frame.status = "ended";
|
|
1943
|
+
updateRenewal();
|
|
1944
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1945
|
+
onEnd();
|
|
1946
|
+
};
|
|
1947
|
+
const topLast = () => lastByFrame.get(keyOf([]));
|
|
1948
|
+
const finish = (outcome) => {
|
|
1949
|
+
const last = topLast();
|
|
1950
|
+
const stop = { kind: "end", runId: handle.runId, ...last !== void 0 ? { last } : {}, outcome };
|
|
1951
|
+
status = "ended";
|
|
1952
|
+
cleanup();
|
|
1953
|
+
done.resolve(outcome);
|
|
1954
|
+
deliver(stop, []);
|
|
1955
|
+
ended.resolve(stop);
|
|
1956
|
+
};
|
|
1957
|
+
const fail = (error) => {
|
|
1958
|
+
status = "ended";
|
|
1959
|
+
cleanup();
|
|
1960
|
+
done.reject(error);
|
|
1961
|
+
const c = command;
|
|
1962
|
+
command = void 0;
|
|
1963
|
+
c?.result.reject(error);
|
|
1964
|
+
const w = idle;
|
|
1965
|
+
idle = void 0;
|
|
1966
|
+
w?.reject(error);
|
|
1967
|
+
if (!started) first.reject(error);
|
|
1968
|
+
else current = done.promise.then(() => {
|
|
1969
|
+
throw error;
|
|
1970
|
+
});
|
|
1971
|
+
current.catch(() => {
|
|
1972
|
+
});
|
|
1973
|
+
ended.reject(error);
|
|
1974
|
+
};
|
|
1975
|
+
const pendingInterrupts = () => {
|
|
1976
|
+
const pending = [];
|
|
1977
|
+
const walk = (path) => {
|
|
1978
|
+
const settled = lastByFrame.get(keyOf(path));
|
|
1979
|
+
if (settled?.fork === void 0) return;
|
|
1980
|
+
for (const branch of settled.fork.branches) {
|
|
1981
|
+
if (branch.status !== "paused") continue;
|
|
1982
|
+
const branchPath = [...path, branch.name];
|
|
1983
|
+
const raised = interrupts.find((i) => samePath(i.branchPath, branchPath));
|
|
1984
|
+
if (raised !== void 0) pending.push({ interruptId: raised.interruptId, prompt: raised.prompt, frame: branchPath });
|
|
1985
|
+
else walk(branchPath);
|
|
1986
|
+
}
|
|
1987
|
+
};
|
|
1988
|
+
if (topInterrupt !== void 0 && topLast()?.fork === void 0) {
|
|
1989
|
+
pending.push({ interruptId: topInterrupt.interruptId, prompt: topInterrupt.prompt, frame: [] });
|
|
1990
|
+
} else {
|
|
1991
|
+
walk([]);
|
|
1992
|
+
}
|
|
1993
|
+
return pending;
|
|
1994
|
+
};
|
|
1995
|
+
const attach = (h) => {
|
|
1996
|
+
h.then((outcome) => {
|
|
1997
|
+
const last = topLast();
|
|
1998
|
+
if (outcome.signal === "$interrupt" && !stopRequested && last !== void 0) {
|
|
1999
|
+
const pending = pendingInterrupts();
|
|
2000
|
+
const head = pending[0];
|
|
2001
|
+
if (head !== void 0) {
|
|
2002
|
+
status = "interrupt";
|
|
2003
|
+
paused = { outcome, pending };
|
|
2004
|
+
for (const frame of frames.values()) if (frame.status === "held") frame.status = "ended";
|
|
2005
|
+
updateRenewal();
|
|
2006
|
+
deliver({
|
|
2007
|
+
kind: "interrupt",
|
|
2008
|
+
frame: head.frame,
|
|
2009
|
+
runId: h.runId,
|
|
2010
|
+
last,
|
|
2011
|
+
interruptId: head.interruptId,
|
|
2012
|
+
prompt: head.prompt,
|
|
2013
|
+
pending,
|
|
2014
|
+
state: snapshotOf(outcome.state)
|
|
2015
|
+
}, head.frame);
|
|
2016
|
+
return;
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
finish(outcome);
|
|
2020
|
+
}, fail);
|
|
2021
|
+
};
|
|
2022
|
+
const gate = async (next, state, info) => {
|
|
2023
|
+
const path = info.branchPath;
|
|
2024
|
+
let frame = frameOf(path);
|
|
2025
|
+
if (frame === void 0) {
|
|
2026
|
+
if (path.length === 0) {
|
|
2027
|
+
frame = addFrame(path, { status: "running", mode: topMode, lastRelease: "hold" });
|
|
2028
|
+
} else {
|
|
2029
|
+
frame = addFrame(path, { status: "running", ...branchStart(frameOf(path.slice(0, -1))?.forkMode) });
|
|
2030
|
+
}
|
|
2031
|
+
} else if (frame.status === "in-fork") {
|
|
2032
|
+
dropDescendants(path);
|
|
2033
|
+
frame.mode = frame.joinHold === false ? "continue" : "step";
|
|
2034
|
+
frame.joinHold = void 0;
|
|
2035
|
+
frame.forkMode = void 0;
|
|
2036
|
+
}
|
|
2037
|
+
if (info.signal?.aborted === true) return { patches: [], halt: true };
|
|
2038
|
+
let reason;
|
|
2039
|
+
let condition;
|
|
2040
|
+
if (frame.mode !== "continue") {
|
|
2041
|
+
reason = frame.mode;
|
|
2042
|
+
} else {
|
|
2043
|
+
const breakpoint = breakpoints.get(next);
|
|
2044
|
+
if (breakpoint !== void 0) {
|
|
2045
|
+
try {
|
|
2046
|
+
if (breakpoint.when === void 0 || breakpoint.when(state)) reason = "breakpoint";
|
|
2047
|
+
} catch (error) {
|
|
2048
|
+
reason = "breakpoint";
|
|
2049
|
+
condition = { error };
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
const enter = () => {
|
|
2054
|
+
frame.before = snapshotOf(state);
|
|
2055
|
+
if (info.fork) {
|
|
2056
|
+
frame.status = "in-fork";
|
|
2057
|
+
frame.forkMode ??= frame.mode === "continue" ? "continue" : "over";
|
|
2058
|
+
seedPausedFork(frame, next);
|
|
2059
|
+
} else {
|
|
2060
|
+
frame.status = "running";
|
|
2061
|
+
}
|
|
2062
|
+
};
|
|
2063
|
+
if (reason === void 0) {
|
|
2064
|
+
enter();
|
|
2065
|
+
return { patches: [], halt: false };
|
|
2066
|
+
}
|
|
2067
|
+
frame.status = "held";
|
|
2068
|
+
frame.next = next;
|
|
2069
|
+
frame.isFork = info.fork;
|
|
2070
|
+
frame.live = state;
|
|
2071
|
+
frame.forkMode = void 0;
|
|
2072
|
+
if (info.touchedKeys !== void 0) frame.touched = info.touchedKeys;
|
|
2073
|
+
const last = lastByFrame.get(keyOf(path));
|
|
2074
|
+
const stop = {
|
|
2075
|
+
kind: "step",
|
|
2076
|
+
frame: path,
|
|
2077
|
+
reason,
|
|
2078
|
+
runId: handle.runId,
|
|
2079
|
+
next,
|
|
2080
|
+
...last !== void 0 ? { last } : {},
|
|
2081
|
+
state: snapshotOf(state),
|
|
2082
|
+
...condition !== void 0 ? { conditionError: condition.error } : {}
|
|
2083
|
+
};
|
|
2084
|
+
frame.stop = stop;
|
|
2085
|
+
const released = new Promise((resolve) => {
|
|
2086
|
+
frame.release = resolve;
|
|
2087
|
+
});
|
|
2088
|
+
const onHalt = () => {
|
|
2089
|
+
frame.release?.({ patches: [], halt: true });
|
|
2090
|
+
};
|
|
2091
|
+
info.signal?.addEventListener("abort", onHalt);
|
|
2092
|
+
updateRenewal();
|
|
2093
|
+
offerStop(stop);
|
|
2094
|
+
const result = await released;
|
|
2095
|
+
info.signal?.removeEventListener("abort", onHalt);
|
|
2096
|
+
frame.release = void 0;
|
|
2097
|
+
frame.live = void 0;
|
|
2098
|
+
if (!result.halt) enter();
|
|
2099
|
+
updateRenewal();
|
|
2100
|
+
return result;
|
|
2101
|
+
};
|
|
2102
|
+
const release = (frame, how, halt = false) => {
|
|
2103
|
+
if (!halt) {
|
|
2104
|
+
frame.mode = how === "next" ? "step" : "continue";
|
|
2105
|
+
frame.lastRelease = how === "next" || how === "stepInto" ? "hold" : "continue";
|
|
2106
|
+
if (frame.isFork === true) frame.forkMode = how === "stepInto" ? "fork-entry" : how === "next" ? "over" : "continue";
|
|
2107
|
+
frame.status = frame.isFork === true ? "in-fork" : "running";
|
|
2108
|
+
}
|
|
2109
|
+
const patches = frame.patches;
|
|
2110
|
+
frame.patches = [];
|
|
2111
|
+
const r = frame.release;
|
|
2112
|
+
frame.release = void 0;
|
|
2113
|
+
r?.({ patches, halt });
|
|
2114
|
+
};
|
|
2115
|
+
const observer = {
|
|
2116
|
+
onStepSettled: (ctx, e) => {
|
|
2117
|
+
const path = ctx.branchPath ?? [];
|
|
2118
|
+
if (path.length === 0) {
|
|
2119
|
+
topInterrupt = e.interrupt !== void 0 ? { interruptId: e.interrupt.interruptId, prompt: e.interrupt.prompt, branchPath: [] } : void 0;
|
|
2120
|
+
}
|
|
2121
|
+
lastByFrame.set(keyOf(path), {
|
|
2122
|
+
name: ctx.stepName,
|
|
2123
|
+
outcome: e.outcome,
|
|
2124
|
+
...e.update !== void 0 ? { update: snapshotOf(e.update) } : {},
|
|
2125
|
+
...e.signal !== void 0 ? { signal: e.signal } : {},
|
|
2126
|
+
next: e.next,
|
|
2127
|
+
...e.error !== void 0 ? { error: e.error } : {},
|
|
2128
|
+
before: frameOf(path)?.before ?? { values: {}, shared: [] },
|
|
2129
|
+
durationMs: e.durationMs,
|
|
2130
|
+
...e.fork !== void 0 ? { fork: e.fork } : {}
|
|
2131
|
+
});
|
|
2132
|
+
},
|
|
2133
|
+
onInterrupt: (ctx, e) => {
|
|
2134
|
+
interrupts.push({ interruptId: e.interruptId, prompt: e.prompt, branchPath: ctx.branchPath ?? [] });
|
|
2135
|
+
},
|
|
2136
|
+
onRunEnd: (ctx, e) => {
|
|
2137
|
+
if (ctx.branchPath !== void 0) onFrameEnd(ctx.branchPath, e.signal);
|
|
2138
|
+
}
|
|
2139
|
+
};
|
|
2140
|
+
const refuse = (name) => {
|
|
2141
|
+
if (status === "ended") return new DebugCommandError("ended", name);
|
|
2142
|
+
if (status === "starting" || !started || command !== void 0) return new DebugCommandError("busy", name);
|
|
2143
|
+
if (status === "interrupt" && name !== "answer") return new DebugCommandError("not-held", name);
|
|
2144
|
+
if (status === "active" && name === "answer") return new DebugCommandError("not-held", name);
|
|
2145
|
+
return void 0;
|
|
2146
|
+
};
|
|
2147
|
+
const issue = (target) => {
|
|
2148
|
+
const result = deferred();
|
|
2149
|
+
command = { target, result };
|
|
2150
|
+
return result.promise;
|
|
2151
|
+
};
|
|
2152
|
+
const drive = (how) => {
|
|
2153
|
+
const refused = refuse(how);
|
|
2154
|
+
if (refused !== void 0) return Promise.reject(refused);
|
|
2155
|
+
const frame = frameOf(focus);
|
|
2156
|
+
if (frame === void 0 || frame.status !== "held") return Promise.reject(new DebugCommandError("not-held", how));
|
|
2157
|
+
if (how === "stepInto" && frame.isFork !== true) return Promise.reject(new DebugCommandError("not-a-fork", how));
|
|
2158
|
+
const p = issue(frame.path);
|
|
2159
|
+
release(frame, how);
|
|
2160
|
+
updateRenewal();
|
|
2161
|
+
return p;
|
|
2162
|
+
};
|
|
2163
|
+
const session = {
|
|
2164
|
+
get sessionId() {
|
|
2165
|
+
return handle.sessionId;
|
|
2166
|
+
},
|
|
2167
|
+
get runId() {
|
|
2168
|
+
return handle.runId;
|
|
2169
|
+
},
|
|
2170
|
+
get current() {
|
|
2171
|
+
return current;
|
|
2172
|
+
},
|
|
2173
|
+
get done() {
|
|
2174
|
+
return done.promise;
|
|
2175
|
+
},
|
|
2176
|
+
get breakpoints() {
|
|
2177
|
+
return [...breakpoints.values()];
|
|
2178
|
+
},
|
|
2179
|
+
get frames() {
|
|
2180
|
+
return sortedFrames().map((f) => ({ frame: f.path, status: f.status, ...f.status === "held" && f.next !== void 0 ? { next: f.next } : {} }));
|
|
2181
|
+
},
|
|
2182
|
+
focus: (path) => {
|
|
2183
|
+
const frame = frameOf(path);
|
|
2184
|
+
if (status !== "active" || frame === void 0 || frame.status !== "held" || frame.stop === void 0) {
|
|
2185
|
+
throw new DebugCommandError("not-held", "focus");
|
|
2186
|
+
}
|
|
2187
|
+
focus = frame.path;
|
|
2188
|
+
setCurrent(frame.stop);
|
|
2189
|
+
return frame.stop;
|
|
2190
|
+
},
|
|
2191
|
+
next: () => drive("next"),
|
|
2192
|
+
continue: () => drive("continue"),
|
|
2193
|
+
stepInto: () => drive("stepInto"),
|
|
2194
|
+
continueAll: () => {
|
|
2195
|
+
const refused = refuse("continueAll");
|
|
2196
|
+
if (refused !== void 0) return Promise.reject(refused);
|
|
2197
|
+
const p = issue("any");
|
|
2198
|
+
for (const frame of sortedFrames()) if (frame.status === "held") release(frame, "continueAll");
|
|
2199
|
+
updateRenewal();
|
|
2200
|
+
return p;
|
|
2201
|
+
},
|
|
2202
|
+
answer: (response, id) => {
|
|
2203
|
+
const at = paused;
|
|
2204
|
+
const refused = refuse("answer") ?? (at === void 0 ? new DebugCommandError("not-held", "answer") : void 0);
|
|
2205
|
+
if (refused !== void 0 || at === void 0) return Promise.reject(refused);
|
|
2206
|
+
const target = id ?? at.pending[0].interruptId;
|
|
2207
|
+
if (!at.pending.some((p2) => p2.interruptId === target)) return Promise.reject(new NoInterruptError());
|
|
2208
|
+
let resumed;
|
|
2209
|
+
try {
|
|
2210
|
+
resumed = handle.resume(response, target);
|
|
2211
|
+
} catch (error) {
|
|
2212
|
+
return Promise.reject(error);
|
|
2213
|
+
}
|
|
2214
|
+
pausedForks = new Map([...lastByFrame].filter(([, s]) => s.fork !== void 0 && (s.outcome === "interrupt" || s.outcome === "stopped")));
|
|
2215
|
+
handle = resumed;
|
|
2216
|
+
paused = void 0;
|
|
2217
|
+
status = "active";
|
|
2218
|
+
topMode = "resume";
|
|
2219
|
+
frames = /* @__PURE__ */ new Map();
|
|
2220
|
+
focus = [];
|
|
2221
|
+
lastByFrame = /* @__PURE__ */ new Map();
|
|
2222
|
+
topInterrupt = void 0;
|
|
2223
|
+
interrupts = [];
|
|
2224
|
+
lease = {};
|
|
2225
|
+
const p = issue([]);
|
|
2226
|
+
attach(handle);
|
|
2227
|
+
return p;
|
|
2228
|
+
},
|
|
2229
|
+
setState: (patch) => {
|
|
2230
|
+
if (status === "ended") throw new DebugCommandError("ended", "setState");
|
|
2231
|
+
const frame = frameOf(focus);
|
|
2232
|
+
if (status !== "active" || frame === void 0 || frame.status !== "held" || frame.live === void 0) {
|
|
2233
|
+
throw new DebugCommandError("not-held", "setState");
|
|
2234
|
+
}
|
|
2235
|
+
const reserved = Object.keys(patch).find((k) => k.startsWith("$"));
|
|
2236
|
+
if (reserved !== void 0) throw new DebugCommandError("reserved-key", "setState", `framework key "${reserved}" cannot be set`);
|
|
2237
|
+
const reported = {};
|
|
2238
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
2239
|
+
frame.live[key] = cloneOrShare(value).value;
|
|
2240
|
+
reported[key] = cloneOrShare(value).value;
|
|
2241
|
+
frame.touched?.add(key);
|
|
2242
|
+
}
|
|
2243
|
+
frame.patches.push(reported);
|
|
2244
|
+
return snapshotOf(frame.live);
|
|
2245
|
+
},
|
|
2246
|
+
setBreakpoint: (step, when) => {
|
|
2247
|
+
breakpoints.set(step, { step, ...when !== void 0 ? { when } : {} });
|
|
2248
|
+
},
|
|
2249
|
+
clearBreakpoint: (step) => {
|
|
2250
|
+
breakpoints.delete(step);
|
|
2251
|
+
},
|
|
2252
|
+
stop: () => {
|
|
2253
|
+
if (status === "ended") return ended.promise;
|
|
2254
|
+
stopRequested = true;
|
|
2255
|
+
if (status === "interrupt" && paused !== void 0) {
|
|
2256
|
+
finish(paused.outcome);
|
|
2257
|
+
return ended.promise;
|
|
2258
|
+
}
|
|
2259
|
+
handle.stop();
|
|
2260
|
+
for (const frame of frames.values()) if (frame.status === "held") release(frame, "continueAll", true);
|
|
2261
|
+
return ended.promise;
|
|
2262
|
+
}
|
|
2263
|
+
};
|
|
2264
|
+
handle = args.start({ gate, observer, onLease: (l) => {
|
|
2265
|
+
lease = l;
|
|
2266
|
+
} });
|
|
2267
|
+
status = "active";
|
|
2268
|
+
attach(handle);
|
|
2269
|
+
if (signal !== void 0) {
|
|
2270
|
+
signal.addEventListener("abort", onAbort);
|
|
2271
|
+
if (signal.aborted) onAbort();
|
|
2272
|
+
}
|
|
2273
|
+
return session;
|
|
2274
|
+
}
|
|
2275
|
+
|
|
1620
2276
|
// src/agent/create-agent.ts
|
|
1621
2277
|
import { randomUUID } from "crypto";
|
|
1622
2278
|
|
|
@@ -1738,13 +2394,26 @@ var InvalidClaimOptionsError = class extends Error {
|
|
|
1738
2394
|
this.ttlMs = ttlMs;
|
|
1739
2395
|
}
|
|
1740
2396
|
};
|
|
2397
|
+
var AGENT_OPTION_EXPECTED = { observer: "an object", onObserverError: "a function", checkpoint: "'off' or 'step'" };
|
|
2398
|
+
var InvalidAgentOptionError = class extends Error {
|
|
2399
|
+
/** The option that was rejected. */
|
|
2400
|
+
key;
|
|
2401
|
+
/** The rejected value, as given. */
|
|
2402
|
+
value;
|
|
2403
|
+
constructor(key, value) {
|
|
2404
|
+
super(`${key} must be ${AGENT_OPTION_EXPECTED[key]} \u2014 got ${describeValue(value)} in createAgent options`);
|
|
2405
|
+
this.name = "InvalidAgentOptionError";
|
|
2406
|
+
this.key = key;
|
|
2407
|
+
this.value = value;
|
|
2408
|
+
}
|
|
2409
|
+
};
|
|
1741
2410
|
var InvalidRunResourceError = class extends Error {
|
|
1742
2411
|
/** The resource key that was rejected. */
|
|
1743
2412
|
key;
|
|
1744
2413
|
/** The rejected value, as given. */
|
|
1745
2414
|
value;
|
|
1746
2415
|
constructor(key, value) {
|
|
1747
|
-
super(`${key} must be a non-empty string \u2014 got ${describeValue(value)} in agent.run() resources`);
|
|
2416
|
+
super(key === "checkpoint" ? `checkpoint must be 'off' or 'step' \u2014 got ${describeValue(value)} in the call's resources` : `${key} must be a non-empty string \u2014 got ${describeValue(value)} in agent.run() resources`);
|
|
1748
2417
|
this.name = "InvalidRunResourceError";
|
|
1749
2418
|
this.key = key;
|
|
1750
2419
|
this.value = value;
|
|
@@ -1756,6 +2425,12 @@ function readIdResource(resources, key) {
|
|
|
1756
2425
|
if (typeof value !== "string" || value.length === 0) throw new InvalidRunResourceError(key, value);
|
|
1757
2426
|
return value;
|
|
1758
2427
|
}
|
|
2428
|
+
function readCheckpointResource(resources) {
|
|
2429
|
+
const value = resources["checkpoint"];
|
|
2430
|
+
if (value === void 0) return void 0;
|
|
2431
|
+
if (!isCheckpointMode(value)) throw new InvalidRunResourceError("checkpoint", value);
|
|
2432
|
+
return value;
|
|
2433
|
+
}
|
|
1759
2434
|
var _agentInternals = /* @__PURE__ */ Symbol("_agentInternals");
|
|
1760
2435
|
function createAgent(id, h, slots, agentOptions) {
|
|
1761
2436
|
const internals = getInternals(h);
|
|
@@ -1793,6 +2468,27 @@ function createAgent(id, h, slots, agentOptions) {
|
|
|
1793
2468
|
agentClaimOptions = { ttlMs };
|
|
1794
2469
|
}
|
|
1795
2470
|
const fallbackClaimOptions = () => agentClaimOptions ?? { ttlMs: defaultClaimTtlMs() };
|
|
2471
|
+
const agentObserver = agentOptions?.observer;
|
|
2472
|
+
if (agentObserver !== void 0 && (agentObserver === null || typeof agentObserver !== "object" || Array.isArray(agentObserver))) {
|
|
2473
|
+
throw new InvalidAgentOptionError("observer", agentObserver);
|
|
2474
|
+
}
|
|
2475
|
+
const agentErrorSink = agentOptions?.onObserverError;
|
|
2476
|
+
if (agentErrorSink !== void 0 && typeof agentErrorSink !== "function") {
|
|
2477
|
+
throw new InvalidAgentOptionError("onObserverError", agentErrorSink);
|
|
2478
|
+
}
|
|
2479
|
+
const resolveObserver = (resources) => {
|
|
2480
|
+
const runObserver = extractRunObserver(resources);
|
|
2481
|
+
const onObserverError = extractRunErrorSink(resources) ?? agentErrorSink;
|
|
2482
|
+
const observer = agentObserver === void 0 ? runObserver : runObserver === void 0 || runObserver === agentObserver ? agentObserver : composeObservers([agentObserver, runObserver], onObserverError);
|
|
2483
|
+
return {
|
|
2484
|
+
...observer !== void 0 ? { observer } : {},
|
|
2485
|
+
...onObserverError !== void 0 ? { onObserverError } : {}
|
|
2486
|
+
};
|
|
2487
|
+
};
|
|
2488
|
+
const agentCheckpoint = agentOptions?.checkpoint;
|
|
2489
|
+
if (agentCheckpoint !== void 0 && !isCheckpointMode(agentCheckpoint)) {
|
|
2490
|
+
throw new InvalidAgentOptionError("checkpoint", agentCheckpoint);
|
|
2491
|
+
}
|
|
1796
2492
|
const resolvedProviders = /* @__PURE__ */ new Map();
|
|
1797
2493
|
const storeEntries = [];
|
|
1798
2494
|
const runtimeKeys = /* @__PURE__ */ new Set();
|
|
@@ -1842,12 +2538,13 @@ function createAgent(id, h, slots, agentOptions) {
|
|
|
1842
2538
|
runtimeKeys,
|
|
1843
2539
|
requiredKeys
|
|
1844
2540
|
};
|
|
1845
|
-
const reservedRunKeys = /* @__PURE__ */ new Set(["sessionId", "requestId", "signal", "events", "listeners", "observer", "onObserverError", "claimOptions", "parentRunId"]);
|
|
2541
|
+
const reservedRunKeys = /* @__PURE__ */ new Set(["sessionId", "requestId", "signal", "events", "listeners", "observer", "onObserverError", "claimOptions", "parentRunId", "checkpoint"]);
|
|
1846
2542
|
const inFlightSessions = /* @__PURE__ */ new Set();
|
|
2543
|
+
const debugSessions = /* @__PURE__ */ new Set();
|
|
1847
2544
|
const interruptPendingSessions = /* @__PURE__ */ new Set();
|
|
1848
2545
|
const resumeFailState = () => initializeState(null, {}, agentInternals.stateSchema);
|
|
1849
2546
|
const makeAgentResumeHandle = (resp, sId, iId, resumeResources) => {
|
|
1850
|
-
const { onStoreError, observer, onObserverError, telemetrySlots, parentRunId, claimOptions } = resumeResources;
|
|
2547
|
+
const { onStoreError, observer, onObserverError, telemetrySlots, parentRunId, claimOptions, checkpoint } = resumeResources;
|
|
1851
2548
|
let _stopped = false;
|
|
1852
2549
|
const abortController = new AbortController();
|
|
1853
2550
|
const flag = {
|
|
@@ -1880,6 +2577,7 @@ function createAgent(id, h, slots, agentOptions) {
|
|
|
1880
2577
|
sessionId: sId,
|
|
1881
2578
|
interruptId: iId,
|
|
1882
2579
|
response: resp,
|
|
2580
|
+
...heldLease(lease),
|
|
1883
2581
|
...onStoreError !== void 0 ? { onStoreError } : {},
|
|
1884
2582
|
failState: resumeFailState,
|
|
1885
2583
|
run: async () => {
|
|
@@ -1902,6 +2600,7 @@ function createAgent(id, h, slots, agentOptions) {
|
|
|
1902
2600
|
agentCtx,
|
|
1903
2601
|
{
|
|
1904
2602
|
mode: "resume",
|
|
2603
|
+
checkpoint,
|
|
1905
2604
|
shouldStop: () => flag.stopped,
|
|
1906
2605
|
onBeforeStep: (n) => {
|
|
1907
2606
|
ref.current = n;
|
|
@@ -1933,266 +2632,280 @@ function createAgent(id, h, slots, agentOptions) {
|
|
|
1933
2632
|
};
|
|
1934
2633
|
return createRunHandle(sId, rId, exec, flag, ref, resumeFn);
|
|
1935
2634
|
};
|
|
1936
|
-
const
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
if (!agentInternals.runtimeKeys.has(key) && !agentInternals.requiredKeys.has(key)) {
|
|
1942
|
-
throw new UnknownRunSlotError(key);
|
|
1943
|
-
}
|
|
2635
|
+
const startRun = (initialState, resources, debug) => {
|
|
2636
|
+
for (const key of Object.keys(resources)) {
|
|
2637
|
+
if (reservedRunKeys.has(key)) continue;
|
|
2638
|
+
if (!agentInternals.runtimeKeys.has(key) && !agentInternals.requiredKeys.has(key)) {
|
|
2639
|
+
throw new UnknownRunSlotError(key);
|
|
1944
2640
|
}
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
2641
|
+
}
|
|
2642
|
+
for (const key of Object.keys(resources)) {
|
|
2643
|
+
if (reservedRunKeys.has(key)) continue;
|
|
2644
|
+
if (agentInternals.requiredKeys.has(key)) {
|
|
2645
|
+
throw new RequiredSlotInRunError(key);
|
|
1950
2646
|
}
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
2647
|
+
}
|
|
2648
|
+
for (const key of agentInternals.runtimeKeys) {
|
|
2649
|
+
if (!(key in resources)) {
|
|
2650
|
+
throw new MissingRuntimeSlotError(key);
|
|
1955
2651
|
}
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
}
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2652
|
+
}
|
|
2653
|
+
const sessionId = readIdResource(resources, "sessionId") ?? randomUUID();
|
|
2654
|
+
const requestId = readIdResource(resources, "requestId");
|
|
2655
|
+
const checkpoint = readCheckpointResource(resources) ?? agentCheckpoint ?? "off";
|
|
2656
|
+
const runId = randomUUID();
|
|
2657
|
+
if (debugSessions.has(sessionId)) throw new SessionInDebugError(sessionId);
|
|
2658
|
+
if (inFlightSessions.has(sessionId)) throw new SessionInFlightError(sessionId);
|
|
2659
|
+
if (capturedStore === void 0 && interruptPendingSessions.has(sessionId)) throw new SessionPendingInterruptError(sessionId);
|
|
2660
|
+
inFlightSessions.add(sessionId);
|
|
2661
|
+
let _stopped = false;
|
|
2662
|
+
const abortController = new AbortController();
|
|
2663
|
+
const stopFlag = {
|
|
2664
|
+
get stopped() {
|
|
2665
|
+
return _stopped;
|
|
2666
|
+
},
|
|
2667
|
+
set stopped(v) {
|
|
2668
|
+
_stopped = v;
|
|
2669
|
+
if (v) abortController.abort();
|
|
2670
|
+
}
|
|
2671
|
+
};
|
|
2672
|
+
const rawSignal = debug === void 0 ? resources["signal"] : void 0;
|
|
2673
|
+
let ctxSignal = abortController.signal;
|
|
2674
|
+
if (rawSignal instanceof AbortSignal) {
|
|
2675
|
+
ctxSignal = rawSignal;
|
|
2676
|
+
rawSignal.addEventListener("abort", () => {
|
|
2677
|
+
stopFlag.stopped = true;
|
|
2678
|
+
abortController.abort();
|
|
2679
|
+
}, { once: true });
|
|
2680
|
+
if (rawSignal.aborted) stopFlag.stopped = true;
|
|
2681
|
+
}
|
|
2682
|
+
const stepRef = { current: null };
|
|
2683
|
+
const runtimeSlots = {};
|
|
2684
|
+
for (const key of agentInternals.runtimeKeys) {
|
|
2685
|
+
if (reservedRunKeys.has(key)) continue;
|
|
2686
|
+
runtimeSlots[key] = resources[key];
|
|
2687
|
+
}
|
|
2688
|
+
const ctx = {
|
|
2689
|
+
...Object.fromEntries(agentInternals.resolvedProviders),
|
|
2690
|
+
...runtimeSlots,
|
|
2691
|
+
agentId: id,
|
|
2692
|
+
sessionId,
|
|
2693
|
+
runId,
|
|
2694
|
+
signal: ctxSignal,
|
|
2695
|
+
...agentOptions?.instanceId !== void 0 ? { instanceId: agentOptions.instanceId } : {}
|
|
2696
|
+
};
|
|
2697
|
+
const events = extractRunEvents(resources);
|
|
2698
|
+
const listeners = extractRunListeners(resources);
|
|
2699
|
+
const resolved = resolveObserver(resources);
|
|
2700
|
+
const onObserverError = resolved.onObserverError;
|
|
2701
|
+
const observer = debug === void 0 ? resolved.observer : resolved.observer === void 0 ? debug.observer : composeObservers([resolved.observer, debug.observer], onObserverError);
|
|
2702
|
+
const telemetrySlots = buildTelemetrySlots([
|
|
2703
|
+
...agentInternals.resolvedProviders.entries(),
|
|
2704
|
+
...Object.entries(runtimeSlots)
|
|
2705
|
+
]);
|
|
2706
|
+
const parentRunId = typeof resources["parentRunId"] === "string" ? resources["parentRunId"] : void 0;
|
|
2707
|
+
const claimOptions = parseClaimOptions(resources["claimOptions"], fallbackClaimOptions());
|
|
2708
|
+
const options = {
|
|
2709
|
+
mode: "run",
|
|
2710
|
+
checkpoint,
|
|
2711
|
+
...requestId !== void 0 ? { requestId } : {},
|
|
2712
|
+
shouldStop: () => stopFlag.stopped,
|
|
2713
|
+
onBeforeStep: (name, state) => {
|
|
2714
|
+
stepRef.current = name;
|
|
2715
|
+
events.onBeforeStep?.(name, state);
|
|
2716
|
+
},
|
|
2717
|
+
// spread optional event callbacks only when defined (exactOptionalPropertyTypes requires omission, not undefined)
|
|
2718
|
+
...events.onStoreError !== void 0 ? { onStoreError: events.onStoreError } : {},
|
|
2719
|
+
...events.onAfterStep !== void 0 ? { onAfterStep: events.onAfterStep } : {},
|
|
2720
|
+
...events.onError !== void 0 ? { onError: events.onError } : {},
|
|
2721
|
+
...events.onComplete !== void 0 ? { onComplete: events.onComplete } : {},
|
|
2722
|
+
...events.onInterrupt !== void 0 ? { onInterrupt: events.onInterrupt } : {},
|
|
2723
|
+
...Object.keys(listeners).length > 0 ? { listeners } : {},
|
|
2724
|
+
...observer !== void 0 ? { observer } : {},
|
|
2725
|
+
...onObserverError !== void 0 ? { onObserverError } : {},
|
|
2726
|
+
...agentOptions?.instanceId !== void 0 ? { instanceId: agentOptions.instanceId } : {},
|
|
2727
|
+
...parentRunId !== void 0 ? { parentRunId } : {},
|
|
2728
|
+
...telemetrySlots.length > 0 ? { telemetrySlots } : {},
|
|
2729
|
+
...debug !== void 0 ? { debugGate: debug.gate } : {}
|
|
2730
|
+
};
|
|
2731
|
+
let lastResult = null;
|
|
2732
|
+
const execution = (async () => {
|
|
2733
|
+
await Promise.resolve();
|
|
2734
|
+
try {
|
|
2735
|
+
return await runClaimed({
|
|
2736
|
+
store: capturedStore,
|
|
2737
|
+
agentId: id,
|
|
2738
|
+
sessionId,
|
|
2739
|
+
claimOptions,
|
|
2740
|
+
...events.onStoreError !== void 0 ? { onStoreError: events.onStoreError } : {},
|
|
2741
|
+
failState: () => initializeState(null, initialState, agentInternals.stateSchema),
|
|
2742
|
+
body: async (lease) => {
|
|
2743
|
+
debug?.onLease(lease);
|
|
2744
|
+
const r = await runWithSession(
|
|
2745
|
+
capturedStore,
|
|
2746
|
+
id,
|
|
2747
|
+
sessionId,
|
|
2748
|
+
runId,
|
|
2749
|
+
agentInternals.loopDef,
|
|
2750
|
+
initialState,
|
|
2751
|
+
agentInternals.stateSchema,
|
|
2752
|
+
ctx,
|
|
2753
|
+
{ ...options, ...lease }
|
|
2754
|
+
);
|
|
2755
|
+
if (capturedStore === void 0 && r.signal === "$interrupt") interruptPendingSessions.add(sessionId);
|
|
2756
|
+
lastResult = { state: r.state, cursor: r.cursor, runId, ...r.carrier !== void 0 ? { carrier: r.carrier } : {} };
|
|
2757
|
+
return { state: r.state, signal: r.signal };
|
|
2758
|
+
}
|
|
2759
|
+
});
|
|
2760
|
+
} finally {
|
|
2761
|
+
inFlightSessions.delete(sessionId);
|
|
2762
|
+
}
|
|
2763
|
+
})();
|
|
2764
|
+
const buildResumeFn = () => {
|
|
2765
|
+
return (response, interruptId) => {
|
|
2766
|
+
if (inFlightSessions.has(sessionId)) throw new SessionInFlightError(sessionId);
|
|
2767
|
+
interruptPendingSessions.delete(sessionId);
|
|
2768
|
+
inFlightSessions.add(sessionId);
|
|
2769
|
+
let _resumeStopped = false;
|
|
2770
|
+
const resumeAbortController = new AbortController();
|
|
2771
|
+
const resumeStopFlag = {
|
|
2772
|
+
get stopped() {
|
|
2773
|
+
return _resumeStopped;
|
|
2774
|
+
},
|
|
2775
|
+
set stopped(v) {
|
|
2776
|
+
_resumeStopped = v;
|
|
2777
|
+
if (v) resumeAbortController.abort();
|
|
2778
|
+
}
|
|
2779
|
+
};
|
|
2780
|
+
const resumeStepRef = { current: null };
|
|
2781
|
+
const resumeRunId = randomUUID();
|
|
2782
|
+
const resumeExecution = (async () => {
|
|
2783
|
+
await Promise.resolve();
|
|
2784
|
+
try {
|
|
2785
|
+
const resumeCtx = {
|
|
2786
|
+
...ctx,
|
|
2787
|
+
runId: resumeRunId,
|
|
2788
|
+
signal: resumeAbortController.signal
|
|
2789
|
+
};
|
|
2790
|
+
if (capturedStore !== void 0) {
|
|
2791
|
+
const store = capturedStore;
|
|
2792
|
+
const onStoreError = events.onStoreError;
|
|
2793
|
+
return await runClaimed({
|
|
2794
|
+
store,
|
|
2795
|
+
agentId: id,
|
|
2044
2796
|
sessionId,
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
ctx,
|
|
2050
|
-
{ ...options, ...lease }
|
|
2051
|
-
);
|
|
2052
|
-
if (capturedStore === void 0 && r.signal === "$interrupt") interruptPendingSessions.add(sessionId);
|
|
2053
|
-
lastResult = { state: r.state, cursor: r.cursor, runId, ...r.carrier !== void 0 ? { carrier: r.carrier } : {} };
|
|
2054
|
-
return { state: r.state, signal: r.signal };
|
|
2055
|
-
}
|
|
2056
|
-
});
|
|
2057
|
-
} finally {
|
|
2058
|
-
inFlightSessions.delete(sessionId);
|
|
2059
|
-
}
|
|
2060
|
-
})();
|
|
2061
|
-
const buildResumeFn = () => {
|
|
2062
|
-
return (response, interruptId) => {
|
|
2063
|
-
if (inFlightSessions.has(sessionId)) throw new SessionInFlightError(sessionId);
|
|
2064
|
-
interruptPendingSessions.delete(sessionId);
|
|
2065
|
-
inFlightSessions.add(sessionId);
|
|
2066
|
-
let _resumeStopped = false;
|
|
2067
|
-
const resumeAbortController = new AbortController();
|
|
2068
|
-
const resumeStopFlag = {
|
|
2069
|
-
get stopped() {
|
|
2070
|
-
return _resumeStopped;
|
|
2071
|
-
},
|
|
2072
|
-
set stopped(v) {
|
|
2073
|
-
_resumeStopped = v;
|
|
2074
|
-
if (v) resumeAbortController.abort();
|
|
2075
|
-
}
|
|
2076
|
-
};
|
|
2077
|
-
const resumeStepRef = { current: null };
|
|
2078
|
-
const resumeRunId = randomUUID();
|
|
2079
|
-
const resumeExecution = (async () => {
|
|
2080
|
-
await Promise.resolve();
|
|
2081
|
-
try {
|
|
2082
|
-
const resumeCtx = {
|
|
2083
|
-
...ctx,
|
|
2084
|
-
runId: resumeRunId,
|
|
2085
|
-
signal: resumeAbortController.signal
|
|
2086
|
-
};
|
|
2087
|
-
if (capturedStore !== void 0) {
|
|
2088
|
-
const store = capturedStore;
|
|
2089
|
-
const onStoreError = events.onStoreError;
|
|
2090
|
-
return await runClaimed({
|
|
2797
|
+
claimOptions,
|
|
2798
|
+
...onStoreError !== void 0 ? { onStoreError } : {},
|
|
2799
|
+
failState: resumeFailState,
|
|
2800
|
+
body: (lease) => injectThenRun({
|
|
2091
2801
|
store,
|
|
2092
2802
|
agentId: id,
|
|
2093
2803
|
sessionId,
|
|
2094
|
-
|
|
2804
|
+
interruptId,
|
|
2805
|
+
response,
|
|
2806
|
+
...heldLease(lease),
|
|
2095
2807
|
...onStoreError !== void 0 ? { onStoreError } : {},
|
|
2096
2808
|
failState: resumeFailState,
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
{
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
lastResult = { state: r2.state, cursor: r2.cursor, runId: resumeRunId, ...r2.carrier !== void 0 ? { carrier: r2.carrier } : {} };
|
|
2137
|
-
return { state: r2.state, signal: r2.signal };
|
|
2138
|
-
}
|
|
2139
|
-
})
|
|
2140
|
-
});
|
|
2141
|
-
}
|
|
2142
|
-
const prev = lastResult;
|
|
2143
|
-
if (prev === null) throw new NoInterruptError();
|
|
2144
|
-
const injected = injectInterruptResponseIntoState(prev.cursor ?? void 0, prev.state, interruptId, response);
|
|
2145
|
-
if (!injected.found) throw new NoInterruptError();
|
|
2146
|
-
const state = injected.state;
|
|
2147
|
-
const cursor = injected.cursor;
|
|
2148
|
-
const r = await runLoop(
|
|
2149
|
-
agentInternals.loopDef,
|
|
2150
|
-
state,
|
|
2151
|
-
resumeCtx,
|
|
2152
|
-
agentInternals.stateSchema,
|
|
2153
|
-
() => resumeStopFlag.stopped,
|
|
2154
|
-
cursor,
|
|
2155
|
-
{
|
|
2156
|
-
runId: resumeRunId,
|
|
2157
|
-
resumedFrom: { runId: prev.runId, ...prev.carrier !== void 0 ? { carrier: prev.carrier } : {} },
|
|
2158
|
-
onBeforeStep: (n, s) => {
|
|
2159
|
-
resumeStepRef.current = n;
|
|
2160
|
-
events.onBeforeStep?.(n, s);
|
|
2161
|
-
},
|
|
2162
|
-
...events.onAfterStep !== void 0 ? { onAfterStep: events.onAfterStep } : {},
|
|
2163
|
-
...events.onError !== void 0 ? { onError: events.onError } : {},
|
|
2164
|
-
...events.onComplete !== void 0 ? { onComplete: events.onComplete } : {},
|
|
2165
|
-
...events.onInterrupt !== void 0 ? { onInterrupt: events.onInterrupt } : {},
|
|
2166
|
-
...Object.keys(listeners).length > 0 ? { listeners } : {},
|
|
2167
|
-
...observer !== void 0 ? { observer } : {},
|
|
2168
|
-
...onObserverError !== void 0 ? { onObserverError } : {},
|
|
2169
|
-
...telemetrySlots.length > 0 ? { telemetrySlots } : {}
|
|
2170
|
-
}
|
|
2171
|
-
);
|
|
2172
|
-
if (r.signal === "$interrupt") interruptPendingSessions.add(sessionId);
|
|
2173
|
-
lastResult = { state: r.state, cursor: r.cursor, runId: resumeRunId, ...r.carrier !== void 0 ? { carrier: r.carrier } : {} };
|
|
2174
|
-
return { state: r.state, signal: r.signal };
|
|
2175
|
-
} finally {
|
|
2176
|
-
inFlightSessions.delete(sessionId);
|
|
2809
|
+
run: async () => {
|
|
2810
|
+
debug?.onLease(lease);
|
|
2811
|
+
const r2 = await runWithSession(
|
|
2812
|
+
store,
|
|
2813
|
+
id,
|
|
2814
|
+
sessionId,
|
|
2815
|
+
resumeRunId,
|
|
2816
|
+
agentInternals.loopDef,
|
|
2817
|
+
{},
|
|
2818
|
+
agentInternals.stateSchema,
|
|
2819
|
+
resumeCtx,
|
|
2820
|
+
{
|
|
2821
|
+
mode: "resume",
|
|
2822
|
+
checkpoint,
|
|
2823
|
+
shouldStop: () => resumeStopFlag.stopped,
|
|
2824
|
+
onBeforeStep: (n, s) => {
|
|
2825
|
+
resumeStepRef.current = n;
|
|
2826
|
+
events.onBeforeStep?.(n, s);
|
|
2827
|
+
},
|
|
2828
|
+
// spread optional event callbacks only when defined (exactOptionalPropertyTypes requires omission, not undefined)
|
|
2829
|
+
...events.onStoreError !== void 0 ? { onStoreError: events.onStoreError } : {},
|
|
2830
|
+
...events.onAfterStep !== void 0 ? { onAfterStep: events.onAfterStep } : {},
|
|
2831
|
+
...events.onError !== void 0 ? { onError: events.onError } : {},
|
|
2832
|
+
...events.onComplete !== void 0 ? { onComplete: events.onComplete } : {},
|
|
2833
|
+
...events.onInterrupt !== void 0 ? { onInterrupt: events.onInterrupt } : {},
|
|
2834
|
+
...Object.keys(listeners).length > 0 ? { listeners } : {},
|
|
2835
|
+
...observer !== void 0 ? { observer } : {},
|
|
2836
|
+
...onObserverError !== void 0 ? { onObserverError } : {},
|
|
2837
|
+
...telemetrySlots.length > 0 ? { telemetrySlots } : {},
|
|
2838
|
+
...agentOptions?.instanceId !== void 0 ? { instanceId: agentOptions.instanceId } : {},
|
|
2839
|
+
...debug !== void 0 ? { debugGate: debug.gate } : {},
|
|
2840
|
+
...lease
|
|
2841
|
+
}
|
|
2842
|
+
);
|
|
2843
|
+
lastResult = { state: r2.state, cursor: r2.cursor, runId: resumeRunId, ...r2.carrier !== void 0 ? { carrier: r2.carrier } : {} };
|
|
2844
|
+
return { state: r2.state, signal: r2.signal };
|
|
2845
|
+
}
|
|
2846
|
+
})
|
|
2847
|
+
});
|
|
2177
2848
|
}
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2849
|
+
const prev = lastResult;
|
|
2850
|
+
if (prev === null) throw new NoInterruptError();
|
|
2851
|
+
const injected = injectInterruptResponseIntoState(prev.cursor ?? void 0, prev.state, interruptId, response);
|
|
2852
|
+
if (!injected.found) throw new NoInterruptError();
|
|
2853
|
+
const state = injected.state;
|
|
2854
|
+
const cursor = injected.cursor;
|
|
2855
|
+
const r = await runLoop(
|
|
2856
|
+
agentInternals.loopDef,
|
|
2857
|
+
state,
|
|
2858
|
+
resumeCtx,
|
|
2859
|
+
agentInternals.stateSchema,
|
|
2860
|
+
() => resumeStopFlag.stopped,
|
|
2861
|
+
cursor,
|
|
2862
|
+
{
|
|
2863
|
+
runId: resumeRunId,
|
|
2864
|
+
resumedFrom: { runId: prev.runId, ...prev.carrier !== void 0 ? { carrier: prev.carrier } : {} },
|
|
2865
|
+
onBeforeStep: (n, s) => {
|
|
2866
|
+
resumeStepRef.current = n;
|
|
2867
|
+
events.onBeforeStep?.(n, s);
|
|
2868
|
+
},
|
|
2869
|
+
...events.onAfterStep !== void 0 ? { onAfterStep: events.onAfterStep } : {},
|
|
2870
|
+
...events.onError !== void 0 ? { onError: events.onError } : {},
|
|
2871
|
+
...events.onComplete !== void 0 ? { onComplete: events.onComplete } : {},
|
|
2872
|
+
...events.onInterrupt !== void 0 ? { onInterrupt: events.onInterrupt } : {},
|
|
2873
|
+
...Object.keys(listeners).length > 0 ? { listeners } : {},
|
|
2874
|
+
...observer !== void 0 ? { observer } : {},
|
|
2875
|
+
...onObserverError !== void 0 ? { onObserverError } : {},
|
|
2876
|
+
...telemetrySlots.length > 0 ? { telemetrySlots } : {},
|
|
2877
|
+
...debug !== void 0 ? { debugGate: debug.gate } : {}
|
|
2878
|
+
}
|
|
2879
|
+
);
|
|
2880
|
+
if (r.signal === "$interrupt") interruptPendingSessions.add(sessionId);
|
|
2881
|
+
lastResult = { state: r.state, cursor: r.cursor, runId: resumeRunId, ...r.carrier !== void 0 ? { carrier: r.carrier } : {} };
|
|
2882
|
+
return { state: r.state, signal: r.signal };
|
|
2883
|
+
} finally {
|
|
2884
|
+
inFlightSessions.delete(sessionId);
|
|
2885
|
+
}
|
|
2886
|
+
})();
|
|
2887
|
+
return createRunHandle(sessionId, resumeRunId, resumeExecution, resumeStopFlag, resumeStepRef, buildResumeFn());
|
|
2181
2888
|
};
|
|
2182
|
-
|
|
2183
|
-
|
|
2889
|
+
};
|
|
2890
|
+
return createRunHandle(sessionId, runId, execution, stopFlag, stepRef, buildResumeFn());
|
|
2891
|
+
};
|
|
2892
|
+
const agent = {
|
|
2893
|
+
id,
|
|
2894
|
+
run: (initialState, resources) => startRun(initialState, resources),
|
|
2184
2895
|
resume: (response, sessionId, interruptId, resources) => {
|
|
2896
|
+
if (debugSessions.has(sessionId)) throw new SessionInDebugError(sessionId);
|
|
2185
2897
|
if (inFlightSessions.has(sessionId)) throw new SessionInFlightError(sessionId);
|
|
2898
|
+
const checkpoint = readCheckpointResource(resources ?? {}) ?? agentCheckpoint ?? "off";
|
|
2186
2899
|
interruptPendingSessions.delete(sessionId);
|
|
2187
2900
|
inFlightSessions.add(sessionId);
|
|
2188
2901
|
const events = extractRunEvents(resources ?? {});
|
|
2189
|
-
const observer =
|
|
2190
|
-
const onObserverError = extractRunErrorSink(resources ?? {});
|
|
2902
|
+
const { observer, onObserverError } = resolveObserver(resources ?? {});
|
|
2191
2903
|
const telemetrySlots = buildTelemetrySlots(agentInternals.resolvedProviders.entries());
|
|
2192
2904
|
const parentRunId = resources?.["parentRunId"];
|
|
2193
2905
|
const claimOptions = parseClaimOptions(resources?.["claimOptions"], fallbackClaimOptions());
|
|
2194
2906
|
return makeAgentResumeHandle(response, sessionId, interruptId, {
|
|
2195
2907
|
claimOptions,
|
|
2908
|
+
checkpoint,
|
|
2196
2909
|
...events.onStoreError !== void 0 ? { onStoreError: events.onStoreError } : {},
|
|
2197
2910
|
...observer !== void 0 ? { observer } : {},
|
|
2198
2911
|
...onObserverError !== void 0 ? { onObserverError } : {},
|
|
@@ -2200,12 +2913,33 @@ function createAgent(id, h, slots, agentOptions) {
|
|
|
2200
2913
|
...typeof parentRunId === "string" ? { parentRunId } : {}
|
|
2201
2914
|
});
|
|
2202
2915
|
},
|
|
2916
|
+
debug: (initialState, resources) => {
|
|
2917
|
+
const rawSignal = resources["signal"];
|
|
2918
|
+
let sessionId;
|
|
2919
|
+
const session = createDebugSession({
|
|
2920
|
+
start: (hooks) => {
|
|
2921
|
+
const handle = startRun(initialState, resources, hooks);
|
|
2922
|
+
sessionId = handle.sessionId;
|
|
2923
|
+
return handle;
|
|
2924
|
+
},
|
|
2925
|
+
store: capturedStore,
|
|
2926
|
+
signal: rawSignal instanceof AbortSignal ? rawSignal : void 0,
|
|
2927
|
+
onEnd: () => {
|
|
2928
|
+
if (sessionId !== void 0) debugSessions.delete(sessionId);
|
|
2929
|
+
}
|
|
2930
|
+
});
|
|
2931
|
+
debugSessions.add(session.sessionId);
|
|
2932
|
+
return session;
|
|
2933
|
+
},
|
|
2203
2934
|
status: (sessionId) => querySessionPhase(capturedStore, id, sessionId),
|
|
2204
2935
|
[_agentInternals]: agentInternals
|
|
2205
2936
|
};
|
|
2206
2937
|
return agent;
|
|
2207
2938
|
}
|
|
2208
2939
|
export {
|
|
2940
|
+
DebugCommandError,
|
|
2941
|
+
InvalidAgentOptionError,
|
|
2942
|
+
InvalidClaimOptionsError,
|
|
2209
2943
|
InvalidRunResourceError,
|
|
2210
2944
|
LeaseExpiredError,
|
|
2211
2945
|
LoopNotDefinedError,
|
|
@@ -2213,6 +2947,7 @@ export {
|
|
|
2213
2947
|
REQUIRED_TAG,
|
|
2214
2948
|
RUNTIME_TAG,
|
|
2215
2949
|
SessionBusyError,
|
|
2950
|
+
SessionInDebugError,
|
|
2216
2951
|
SessionInFlightError,
|
|
2217
2952
|
SessionPendingInterruptError,
|
|
2218
2953
|
StoreLoadError,
|