@noetaris/harness 0.10.0 → 0.12.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/dist/index.js CHANGED
@@ -634,16 +634,14 @@ function extractRunListeners(resources) {
634
634
  // src/agent/ctx-within.ts
635
635
  function createWithinFn(observer, currentStep, onObserverError) {
636
636
  return (type, payload, fn) => {
637
- const wrap = observer?.wrap;
638
637
  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);
638
+ if (observer?.wrap === void 0 || stepCtx === null) return fn();
639
+ return runWrapped((inner) => observer.wrap?.(stepCtx, { kind: "event", type, payload }, inner), fn, onObserverError);
641
640
  };
642
641
  }
643
642
  function runStepScope(observer, stepCtx, fn, onObserverError) {
644
- const wrap = observer?.wrap;
645
- if (wrap === void 0) return fn();
646
- return runWrapped((inner) => wrap(stepCtx, { kind: "step" }, inner), fn, onObserverError);
643
+ if (observer?.wrap === void 0) return fn();
644
+ return runWrapped((inner) => observer.wrap?.(stepCtx, { kind: "step" }, inner), fn, onObserverError);
647
645
  }
648
646
 
649
647
  // src/loop/loop-executor.ts
@@ -707,10 +705,9 @@ function applyUpdate(state, update, schema, touchedKeys) {
707
705
  }
708
706
  }
709
707
  function fireSettled(obs, stepCtx, stepStart, fields, onObserverError) {
710
- const hook = obs?.onStepSettled;
711
- if (hook === void 0) return;
708
+ if (obs?.onStepSettled === void 0) return;
712
709
  const event = { ...fields, durationMs: Date.now() - stepStart };
713
- safeInvoke("onStepSettled", () => hook(stepCtx, event), onObserverError);
710
+ safeInvoke("onStepSettled", () => obs.onStepSettled?.(stepCtx, event), onObserverError);
714
711
  }
715
712
  function stepContextOf(runCtx, stepName) {
716
713
  return {
@@ -745,11 +742,13 @@ async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callb
745
742
  sessionId: ctx.sessionId,
746
743
  runId: typeof callbacks?.runId === "string" ? callbacks.runId : "",
747
744
  ...typeof callbacks?.parentRunId === "string" ? { parentRunId: callbacks.parentRunId } : {},
745
+ ...typeof callbacks?.requestId === "string" ? { requestId: callbacks.requestId } : {},
748
746
  ...typeof ctx.instanceId === "string" ? { instanceId: ctx.instanceId } : {},
749
747
  ...isBranch ? { branchPath } : {},
750
748
  // Branch runLoops receive the outer callbacks by spread, so the refs are dropped here.
751
749
  ...!isBranch && callbacks?.resumedFrom !== void 0 ? { resumedFrom: callbacks.resumedFrom } : {},
752
- ...!isBranch && callbacks?.branchedFrom !== void 0 ? { branchedFrom: callbacks.branchedFrom } : {}
750
+ ...!isBranch && callbacks?.branchedFrom !== void 0 ? { branchedFrom: callbacks.branchedFrom } : {},
751
+ ...!isBranch && callbacks?.continuedFrom !== void 0 ? { continuedFrom: callbacks.continuedFrom } : {}
753
752
  };
754
753
  safeInvoke("onRunStart", () => obs?.onRunStart?.(runCtx), onObserverError);
755
754
  const carrier = isBranch ? void 0 : normalizeCarrier(safeCall("carry", () => obs?.carry?.(runCtx), onObserverError));
@@ -773,6 +772,24 @@ async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callb
773
772
  endRun("$stopped");
774
773
  return withCarrier({ state, signal: null, cursor, paused: true });
775
774
  }
775
+ if (callbacks?.debugGate !== void 0) {
776
+ const next = isForkCursor(cursor) ? cursor.fork : cursor;
777
+ const fork = isForkCursor(cursor) || graph.steps.some((s) => s.name === next && isForkDef(s));
778
+ const { patches, halt } = await callbacks.debugGate(next, state, {
779
+ branchPath: branchPath ?? [],
780
+ fork,
781
+ ...callbacks.touchedKeys !== void 0 ? { touchedKeys: callbacks.touchedKeys } : {},
782
+ ...ctx.signal !== void 0 ? { signal: ctx.signal } : {}
783
+ });
784
+ const nextCtx = stepContextOf(runCtx, next);
785
+ for (const patch of patches) {
786
+ safeInvoke("onEvent", () => obs?.onEvent?.(nextCtx, "debug.setState", { patch }), onObserverError);
787
+ }
788
+ if (halt || shouldStop?.()) {
789
+ endRun("$stopped");
790
+ return withCarrier({ state, signal: null, cursor, paused: true });
791
+ }
792
+ }
776
793
  if (isForkCursor(cursor)) {
777
794
  const forkCursor = cursor;
778
795
  const forkNode = graph.steps.find((s) => s.name === forkCursor.fork);
@@ -1026,6 +1043,7 @@ async function dispatchFork(forkNode, priorBranches, outerState, outerCtx, schem
1026
1043
  } else {
1027
1044
  outerState[key] = outcome.result.state[key];
1028
1045
  }
1046
+ callbacks?.touchedKeys?.add(key);
1029
1047
  }
1030
1048
  }
1031
1049
  return {
@@ -1181,6 +1199,15 @@ var SessionInFlightError = class extends Error {
1181
1199
  this.sessionId = sessionId;
1182
1200
  }
1183
1201
  };
1202
+ var SessionInDebugError = class extends Error {
1203
+ /** The session being debugged. */
1204
+ sessionId;
1205
+ constructor(sessionId) {
1206
+ super(`session "${sessionId}" is being debugged`);
1207
+ this.name = "SessionInDebugError";
1208
+ this.sessionId = sessionId;
1209
+ }
1210
+ };
1184
1211
  var SessionPendingInterruptError = class extends Error {
1185
1212
  /** The session that is awaiting an interrupt response. */
1186
1213
  sessionId;
@@ -1287,12 +1314,32 @@ async function querySessionPhase(store, agentId, sessionId) {
1287
1314
  return storedSessionToPhase(loaded);
1288
1315
  }
1289
1316
  function runRefsOf(loaded) {
1290
- if (loaded === null || loaded.phase !== "paused") return {};
1291
- if (loaded.step !== void 0) {
1292
- return { resumedFrom: { runId: loaded.runId, ...loaded.carrier !== void 0 ? { carrier: loaded.carrier } : {} } };
1293
- }
1317
+ if (loaded === null) return {};
1318
+ const ref = { runId: loaded.runId, ...loaded.carrier !== void 0 ? { carrier: loaded.carrier } : {} };
1319
+ if (loaded.phase === "completed") return { continuedFrom: ref };
1320
+ if (loaded.step !== void 0) return { resumedFrom: ref };
1294
1321
  return loaded.branchedFrom !== void 0 ? { branchedFrom: loaded.branchedFrom } : {};
1295
1322
  }
1323
+ function decideRun(args) {
1324
+ const { loaded, older, mode, requestId, sessionId } = args;
1325
+ if (loaded === null) return { action: "start" };
1326
+ const pending = loaded.phase === "paused" && collectPendingInterrupts(loaded.step, loaded.finalState).length > 0;
1327
+ if (mode === "resume") {
1328
+ return loaded.phase === "completed" ? { action: "replay", row: loaded } : { action: "continue", mergeInput: false };
1329
+ }
1330
+ if (requestId !== void 0 && loaded.requestId === requestId) {
1331
+ if (loaded.phase === "completed" || pending) return { action: "replay", row: loaded };
1332
+ return { action: "continue", mergeInput: false };
1333
+ }
1334
+ if (older !== null) return { action: "replay", row: older };
1335
+ if (loaded.phase === "completed") return { action: "new-turn" };
1336
+ if (pending) return { action: "reject", error: new SessionPendingInterruptError(sessionId) };
1337
+ return { action: "continue", mergeInput: true };
1338
+ }
1339
+ function withoutSystemFields(loaded) {
1340
+ const { $error: _error, $interrupt: _interrupt, $interruptResponses: _responses, ...finalState } = loaded.finalState;
1341
+ return { ...loaded, finalState };
1342
+ }
1296
1343
  async function runWithSession(store, agentId, sessionId, runId, graph, initialStateArg, schema, ctx, options) {
1297
1344
  const leaseRef = options?.leaseRef ?? { current: null };
1298
1345
  const keepAlive = createKeepAliveFn(leaseRef, store, options?.claimTtlMs ?? defaultClaimTtlMs());
@@ -1306,10 +1353,11 @@ async function runWithSession(store, agentId, sessionId, runId, graph, initialSt
1306
1353
  }
1307
1354
  return false;
1308
1355
  };
1356
+ const { mode = "run", requestId: callerRequestId, ...loopOptions } = options ?? {};
1309
1357
  if (store === void 0) {
1310
1358
  const state2 = initializeState(null, initialStateArg, schema);
1311
1359
  const result2 = await runLoop(graph, state2, ctx, schema, composedShouldStop, void 0, {
1312
- ...options,
1360
+ ...loopOptions,
1313
1361
  runId,
1314
1362
  ...options?.parentRunId !== void 0 ? { parentRunId: options.parentRunId } : {}
1315
1363
  });
@@ -1320,31 +1368,46 @@ async function runWithSession(store, agentId, sessionId, runId, graph, initialSt
1320
1368
  }
1321
1369
  return result2;
1322
1370
  }
1371
+ const requestId = mode === "run" ? callerRequestId : void 0;
1372
+ const fail = (error) => {
1373
+ const failState = initializeState(null, initialStateArg, schema);
1374
+ failState["$error"] = error;
1375
+ return { state: failState, signal: "$error", paused: false, cursor: null };
1376
+ };
1323
1377
  let loaded;
1378
+ let older = null;
1324
1379
  try {
1325
1380
  loaded = await store.load(agentId, sessionId);
1381
+ if (requestId !== void 0 && loaded !== null && loaded.requestId !== requestId && typeof store.findRequest === "function") {
1382
+ older = await store.findRequest(agentId, sessionId, requestId);
1383
+ }
1326
1384
  } catch (error) {
1327
1385
  const storeError = new StoreLoadError(error);
1328
1386
  options?.onStoreError?.(storeError, "load");
1329
- const failState = initializeState(null, initialStateArg, schema);
1330
- failState["$error"] = storeError;
1331
- return { state: failState, signal: "$error", paused: false, cursor: null };
1387
+ return fail(storeError);
1332
1388
  }
1333
- if (loaded !== null && loaded.phase === "completed") {
1389
+ const decision = decideRun({ loaded, older, mode, requestId, sessionId });
1390
+ if (decision.action === "reject") return fail(decision.error);
1391
+ if (decision.action === "replay") {
1392
+ const { row } = decision;
1334
1393
  return {
1335
- state: loaded.finalState,
1336
- signal: loaded.signal ?? null,
1337
- cursor: null,
1338
- paused: false
1394
+ state: row.finalState,
1395
+ signal: row.signal ?? null,
1396
+ cursor: row.step ?? null,
1397
+ paused: row.phase === "paused"
1339
1398
  };
1340
1399
  }
1341
- const state = initializeState(loaded, initialStateArg, schema);
1400
+ const segmentRequestId = mode === "run" ? requestId : loaded?.requestId;
1401
+ const base = decision.action === "new-turn" && loaded !== null ? withoutSystemFields(loaded) : loaded;
1402
+ const input = decision.action === "continue" && !decision.mergeInput ? {} : initialStateArg;
1403
+ const state = initializeState(base, input, schema);
1342
1404
  const initialStateSnapshot = { ...state };
1343
1405
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
1344
1406
  const result = await runLoop(graph, state, ctx, schema, composedShouldStop, loaded?.step, {
1345
- ...options,
1407
+ ...loopOptions,
1346
1408
  runId,
1347
1409
  ...options?.parentRunId !== void 0 ? { parentRunId: options.parentRunId } : {},
1410
+ ...segmentRequestId !== void 0 ? { requestId: segmentRequestId } : {},
1348
1411
  ...runRefsOf(loaded)
1349
1412
  });
1350
1413
  if (leaseExpired) {
@@ -1370,7 +1433,8 @@ async function runWithSession(store, agentId, sessionId, runId, graph, initialSt
1370
1433
  step: result.cursor,
1371
1434
  ...result.signal !== null ? { signal: result.signal } : {},
1372
1435
  ...metadata !== void 0 ? { metadata } : {},
1373
- ...result.carrier !== void 0 ? { carrier: result.carrier } : {}
1436
+ ...result.carrier !== void 0 ? { carrier: result.carrier } : {},
1437
+ ...segmentRequestId !== void 0 ? { requestId: segmentRequestId } : {}
1374
1438
  };
1375
1439
  await store.save(agentId, sessionId, saved);
1376
1440
  } else {
@@ -1386,7 +1450,8 @@ async function runWithSession(store, agentId, sessionId, runId, graph, initialSt
1386
1450
  finalState: result.state,
1387
1451
  ...result.signal !== null ? { signal: result.signal } : {},
1388
1452
  ...metadata !== void 0 ? { metadata } : {},
1389
- ...result.carrier !== void 0 ? { carrier: result.carrier } : {}
1453
+ ...result.carrier !== void 0 ? { carrier: result.carrier } : {},
1454
+ ...segmentRequestId !== void 0 ? { requestId: segmentRequestId } : {}
1390
1455
  };
1391
1456
  await store.save(agentId, sessionId, saved);
1392
1457
  }
@@ -1510,7 +1575,8 @@ async function injectInterruptResponse(store, agentId, sessionId, interruptId, r
1510
1575
  ...loaded.signal !== void 0 ? { signal: loaded.signal } : {},
1511
1576
  ...loaded.metadata !== void 0 ? { metadata: loaded.metadata } : {},
1512
1577
  ...loaded.carrier !== void 0 ? { carrier: loaded.carrier } : {},
1513
- ...loaded.branchedFrom !== void 0 ? { branchedFrom: loaded.branchedFrom } : {}
1578
+ ...loaded.branchedFrom !== void 0 ? { branchedFrom: loaded.branchedFrom } : {},
1579
+ ...loaded.requestId !== void 0 ? { requestId: loaded.requestId } : {}
1514
1580
  };
1515
1581
  await store.save(agentId, sessionId, updated);
1516
1582
  }
@@ -1576,6 +1642,572 @@ function createRunHandle(sessionId, runId, execution, stopFlag, stepRef, resumeF
1576
1642
  return handle;
1577
1643
  }
1578
1644
 
1645
+ // src/agent/debug-session.ts
1646
+ var DebugCommandError = class extends Error {
1647
+ /**
1648
+ * - `busy`: another command is still running
1649
+ * - `not-held`: the focused frame is not held at a step stop (or, for `answer`, the session is not at
1650
+ * an interrupt stop; for `focus`, the frame is not held)
1651
+ * - `not-a-fork`: `stepInto` where the next step is not a fork node
1652
+ * - `reserved-key`: `setState` was given a framework key (`$…`)
1653
+ * - `ended`: the session is over
1654
+ */
1655
+ reason;
1656
+ /** The command that was refused. */
1657
+ command;
1658
+ constructor(reason, command, detail) {
1659
+ super(`${command}(): ${detail ?? describeReason(reason)}`);
1660
+ this.name = "DebugCommandError";
1661
+ this.reason = reason;
1662
+ this.command = command;
1663
+ }
1664
+ };
1665
+ function describeReason(reason) {
1666
+ switch (reason) {
1667
+ case "busy":
1668
+ return "another debug command is still running";
1669
+ case "not-held":
1670
+ return "the debug session is not stopped where this command applies";
1671
+ case "not-a-fork":
1672
+ return "the next step is not a fork";
1673
+ case "reserved-key":
1674
+ return "framework keys ($\u2026) cannot be set";
1675
+ case "ended":
1676
+ return "the debug session has ended";
1677
+ }
1678
+ }
1679
+ function cloneOrShare(value) {
1680
+ try {
1681
+ return { value: structuredClone(value), shared: false };
1682
+ } catch {
1683
+ return { value, shared: true };
1684
+ }
1685
+ }
1686
+ function snapshotOf(source) {
1687
+ const values = {};
1688
+ const shared = [];
1689
+ for (const [key, raw] of Object.entries(source)) {
1690
+ const copy = cloneOrShare(raw);
1691
+ values[key] = copy.value;
1692
+ if (copy.shared) shared.push(key);
1693
+ }
1694
+ return { values, shared };
1695
+ }
1696
+ function deferred() {
1697
+ let resolve;
1698
+ let reject;
1699
+ const promise = new Promise((res, rej) => {
1700
+ resolve = res;
1701
+ reject = rej;
1702
+ });
1703
+ promise.catch(() => {
1704
+ });
1705
+ return { promise, resolve, reject };
1706
+ }
1707
+ var keyOf = (path) => JSON.stringify(path);
1708
+ var samePath = (a, b) => a.length === b.length && a.every((s, i) => s === b[i]);
1709
+ var within = (path, root) => path.length >= root.length && root.every((s, i) => s === path[i]);
1710
+ function branchStart(parentForkMode) {
1711
+ return {
1712
+ mode: parentForkMode === "fork-entry" ? "fork-entry" : "continue",
1713
+ lastRelease: parentForkMode === "continue" || parentForkMode === void 0 ? "continue" : "hold"
1714
+ };
1715
+ }
1716
+ function createDebugSession(args) {
1717
+ const { store, signal, onEnd } = args;
1718
+ let status = "starting";
1719
+ let handle;
1720
+ let topMode = "start";
1721
+ let frames = /* @__PURE__ */ new Map();
1722
+ let seq = 0;
1723
+ let focus = [];
1724
+ let lastByFrame = /* @__PURE__ */ new Map();
1725
+ let topInterrupt;
1726
+ let interrupts = [];
1727
+ let pausedForks = /* @__PURE__ */ new Map();
1728
+ let paused;
1729
+ let stopRequested = false;
1730
+ let lease = {};
1731
+ let renewal;
1732
+ const breakpoints = /* @__PURE__ */ new Map();
1733
+ let command;
1734
+ let idle;
1735
+ const first = deferred();
1736
+ let current = first.promise;
1737
+ let started = false;
1738
+ const ended = deferred();
1739
+ const done = deferred();
1740
+ const anyHeld = () => [...frames.values()].some((f) => f.status === "held");
1741
+ const updateRenewal = () => {
1742
+ if (!anyHeld()) {
1743
+ if (renewal !== void 0) clearInterval(renewal);
1744
+ renewal = void 0;
1745
+ return;
1746
+ }
1747
+ if (renewal !== void 0) return;
1748
+ const { leaseRef, claimTtlMs } = lease;
1749
+ const extendClaim = store?.extendClaim;
1750
+ if (leaseRef === void 0 || claimTtlMs === void 0 || typeof extendClaim !== "function") return;
1751
+ const renew = () => {
1752
+ const held = leaseRef.current;
1753
+ if (held === null) return;
1754
+ extendClaim.call(store, held, { ttlMs: claimTtlMs }).then((renewed) => {
1755
+ leaseRef.current = renewed;
1756
+ }, () => {
1757
+ });
1758
+ };
1759
+ renew();
1760
+ renewal = setInterval(renew, claimTtlMs / 3);
1761
+ renewal.unref?.();
1762
+ };
1763
+ const frameOf = (path) => frames.get(keyOf(path));
1764
+ const addFrame = (path, init) => {
1765
+ const frame = { path, seq: seq++, patches: [], ...init };
1766
+ frames.set(keyOf(path), frame);
1767
+ return frame;
1768
+ };
1769
+ const treeOrder = (a, b) => {
1770
+ const n = Math.min(a.path.length, b.path.length);
1771
+ for (let i = 0; i < n; i++) {
1772
+ if (a.path[i] !== b.path[i]) {
1773
+ const sa = frameOf(a.path.slice(0, i + 1))?.seq ?? 0;
1774
+ const sb = frameOf(b.path.slice(0, i + 1))?.seq ?? 0;
1775
+ return sa - sb;
1776
+ }
1777
+ }
1778
+ return a.path.length - b.path.length;
1779
+ };
1780
+ const sortedFrames = () => [...frames.values()].sort(treeOrder);
1781
+ const dropDescendants = (root) => {
1782
+ for (const [key, frame] of frames) {
1783
+ if (frame.path.length > root.length && within(frame.path, root)) frames.delete(key);
1784
+ }
1785
+ };
1786
+ const seedPausedFork = (frame, forkName) => {
1787
+ const settled = pausedForks.get(keyOf(frame.path));
1788
+ if (settled?.fork === void 0 || settled.name !== forkName) return;
1789
+ pausedForks.delete(keyOf(frame.path));
1790
+ for (const branch of settled.fork.branches) {
1791
+ addFrame([...frame.path, branch.name], { status: branch.status === "done" ? "ended" : "running", ...branchStart(frame.forkMode) });
1792
+ }
1793
+ };
1794
+ const setCurrent = (stop) => {
1795
+ current = Promise.resolve(stop);
1796
+ if (!started) {
1797
+ started = true;
1798
+ first.resolve(stop);
1799
+ }
1800
+ };
1801
+ const deliver = (stop, frame) => {
1802
+ focus = frame;
1803
+ setCurrent(stop);
1804
+ const c = command;
1805
+ command = void 0;
1806
+ c?.result.resolve(stop);
1807
+ const w = idle;
1808
+ idle = void 0;
1809
+ w?.resolve(stop);
1810
+ };
1811
+ const offerStop = (stop) => {
1812
+ if (!started) return deliver(stop, stop.frame);
1813
+ if (command !== void 0) {
1814
+ const { target, orphan } = command;
1815
+ if (target === "any" || orphan !== void 0 || within(stop.frame, target)) deliver(stop, stop.frame);
1816
+ return;
1817
+ }
1818
+ if (idle !== void 0) deliver(stop, stop.frame);
1819
+ };
1820
+ const branchEndStop = (frame, endedFrame) => {
1821
+ const held = frame.stop;
1822
+ const stop = {
1823
+ kind: "step",
1824
+ frame: frame.path,
1825
+ reason: "branch-end",
1826
+ ended: endedFrame,
1827
+ runId: held.runId,
1828
+ next: held.next,
1829
+ ...held.last !== void 0 ? { last: held.last } : {},
1830
+ state: held.state
1831
+ };
1832
+ frame.stop = stop;
1833
+ return stop;
1834
+ };
1835
+ const afterFrameEnd = (endedFrame) => {
1836
+ setTimeout(() => {
1837
+ if (status !== "active") return;
1838
+ const heldFrame = sortedFrames().find((f) => f.status === "held");
1839
+ if (command !== void 0) {
1840
+ if (command.orphan !== endedFrame) return;
1841
+ if (heldFrame !== void 0) deliver(branchEndStop(heldFrame, endedFrame), heldFrame.path);
1842
+ return;
1843
+ }
1844
+ if (!samePath(focus, endedFrame.frame)) return;
1845
+ if (heldFrame !== void 0) {
1846
+ focus = heldFrame.path;
1847
+ setCurrent(branchEndStop(heldFrame, endedFrame));
1848
+ } else {
1849
+ idle ??= deferred();
1850
+ current = idle.promise;
1851
+ }
1852
+ }, 0);
1853
+ };
1854
+ const onFrameEnd = (path, signal2) => {
1855
+ const frame = frameOf(path);
1856
+ if (frame === void 0) return;
1857
+ frame.status = "ended";
1858
+ const parent = frameOf(path.slice(0, -1));
1859
+ if (parent !== void 0) parent.joinHold = frame.lastRelease === "hold";
1860
+ const outcome = signal2 === "$interrupt" ? "interrupt" : signal2 === "$error" ? "error" : signal2 === "$stopped" ? "stopped" : "done";
1861
+ const last = lastByFrame.get(keyOf(path));
1862
+ const endedFrame = { frame: path, outcome, ...last !== void 0 ? { last } : {} };
1863
+ updateRenewal();
1864
+ if (command !== void 0) {
1865
+ if (command.target !== "any" && samePath(command.target, path)) {
1866
+ command.orphan = endedFrame;
1867
+ afterFrameEnd(endedFrame);
1868
+ }
1869
+ return;
1870
+ }
1871
+ if (samePath(focus, path)) afterFrameEnd(endedFrame);
1872
+ };
1873
+ const onAbort = () => {
1874
+ void session.stop();
1875
+ };
1876
+ const cleanup = () => {
1877
+ for (const frame of frames.values()) if (frame.status === "held") frame.status = "ended";
1878
+ updateRenewal();
1879
+ signal?.removeEventListener("abort", onAbort);
1880
+ onEnd();
1881
+ };
1882
+ const topLast = () => lastByFrame.get(keyOf([]));
1883
+ const finish = (outcome) => {
1884
+ const last = topLast();
1885
+ const stop = { kind: "end", runId: handle.runId, ...last !== void 0 ? { last } : {}, outcome };
1886
+ status = "ended";
1887
+ cleanup();
1888
+ done.resolve(outcome);
1889
+ deliver(stop, []);
1890
+ ended.resolve(stop);
1891
+ };
1892
+ const fail = (error) => {
1893
+ status = "ended";
1894
+ cleanup();
1895
+ done.reject(error);
1896
+ const c = command;
1897
+ command = void 0;
1898
+ c?.result.reject(error);
1899
+ const w = idle;
1900
+ idle = void 0;
1901
+ w?.reject(error);
1902
+ if (!started) first.reject(error);
1903
+ else current = done.promise.then(() => {
1904
+ throw error;
1905
+ });
1906
+ current.catch(() => {
1907
+ });
1908
+ ended.reject(error);
1909
+ };
1910
+ const pendingInterrupts = () => {
1911
+ const pending = [];
1912
+ const walk = (path) => {
1913
+ const settled = lastByFrame.get(keyOf(path));
1914
+ if (settled?.fork === void 0) return;
1915
+ for (const branch of settled.fork.branches) {
1916
+ if (branch.status !== "paused") continue;
1917
+ const branchPath = [...path, branch.name];
1918
+ const raised = interrupts.find((i) => samePath(i.branchPath, branchPath));
1919
+ if (raised !== void 0) pending.push({ interruptId: raised.interruptId, prompt: raised.prompt, frame: branchPath });
1920
+ else walk(branchPath);
1921
+ }
1922
+ };
1923
+ if (topInterrupt !== void 0 && topLast()?.fork === void 0) {
1924
+ pending.push({ interruptId: topInterrupt.interruptId, prompt: topInterrupt.prompt, frame: [] });
1925
+ } else {
1926
+ walk([]);
1927
+ }
1928
+ return pending;
1929
+ };
1930
+ const attach = (h) => {
1931
+ h.then((outcome) => {
1932
+ const last = topLast();
1933
+ if (outcome.signal === "$interrupt" && !stopRequested && last !== void 0) {
1934
+ const pending = pendingInterrupts();
1935
+ const head = pending[0];
1936
+ if (head !== void 0) {
1937
+ status = "interrupt";
1938
+ paused = { outcome, pending };
1939
+ for (const frame of frames.values()) if (frame.status === "held") frame.status = "ended";
1940
+ updateRenewal();
1941
+ deliver({
1942
+ kind: "interrupt",
1943
+ frame: head.frame,
1944
+ runId: h.runId,
1945
+ last,
1946
+ interruptId: head.interruptId,
1947
+ prompt: head.prompt,
1948
+ pending,
1949
+ state: snapshotOf(outcome.state)
1950
+ }, head.frame);
1951
+ return;
1952
+ }
1953
+ }
1954
+ finish(outcome);
1955
+ }, fail);
1956
+ };
1957
+ const gate = async (next, state, info) => {
1958
+ const path = info.branchPath;
1959
+ let frame = frameOf(path);
1960
+ if (frame === void 0) {
1961
+ if (path.length === 0) {
1962
+ frame = addFrame(path, { status: "running", mode: topMode, lastRelease: "hold" });
1963
+ } else {
1964
+ frame = addFrame(path, { status: "running", ...branchStart(frameOf(path.slice(0, -1))?.forkMode) });
1965
+ }
1966
+ } else if (frame.status === "in-fork") {
1967
+ dropDescendants(path);
1968
+ frame.mode = frame.joinHold === false ? "continue" : "step";
1969
+ frame.joinHold = void 0;
1970
+ frame.forkMode = void 0;
1971
+ }
1972
+ if (info.signal?.aborted === true) return { patches: [], halt: true };
1973
+ let reason;
1974
+ let condition;
1975
+ if (frame.mode !== "continue") {
1976
+ reason = frame.mode;
1977
+ } else {
1978
+ const breakpoint = breakpoints.get(next);
1979
+ if (breakpoint !== void 0) {
1980
+ try {
1981
+ if (breakpoint.when === void 0 || breakpoint.when(state)) reason = "breakpoint";
1982
+ } catch (error) {
1983
+ reason = "breakpoint";
1984
+ condition = { error };
1985
+ }
1986
+ }
1987
+ }
1988
+ const enter = () => {
1989
+ frame.before = snapshotOf(state);
1990
+ if (info.fork) {
1991
+ frame.status = "in-fork";
1992
+ frame.forkMode ??= frame.mode === "continue" ? "continue" : "over";
1993
+ seedPausedFork(frame, next);
1994
+ } else {
1995
+ frame.status = "running";
1996
+ }
1997
+ };
1998
+ if (reason === void 0) {
1999
+ enter();
2000
+ return { patches: [], halt: false };
2001
+ }
2002
+ frame.status = "held";
2003
+ frame.next = next;
2004
+ frame.isFork = info.fork;
2005
+ frame.live = state;
2006
+ frame.forkMode = void 0;
2007
+ if (info.touchedKeys !== void 0) frame.touched = info.touchedKeys;
2008
+ const last = lastByFrame.get(keyOf(path));
2009
+ const stop = {
2010
+ kind: "step",
2011
+ frame: path,
2012
+ reason,
2013
+ runId: handle.runId,
2014
+ next,
2015
+ ...last !== void 0 ? { last } : {},
2016
+ state: snapshotOf(state),
2017
+ ...condition !== void 0 ? { conditionError: condition.error } : {}
2018
+ };
2019
+ frame.stop = stop;
2020
+ const released = new Promise((resolve) => {
2021
+ frame.release = resolve;
2022
+ });
2023
+ const onHalt = () => {
2024
+ frame.release?.({ patches: [], halt: true });
2025
+ };
2026
+ info.signal?.addEventListener("abort", onHalt);
2027
+ updateRenewal();
2028
+ offerStop(stop);
2029
+ const result = await released;
2030
+ info.signal?.removeEventListener("abort", onHalt);
2031
+ frame.release = void 0;
2032
+ frame.live = void 0;
2033
+ if (!result.halt) enter();
2034
+ updateRenewal();
2035
+ return result;
2036
+ };
2037
+ const release = (frame, how, halt = false) => {
2038
+ if (!halt) {
2039
+ frame.mode = how === "next" ? "step" : "continue";
2040
+ frame.lastRelease = how === "next" || how === "stepInto" ? "hold" : "continue";
2041
+ if (frame.isFork === true) frame.forkMode = how === "stepInto" ? "fork-entry" : how === "next" ? "over" : "continue";
2042
+ frame.status = frame.isFork === true ? "in-fork" : "running";
2043
+ }
2044
+ const patches = frame.patches;
2045
+ frame.patches = [];
2046
+ const r = frame.release;
2047
+ frame.release = void 0;
2048
+ r?.({ patches, halt });
2049
+ };
2050
+ const observer = {
2051
+ onStepSettled: (ctx, e) => {
2052
+ const path = ctx.branchPath ?? [];
2053
+ if (path.length === 0) {
2054
+ topInterrupt = e.interrupt !== void 0 ? { interruptId: e.interrupt.interruptId, prompt: e.interrupt.prompt, branchPath: [] } : void 0;
2055
+ }
2056
+ lastByFrame.set(keyOf(path), {
2057
+ name: ctx.stepName,
2058
+ outcome: e.outcome,
2059
+ ...e.update !== void 0 ? { update: snapshotOf(e.update) } : {},
2060
+ ...e.signal !== void 0 ? { signal: e.signal } : {},
2061
+ next: e.next,
2062
+ ...e.error !== void 0 ? { error: e.error } : {},
2063
+ before: frameOf(path)?.before ?? { values: {}, shared: [] },
2064
+ durationMs: e.durationMs,
2065
+ ...e.fork !== void 0 ? { fork: e.fork } : {}
2066
+ });
2067
+ },
2068
+ onInterrupt: (ctx, e) => {
2069
+ interrupts.push({ interruptId: e.interruptId, prompt: e.prompt, branchPath: ctx.branchPath ?? [] });
2070
+ },
2071
+ onRunEnd: (ctx, e) => {
2072
+ if (ctx.branchPath !== void 0) onFrameEnd(ctx.branchPath, e.signal);
2073
+ }
2074
+ };
2075
+ const refuse = (name) => {
2076
+ if (status === "ended") return new DebugCommandError("ended", name);
2077
+ if (status === "starting" || !started || command !== void 0) return new DebugCommandError("busy", name);
2078
+ if (status === "interrupt" && name !== "answer") return new DebugCommandError("not-held", name);
2079
+ if (status === "active" && name === "answer") return new DebugCommandError("not-held", name);
2080
+ return void 0;
2081
+ };
2082
+ const issue = (target) => {
2083
+ const result = deferred();
2084
+ command = { target, result };
2085
+ return result.promise;
2086
+ };
2087
+ const drive = (how) => {
2088
+ const refused = refuse(how);
2089
+ if (refused !== void 0) return Promise.reject(refused);
2090
+ const frame = frameOf(focus);
2091
+ if (frame === void 0 || frame.status !== "held") return Promise.reject(new DebugCommandError("not-held", how));
2092
+ if (how === "stepInto" && frame.isFork !== true) return Promise.reject(new DebugCommandError("not-a-fork", how));
2093
+ const p = issue(frame.path);
2094
+ release(frame, how);
2095
+ updateRenewal();
2096
+ return p;
2097
+ };
2098
+ const session = {
2099
+ get sessionId() {
2100
+ return handle.sessionId;
2101
+ },
2102
+ get runId() {
2103
+ return handle.runId;
2104
+ },
2105
+ get current() {
2106
+ return current;
2107
+ },
2108
+ get done() {
2109
+ return done.promise;
2110
+ },
2111
+ get breakpoints() {
2112
+ return [...breakpoints.values()];
2113
+ },
2114
+ get frames() {
2115
+ return sortedFrames().map((f) => ({ frame: f.path, status: f.status, ...f.status === "held" && f.next !== void 0 ? { next: f.next } : {} }));
2116
+ },
2117
+ focus: (path) => {
2118
+ const frame = frameOf(path);
2119
+ if (status !== "active" || frame === void 0 || frame.status !== "held" || frame.stop === void 0) {
2120
+ throw new DebugCommandError("not-held", "focus");
2121
+ }
2122
+ focus = frame.path;
2123
+ setCurrent(frame.stop);
2124
+ return frame.stop;
2125
+ },
2126
+ next: () => drive("next"),
2127
+ continue: () => drive("continue"),
2128
+ stepInto: () => drive("stepInto"),
2129
+ continueAll: () => {
2130
+ const refused = refuse("continueAll");
2131
+ if (refused !== void 0) return Promise.reject(refused);
2132
+ const p = issue("any");
2133
+ for (const frame of sortedFrames()) if (frame.status === "held") release(frame, "continueAll");
2134
+ updateRenewal();
2135
+ return p;
2136
+ },
2137
+ answer: (response, id) => {
2138
+ const at = paused;
2139
+ const refused = refuse("answer") ?? (at === void 0 ? new DebugCommandError("not-held", "answer") : void 0);
2140
+ if (refused !== void 0 || at === void 0) return Promise.reject(refused);
2141
+ const target = id ?? at.pending[0].interruptId;
2142
+ if (!at.pending.some((p2) => p2.interruptId === target)) return Promise.reject(new NoInterruptError());
2143
+ let resumed;
2144
+ try {
2145
+ resumed = handle.resume(response, target);
2146
+ } catch (error) {
2147
+ return Promise.reject(error);
2148
+ }
2149
+ pausedForks = new Map([...lastByFrame].filter(([, s]) => s.fork !== void 0 && (s.outcome === "interrupt" || s.outcome === "stopped")));
2150
+ handle = resumed;
2151
+ paused = void 0;
2152
+ status = "active";
2153
+ topMode = "resume";
2154
+ frames = /* @__PURE__ */ new Map();
2155
+ focus = [];
2156
+ lastByFrame = /* @__PURE__ */ new Map();
2157
+ topInterrupt = void 0;
2158
+ interrupts = [];
2159
+ lease = {};
2160
+ const p = issue([]);
2161
+ attach(handle);
2162
+ return p;
2163
+ },
2164
+ setState: (patch) => {
2165
+ if (status === "ended") throw new DebugCommandError("ended", "setState");
2166
+ const frame = frameOf(focus);
2167
+ if (status !== "active" || frame === void 0 || frame.status !== "held" || frame.live === void 0) {
2168
+ throw new DebugCommandError("not-held", "setState");
2169
+ }
2170
+ const reserved = Object.keys(patch).find((k) => k.startsWith("$"));
2171
+ if (reserved !== void 0) throw new DebugCommandError("reserved-key", "setState", `framework key "${reserved}" cannot be set`);
2172
+ const reported = {};
2173
+ for (const [key, value] of Object.entries(patch)) {
2174
+ frame.live[key] = cloneOrShare(value).value;
2175
+ reported[key] = cloneOrShare(value).value;
2176
+ frame.touched?.add(key);
2177
+ }
2178
+ frame.patches.push(reported);
2179
+ return snapshotOf(frame.live);
2180
+ },
2181
+ setBreakpoint: (step, when) => {
2182
+ breakpoints.set(step, { step, ...when !== void 0 ? { when } : {} });
2183
+ },
2184
+ clearBreakpoint: (step) => {
2185
+ breakpoints.delete(step);
2186
+ },
2187
+ stop: () => {
2188
+ if (status === "ended") return ended.promise;
2189
+ stopRequested = true;
2190
+ if (status === "interrupt" && paused !== void 0) {
2191
+ finish(paused.outcome);
2192
+ return ended.promise;
2193
+ }
2194
+ handle.stop();
2195
+ for (const frame of frames.values()) if (frame.status === "held") release(frame, "continueAll", true);
2196
+ return ended.promise;
2197
+ }
2198
+ };
2199
+ handle = args.start({ gate, observer, onLease: (l) => {
2200
+ lease = l;
2201
+ } });
2202
+ status = "active";
2203
+ attach(handle);
2204
+ if (signal !== void 0) {
2205
+ signal.addEventListener("abort", onAbort);
2206
+ if (signal.aborted) onAbort();
2207
+ }
2208
+ return session;
2209
+ }
2210
+
1579
2211
  // src/agent/create-agent.ts
1580
2212
  import { randomUUID } from "crypto";
1581
2213
 
@@ -1697,6 +2329,36 @@ var InvalidClaimOptionsError = class extends Error {
1697
2329
  this.ttlMs = ttlMs;
1698
2330
  }
1699
2331
  };
2332
+ var InvalidAgentOptionError = class extends Error {
2333
+ /** The option that was rejected. */
2334
+ key;
2335
+ /** The rejected value, as given. */
2336
+ value;
2337
+ constructor(key, value) {
2338
+ super(`${key} must be ${key === "observer" ? "an object" : "a function"} \u2014 got ${describeValue(value)} in createAgent options`);
2339
+ this.name = "InvalidAgentOptionError";
2340
+ this.key = key;
2341
+ this.value = value;
2342
+ }
2343
+ };
2344
+ var InvalidRunResourceError = class extends Error {
2345
+ /** The resource key that was rejected. */
2346
+ key;
2347
+ /** The rejected value, as given. */
2348
+ value;
2349
+ constructor(key, value) {
2350
+ super(`${key} must be a non-empty string \u2014 got ${describeValue(value)} in agent.run() resources`);
2351
+ this.name = "InvalidRunResourceError";
2352
+ this.key = key;
2353
+ this.value = value;
2354
+ }
2355
+ };
2356
+ function readIdResource(resources, key) {
2357
+ const value = resources[key];
2358
+ if (value === void 0) return void 0;
2359
+ if (typeof value !== "string" || value.length === 0) throw new InvalidRunResourceError(key, value);
2360
+ return value;
2361
+ }
1700
2362
  var _agentInternals = /* @__PURE__ */ Symbol("_agentInternals");
1701
2363
  function createAgent(id, h, slots, agentOptions) {
1702
2364
  const internals = getInternals(h);
@@ -1734,6 +2396,23 @@ function createAgent(id, h, slots, agentOptions) {
1734
2396
  agentClaimOptions = { ttlMs };
1735
2397
  }
1736
2398
  const fallbackClaimOptions = () => agentClaimOptions ?? { ttlMs: defaultClaimTtlMs() };
2399
+ const agentObserver = agentOptions?.observer;
2400
+ if (agentObserver !== void 0 && (agentObserver === null || typeof agentObserver !== "object" || Array.isArray(agentObserver))) {
2401
+ throw new InvalidAgentOptionError("observer", agentObserver);
2402
+ }
2403
+ const agentErrorSink = agentOptions?.onObserverError;
2404
+ if (agentErrorSink !== void 0 && typeof agentErrorSink !== "function") {
2405
+ throw new InvalidAgentOptionError("onObserverError", agentErrorSink);
2406
+ }
2407
+ const resolveObserver = (resources) => {
2408
+ const runObserver = extractRunObserver(resources);
2409
+ const onObserverError = extractRunErrorSink(resources) ?? agentErrorSink;
2410
+ const observer = agentObserver === void 0 ? runObserver : runObserver === void 0 || runObserver === agentObserver ? agentObserver : composeObservers([agentObserver, runObserver], onObserverError);
2411
+ return {
2412
+ ...observer !== void 0 ? { observer } : {},
2413
+ ...onObserverError !== void 0 ? { onObserverError } : {}
2414
+ };
2415
+ };
1737
2416
  const resolvedProviders = /* @__PURE__ */ new Map();
1738
2417
  const storeEntries = [];
1739
2418
  const runtimeKeys = /* @__PURE__ */ new Set();
@@ -1783,8 +2462,9 @@ function createAgent(id, h, slots, agentOptions) {
1783
2462
  runtimeKeys,
1784
2463
  requiredKeys
1785
2464
  };
1786
- const reservedRunKeys = /* @__PURE__ */ new Set(["sessionId", "signal", "events", "listeners", "observer", "onObserverError", "claimOptions", "parentRunId"]);
2465
+ const reservedRunKeys = /* @__PURE__ */ new Set(["sessionId", "requestId", "signal", "events", "listeners", "observer", "onObserverError", "claimOptions", "parentRunId"]);
1787
2466
  const inFlightSessions = /* @__PURE__ */ new Set();
2467
+ const debugSessions = /* @__PURE__ */ new Set();
1788
2468
  const interruptPendingSessions = /* @__PURE__ */ new Set();
1789
2469
  const resumeFailState = () => initializeState(null, {}, agentInternals.stateSchema);
1790
2470
  const makeAgentResumeHandle = (resp, sId, iId, resumeResources) => {
@@ -1842,6 +2522,7 @@ function createAgent(id, h, slots, agentOptions) {
1842
2522
  agentInternals.stateSchema,
1843
2523
  agentCtx,
1844
2524
  {
2525
+ mode: "resume",
1845
2526
  shouldStop: () => flag.stopped,
1846
2527
  onBeforeStep: (n) => {
1847
2528
  ref.current = n;
@@ -1855,7 +2536,6 @@ function createAgent(id, h, slots, agentOptions) {
1855
2536
  ...parentRunId !== void 0 ? { parentRunId } : {}
1856
2537
  }
1857
2538
  );
1858
- if (r.signal === "$interrupt") interruptPendingSessions.add(sId);
1859
2539
  return { state: r.state, signal: r.signal };
1860
2540
  }
1861
2541
  });
@@ -1874,258 +2554,269 @@ function createAgent(id, h, slots, agentOptions) {
1874
2554
  };
1875
2555
  return createRunHandle(sId, rId, exec, flag, ref, resumeFn);
1876
2556
  };
1877
- const agent = {
1878
- id,
1879
- run: (initialState, resources) => {
1880
- for (const key of Object.keys(resources)) {
1881
- if (reservedRunKeys.has(key)) continue;
1882
- if (!agentInternals.runtimeKeys.has(key) && !agentInternals.requiredKeys.has(key)) {
1883
- throw new UnknownRunSlotError(key);
1884
- }
2557
+ const startRun = (initialState, resources, debug) => {
2558
+ for (const key of Object.keys(resources)) {
2559
+ if (reservedRunKeys.has(key)) continue;
2560
+ if (!agentInternals.runtimeKeys.has(key) && !agentInternals.requiredKeys.has(key)) {
2561
+ throw new UnknownRunSlotError(key);
1885
2562
  }
1886
- for (const key of Object.keys(resources)) {
1887
- if (reservedRunKeys.has(key)) continue;
1888
- if (agentInternals.requiredKeys.has(key)) {
1889
- throw new RequiredSlotInRunError(key);
1890
- }
2563
+ }
2564
+ for (const key of Object.keys(resources)) {
2565
+ if (reservedRunKeys.has(key)) continue;
2566
+ if (agentInternals.requiredKeys.has(key)) {
2567
+ throw new RequiredSlotInRunError(key);
1891
2568
  }
1892
- for (const key of agentInternals.runtimeKeys) {
1893
- if (!(key in resources)) {
1894
- throw new MissingRuntimeSlotError(key);
1895
- }
2569
+ }
2570
+ for (const key of agentInternals.runtimeKeys) {
2571
+ if (!(key in resources)) {
2572
+ throw new MissingRuntimeSlotError(key);
1896
2573
  }
1897
- const sessionId = typeof resources["sessionId"] === "string" ? resources["sessionId"] : randomUUID();
1898
- const runId = randomUUID();
1899
- if (inFlightSessions.has(sessionId)) throw new SessionInFlightError(sessionId);
1900
- if (interruptPendingSessions.has(sessionId)) throw new SessionPendingInterruptError(sessionId);
1901
- inFlightSessions.add(sessionId);
1902
- let _stopped = false;
1903
- const abortController = new AbortController();
1904
- const stopFlag = {
1905
- get stopped() {
1906
- return _stopped;
1907
- },
1908
- set stopped(v) {
1909
- _stopped = v;
1910
- if (v) abortController.abort();
1911
- }
1912
- };
1913
- const rawSignal = resources["signal"];
1914
- let ctxSignal = abortController.signal;
1915
- if (rawSignal instanceof AbortSignal) {
1916
- ctxSignal = rawSignal;
1917
- rawSignal.addEventListener("abort", () => {
1918
- stopFlag.stopped = true;
1919
- abortController.abort();
1920
- }, { once: true });
1921
- if (rawSignal.aborted) stopFlag.stopped = true;
2574
+ }
2575
+ const sessionId = readIdResource(resources, "sessionId") ?? randomUUID();
2576
+ const requestId = readIdResource(resources, "requestId");
2577
+ const runId = randomUUID();
2578
+ if (debugSessions.has(sessionId)) throw new SessionInDebugError(sessionId);
2579
+ if (inFlightSessions.has(sessionId)) throw new SessionInFlightError(sessionId);
2580
+ if (capturedStore === void 0 && interruptPendingSessions.has(sessionId)) throw new SessionPendingInterruptError(sessionId);
2581
+ inFlightSessions.add(sessionId);
2582
+ let _stopped = false;
2583
+ const abortController = new AbortController();
2584
+ const stopFlag = {
2585
+ get stopped() {
2586
+ return _stopped;
2587
+ },
2588
+ set stopped(v) {
2589
+ _stopped = v;
2590
+ if (v) abortController.abort();
1922
2591
  }
1923
- const stepRef = { current: null };
1924
- const runtimeSlots = {};
1925
- for (const key of agentInternals.runtimeKeys) {
1926
- if (reservedRunKeys.has(key)) continue;
1927
- runtimeSlots[key] = resources[key];
2592
+ };
2593
+ const rawSignal = debug === void 0 ? resources["signal"] : void 0;
2594
+ let ctxSignal = abortController.signal;
2595
+ if (rawSignal instanceof AbortSignal) {
2596
+ ctxSignal = rawSignal;
2597
+ rawSignal.addEventListener("abort", () => {
2598
+ stopFlag.stopped = true;
2599
+ abortController.abort();
2600
+ }, { once: true });
2601
+ if (rawSignal.aborted) stopFlag.stopped = true;
2602
+ }
2603
+ const stepRef = { current: null };
2604
+ const runtimeSlots = {};
2605
+ for (const key of agentInternals.runtimeKeys) {
2606
+ if (reservedRunKeys.has(key)) continue;
2607
+ runtimeSlots[key] = resources[key];
2608
+ }
2609
+ const ctx = {
2610
+ ...Object.fromEntries(agentInternals.resolvedProviders),
2611
+ ...runtimeSlots,
2612
+ agentId: id,
2613
+ sessionId,
2614
+ runId,
2615
+ signal: ctxSignal,
2616
+ ...agentOptions?.instanceId !== void 0 ? { instanceId: agentOptions.instanceId } : {}
2617
+ };
2618
+ const events = extractRunEvents(resources);
2619
+ const listeners = extractRunListeners(resources);
2620
+ const resolved = resolveObserver(resources);
2621
+ const onObserverError = resolved.onObserverError;
2622
+ const observer = debug === void 0 ? resolved.observer : resolved.observer === void 0 ? debug.observer : composeObservers([resolved.observer, debug.observer], onObserverError);
2623
+ const telemetrySlots = buildTelemetrySlots([
2624
+ ...agentInternals.resolvedProviders.entries(),
2625
+ ...Object.entries(runtimeSlots)
2626
+ ]);
2627
+ const parentRunId = typeof resources["parentRunId"] === "string" ? resources["parentRunId"] : void 0;
2628
+ const claimOptions = parseClaimOptions(resources["claimOptions"], fallbackClaimOptions());
2629
+ const options = {
2630
+ mode: "run",
2631
+ ...requestId !== void 0 ? { requestId } : {},
2632
+ shouldStop: () => stopFlag.stopped,
2633
+ onBeforeStep: (name, state) => {
2634
+ stepRef.current = name;
2635
+ events.onBeforeStep?.(name, state);
2636
+ },
2637
+ // spread optional event callbacks only when defined (exactOptionalPropertyTypes requires omission, not undefined)
2638
+ ...events.onStoreError !== void 0 ? { onStoreError: events.onStoreError } : {},
2639
+ ...events.onAfterStep !== void 0 ? { onAfterStep: events.onAfterStep } : {},
2640
+ ...events.onError !== void 0 ? { onError: events.onError } : {},
2641
+ ...events.onComplete !== void 0 ? { onComplete: events.onComplete } : {},
2642
+ ...events.onInterrupt !== void 0 ? { onInterrupt: events.onInterrupt } : {},
2643
+ ...Object.keys(listeners).length > 0 ? { listeners } : {},
2644
+ ...observer !== void 0 ? { observer } : {},
2645
+ ...onObserverError !== void 0 ? { onObserverError } : {},
2646
+ ...agentOptions?.instanceId !== void 0 ? { instanceId: agentOptions.instanceId } : {},
2647
+ ...parentRunId !== void 0 ? { parentRunId } : {},
2648
+ ...telemetrySlots.length > 0 ? { telemetrySlots } : {},
2649
+ ...debug !== void 0 ? { debugGate: debug.gate } : {}
2650
+ };
2651
+ let lastResult = null;
2652
+ const execution = (async () => {
2653
+ await Promise.resolve();
2654
+ try {
2655
+ return await runClaimed({
2656
+ store: capturedStore,
2657
+ agentId: id,
2658
+ sessionId,
2659
+ claimOptions,
2660
+ ...events.onStoreError !== void 0 ? { onStoreError: events.onStoreError } : {},
2661
+ failState: () => initializeState(null, initialState, agentInternals.stateSchema),
2662
+ body: async (lease) => {
2663
+ debug?.onLease(lease);
2664
+ const r = await runWithSession(
2665
+ capturedStore,
2666
+ id,
2667
+ sessionId,
2668
+ runId,
2669
+ agentInternals.loopDef,
2670
+ initialState,
2671
+ agentInternals.stateSchema,
2672
+ ctx,
2673
+ { ...options, ...lease }
2674
+ );
2675
+ if (capturedStore === void 0 && r.signal === "$interrupt") interruptPendingSessions.add(sessionId);
2676
+ lastResult = { state: r.state, cursor: r.cursor, runId, ...r.carrier !== void 0 ? { carrier: r.carrier } : {} };
2677
+ return { state: r.state, signal: r.signal };
2678
+ }
2679
+ });
2680
+ } finally {
2681
+ inFlightSessions.delete(sessionId);
1928
2682
  }
1929
- const ctx = {
1930
- ...Object.fromEntries(agentInternals.resolvedProviders),
1931
- ...runtimeSlots,
1932
- agentId: id,
1933
- sessionId,
1934
- runId,
1935
- signal: ctxSignal,
1936
- ...agentOptions?.instanceId !== void 0 ? { instanceId: agentOptions.instanceId } : {}
1937
- };
1938
- const events = extractRunEvents(resources);
1939
- const listeners = extractRunListeners(resources);
1940
- const observer = extractRunObserver(resources);
1941
- const onObserverError = extractRunErrorSink(resources);
1942
- const telemetrySlots = buildTelemetrySlots([
1943
- ...agentInternals.resolvedProviders.entries(),
1944
- ...Object.entries(runtimeSlots)
1945
- ]);
1946
- const parentRunId = typeof resources["parentRunId"] === "string" ? resources["parentRunId"] : void 0;
1947
- const claimOptions = parseClaimOptions(resources["claimOptions"], fallbackClaimOptions());
1948
- const options = {
1949
- shouldStop: () => stopFlag.stopped,
1950
- onBeforeStep: (name, state) => {
1951
- stepRef.current = name;
1952
- events.onBeforeStep?.(name, state);
1953
- },
1954
- // spread optional event callbacks only when defined (exactOptionalPropertyTypes requires omission, not undefined)
1955
- ...events.onStoreError !== void 0 ? { onStoreError: events.onStoreError } : {},
1956
- ...events.onAfterStep !== void 0 ? { onAfterStep: events.onAfterStep } : {},
1957
- ...events.onError !== void 0 ? { onError: events.onError } : {},
1958
- ...events.onComplete !== void 0 ? { onComplete: events.onComplete } : {},
1959
- ...events.onInterrupt !== void 0 ? { onInterrupt: events.onInterrupt } : {},
1960
- ...Object.keys(listeners).length > 0 ? { listeners } : {},
1961
- ...observer !== void 0 ? { observer } : {},
1962
- ...onObserverError !== void 0 ? { onObserverError } : {},
1963
- ...agentOptions?.instanceId !== void 0 ? { instanceId: agentOptions.instanceId } : {},
1964
- ...parentRunId !== void 0 ? { parentRunId } : {},
1965
- ...telemetrySlots.length > 0 ? { telemetrySlots } : {}
1966
- };
1967
- let lastResult = null;
1968
- const execution = (async () => {
1969
- await Promise.resolve();
1970
- try {
1971
- return await runClaimed({
1972
- store: capturedStore,
1973
- agentId: id,
1974
- sessionId,
1975
- claimOptions,
1976
- ...events.onStoreError !== void 0 ? { onStoreError: events.onStoreError } : {},
1977
- failState: () => initializeState(null, initialState, agentInternals.stateSchema),
1978
- body: async (lease) => {
1979
- const r = await runWithSession(
1980
- capturedStore,
1981
- id,
2683
+ })();
2684
+ const buildResumeFn = () => {
2685
+ return (response, interruptId) => {
2686
+ if (inFlightSessions.has(sessionId)) throw new SessionInFlightError(sessionId);
2687
+ interruptPendingSessions.delete(sessionId);
2688
+ inFlightSessions.add(sessionId);
2689
+ let _resumeStopped = false;
2690
+ const resumeAbortController = new AbortController();
2691
+ const resumeStopFlag = {
2692
+ get stopped() {
2693
+ return _resumeStopped;
2694
+ },
2695
+ set stopped(v) {
2696
+ _resumeStopped = v;
2697
+ if (v) resumeAbortController.abort();
2698
+ }
2699
+ };
2700
+ const resumeStepRef = { current: null };
2701
+ const resumeRunId = randomUUID();
2702
+ const resumeExecution = (async () => {
2703
+ await Promise.resolve();
2704
+ try {
2705
+ const resumeCtx = {
2706
+ ...ctx,
2707
+ runId: resumeRunId,
2708
+ signal: resumeAbortController.signal
2709
+ };
2710
+ if (capturedStore !== void 0) {
2711
+ const store = capturedStore;
2712
+ const onStoreError = events.onStoreError;
2713
+ return await runClaimed({
2714
+ store,
2715
+ agentId: id,
1982
2716
  sessionId,
1983
- runId,
1984
- agentInternals.loopDef,
1985
- initialState,
1986
- agentInternals.stateSchema,
1987
- ctx,
1988
- { ...options, ...lease }
1989
- );
1990
- if (r.signal === "$interrupt") interruptPendingSessions.add(sessionId);
1991
- lastResult = { state: r.state, cursor: r.cursor, runId, ...r.carrier !== void 0 ? { carrier: r.carrier } : {} };
1992
- return { state: r.state, signal: r.signal };
1993
- }
1994
- });
1995
- } finally {
1996
- inFlightSessions.delete(sessionId);
1997
- }
1998
- })();
1999
- const buildResumeFn = () => {
2000
- return (response, interruptId) => {
2001
- if (inFlightSessions.has(sessionId)) throw new SessionInFlightError(sessionId);
2002
- interruptPendingSessions.delete(sessionId);
2003
- inFlightSessions.add(sessionId);
2004
- let _resumeStopped = false;
2005
- const resumeAbortController = new AbortController();
2006
- const resumeStopFlag = {
2007
- get stopped() {
2008
- return _resumeStopped;
2009
- },
2010
- set stopped(v) {
2011
- _resumeStopped = v;
2012
- if (v) resumeAbortController.abort();
2013
- }
2014
- };
2015
- const resumeStepRef = { current: null };
2016
- const resumeRunId = randomUUID();
2017
- const resumeExecution = (async () => {
2018
- await Promise.resolve();
2019
- try {
2020
- const resumeCtx = {
2021
- ...ctx,
2022
- runId: resumeRunId,
2023
- signal: resumeAbortController.signal
2024
- };
2025
- if (capturedStore !== void 0) {
2026
- const store = capturedStore;
2027
- const onStoreError = events.onStoreError;
2028
- return await runClaimed({
2717
+ claimOptions,
2718
+ ...onStoreError !== void 0 ? { onStoreError } : {},
2719
+ failState: resumeFailState,
2720
+ body: (lease) => injectThenRun({
2029
2721
  store,
2030
2722
  agentId: id,
2031
2723
  sessionId,
2032
- claimOptions,
2724
+ interruptId,
2725
+ response,
2033
2726
  ...onStoreError !== void 0 ? { onStoreError } : {},
2034
2727
  failState: resumeFailState,
2035
- body: (lease) => injectThenRun({
2036
- store,
2037
- agentId: id,
2038
- sessionId,
2039
- interruptId,
2040
- response,
2041
- ...onStoreError !== void 0 ? { onStoreError } : {},
2042
- failState: resumeFailState,
2043
- run: async () => {
2044
- const r2 = await runWithSession(
2045
- store,
2046
- id,
2047
- sessionId,
2048
- resumeRunId,
2049
- agentInternals.loopDef,
2050
- {},
2051
- agentInternals.stateSchema,
2052
- resumeCtx,
2053
- {
2054
- shouldStop: () => resumeStopFlag.stopped,
2055
- onBeforeStep: (n, s) => {
2056
- resumeStepRef.current = n;
2057
- events.onBeforeStep?.(n, s);
2058
- },
2059
- // spread optional event callbacks only when defined (exactOptionalPropertyTypes requires omission, not undefined)
2060
- ...events.onStoreError !== void 0 ? { onStoreError: events.onStoreError } : {},
2061
- ...events.onAfterStep !== void 0 ? { onAfterStep: events.onAfterStep } : {},
2062
- ...events.onError !== void 0 ? { onError: events.onError } : {},
2063
- ...events.onComplete !== void 0 ? { onComplete: events.onComplete } : {},
2064
- ...events.onInterrupt !== void 0 ? { onInterrupt: events.onInterrupt } : {},
2065
- ...Object.keys(listeners).length > 0 ? { listeners } : {},
2066
- ...observer !== void 0 ? { observer } : {},
2067
- ...onObserverError !== void 0 ? { onObserverError } : {},
2068
- ...telemetrySlots.length > 0 ? { telemetrySlots } : {},
2069
- ...agentOptions?.instanceId !== void 0 ? { instanceId: agentOptions.instanceId } : {},
2070
- ...lease
2071
- }
2072
- );
2073
- if (r2.signal === "$interrupt") interruptPendingSessions.add(sessionId);
2074
- lastResult = { state: r2.state, cursor: r2.cursor, runId: resumeRunId, ...r2.carrier !== void 0 ? { carrier: r2.carrier } : {} };
2075
- return { state: r2.state, signal: r2.signal };
2076
- }
2077
- })
2078
- });
2079
- }
2080
- const prev = lastResult;
2081
- if (prev === null) throw new NoInterruptError();
2082
- const injected = injectInterruptResponseIntoState(prev.cursor ?? void 0, prev.state, interruptId, response);
2083
- if (!injected.found) throw new NoInterruptError();
2084
- const state = injected.state;
2085
- const cursor = injected.cursor;
2086
- const r = await runLoop(
2087
- agentInternals.loopDef,
2088
- state,
2089
- resumeCtx,
2090
- agentInternals.stateSchema,
2091
- () => resumeStopFlag.stopped,
2092
- cursor,
2093
- {
2094
- runId: resumeRunId,
2095
- resumedFrom: { runId: prev.runId, ...prev.carrier !== void 0 ? { carrier: prev.carrier } : {} },
2096
- onBeforeStep: (n, s) => {
2097
- resumeStepRef.current = n;
2098
- events.onBeforeStep?.(n, s);
2099
- },
2100
- ...events.onAfterStep !== void 0 ? { onAfterStep: events.onAfterStep } : {},
2101
- ...events.onError !== void 0 ? { onError: events.onError } : {},
2102
- ...events.onComplete !== void 0 ? { onComplete: events.onComplete } : {},
2103
- ...events.onInterrupt !== void 0 ? { onInterrupt: events.onInterrupt } : {},
2104
- ...Object.keys(listeners).length > 0 ? { listeners } : {},
2105
- ...observer !== void 0 ? { observer } : {},
2106
- ...onObserverError !== void 0 ? { onObserverError } : {},
2107
- ...telemetrySlots.length > 0 ? { telemetrySlots } : {}
2108
- }
2109
- );
2110
- if (r.signal === "$interrupt") interruptPendingSessions.add(sessionId);
2111
- lastResult = { state: r.state, cursor: r.cursor, runId: resumeRunId, ...r.carrier !== void 0 ? { carrier: r.carrier } : {} };
2112
- return { state: r.state, signal: r.signal };
2113
- } finally {
2114
- inFlightSessions.delete(sessionId);
2728
+ run: async () => {
2729
+ debug?.onLease(lease);
2730
+ const r2 = await runWithSession(
2731
+ store,
2732
+ id,
2733
+ sessionId,
2734
+ resumeRunId,
2735
+ agentInternals.loopDef,
2736
+ {},
2737
+ agentInternals.stateSchema,
2738
+ resumeCtx,
2739
+ {
2740
+ mode: "resume",
2741
+ shouldStop: () => resumeStopFlag.stopped,
2742
+ onBeforeStep: (n, s) => {
2743
+ resumeStepRef.current = n;
2744
+ events.onBeforeStep?.(n, s);
2745
+ },
2746
+ // spread optional event callbacks only when defined (exactOptionalPropertyTypes requires omission, not undefined)
2747
+ ...events.onStoreError !== void 0 ? { onStoreError: events.onStoreError } : {},
2748
+ ...events.onAfterStep !== void 0 ? { onAfterStep: events.onAfterStep } : {},
2749
+ ...events.onError !== void 0 ? { onError: events.onError } : {},
2750
+ ...events.onComplete !== void 0 ? { onComplete: events.onComplete } : {},
2751
+ ...events.onInterrupt !== void 0 ? { onInterrupt: events.onInterrupt } : {},
2752
+ ...Object.keys(listeners).length > 0 ? { listeners } : {},
2753
+ ...observer !== void 0 ? { observer } : {},
2754
+ ...onObserverError !== void 0 ? { onObserverError } : {},
2755
+ ...telemetrySlots.length > 0 ? { telemetrySlots } : {},
2756
+ ...agentOptions?.instanceId !== void 0 ? { instanceId: agentOptions.instanceId } : {},
2757
+ ...debug !== void 0 ? { debugGate: debug.gate } : {},
2758
+ ...lease
2759
+ }
2760
+ );
2761
+ lastResult = { state: r2.state, cursor: r2.cursor, runId: resumeRunId, ...r2.carrier !== void 0 ? { carrier: r2.carrier } : {} };
2762
+ return { state: r2.state, signal: r2.signal };
2763
+ }
2764
+ })
2765
+ });
2115
2766
  }
2116
- })();
2117
- return createRunHandle(sessionId, resumeRunId, resumeExecution, resumeStopFlag, resumeStepRef, buildResumeFn());
2118
- };
2767
+ const prev = lastResult;
2768
+ if (prev === null) throw new NoInterruptError();
2769
+ const injected = injectInterruptResponseIntoState(prev.cursor ?? void 0, prev.state, interruptId, response);
2770
+ if (!injected.found) throw new NoInterruptError();
2771
+ const state = injected.state;
2772
+ const cursor = injected.cursor;
2773
+ const r = await runLoop(
2774
+ agentInternals.loopDef,
2775
+ state,
2776
+ resumeCtx,
2777
+ agentInternals.stateSchema,
2778
+ () => resumeStopFlag.stopped,
2779
+ cursor,
2780
+ {
2781
+ runId: resumeRunId,
2782
+ resumedFrom: { runId: prev.runId, ...prev.carrier !== void 0 ? { carrier: prev.carrier } : {} },
2783
+ onBeforeStep: (n, s) => {
2784
+ resumeStepRef.current = n;
2785
+ events.onBeforeStep?.(n, s);
2786
+ },
2787
+ ...events.onAfterStep !== void 0 ? { onAfterStep: events.onAfterStep } : {},
2788
+ ...events.onError !== void 0 ? { onError: events.onError } : {},
2789
+ ...events.onComplete !== void 0 ? { onComplete: events.onComplete } : {},
2790
+ ...events.onInterrupt !== void 0 ? { onInterrupt: events.onInterrupt } : {},
2791
+ ...Object.keys(listeners).length > 0 ? { listeners } : {},
2792
+ ...observer !== void 0 ? { observer } : {},
2793
+ ...onObserverError !== void 0 ? { onObserverError } : {},
2794
+ ...telemetrySlots.length > 0 ? { telemetrySlots } : {},
2795
+ ...debug !== void 0 ? { debugGate: debug.gate } : {}
2796
+ }
2797
+ );
2798
+ if (r.signal === "$interrupt") interruptPendingSessions.add(sessionId);
2799
+ lastResult = { state: r.state, cursor: r.cursor, runId: resumeRunId, ...r.carrier !== void 0 ? { carrier: r.carrier } : {} };
2800
+ return { state: r.state, signal: r.signal };
2801
+ } finally {
2802
+ inFlightSessions.delete(sessionId);
2803
+ }
2804
+ })();
2805
+ return createRunHandle(sessionId, resumeRunId, resumeExecution, resumeStopFlag, resumeStepRef, buildResumeFn());
2119
2806
  };
2120
- return createRunHandle(sessionId, runId, execution, stopFlag, stepRef, buildResumeFn());
2121
- },
2807
+ };
2808
+ return createRunHandle(sessionId, runId, execution, stopFlag, stepRef, buildResumeFn());
2809
+ };
2810
+ const agent = {
2811
+ id,
2812
+ run: (initialState, resources) => startRun(initialState, resources),
2122
2813
  resume: (response, sessionId, interruptId, resources) => {
2814
+ if (debugSessions.has(sessionId)) throw new SessionInDebugError(sessionId);
2123
2815
  if (inFlightSessions.has(sessionId)) throw new SessionInFlightError(sessionId);
2124
2816
  interruptPendingSessions.delete(sessionId);
2125
2817
  inFlightSessions.add(sessionId);
2126
2818
  const events = extractRunEvents(resources ?? {});
2127
- const observer = extractRunObserver(resources ?? {});
2128
- const onObserverError = extractRunErrorSink(resources ?? {});
2819
+ const { observer, onObserverError } = resolveObserver(resources ?? {});
2129
2820
  const telemetrySlots = buildTelemetrySlots(agentInternals.resolvedProviders.entries());
2130
2821
  const parentRunId = resources?.["parentRunId"];
2131
2822
  const claimOptions = parseClaimOptions(resources?.["claimOptions"], fallbackClaimOptions());
@@ -2138,18 +2829,41 @@ function createAgent(id, h, slots, agentOptions) {
2138
2829
  ...typeof parentRunId === "string" ? { parentRunId } : {}
2139
2830
  });
2140
2831
  },
2832
+ debug: (initialState, resources) => {
2833
+ const rawSignal = resources["signal"];
2834
+ let sessionId;
2835
+ const session = createDebugSession({
2836
+ start: (hooks) => {
2837
+ const handle = startRun(initialState, resources, hooks);
2838
+ sessionId = handle.sessionId;
2839
+ return handle;
2840
+ },
2841
+ store: capturedStore,
2842
+ signal: rawSignal instanceof AbortSignal ? rawSignal : void 0,
2843
+ onEnd: () => {
2844
+ if (sessionId !== void 0) debugSessions.delete(sessionId);
2845
+ }
2846
+ });
2847
+ debugSessions.add(session.sessionId);
2848
+ return session;
2849
+ },
2141
2850
  status: (sessionId) => querySessionPhase(capturedStore, id, sessionId),
2142
2851
  [_agentInternals]: agentInternals
2143
2852
  };
2144
2853
  return agent;
2145
2854
  }
2146
2855
  export {
2856
+ DebugCommandError,
2857
+ InvalidAgentOptionError,
2858
+ InvalidClaimOptionsError,
2859
+ InvalidRunResourceError,
2147
2860
  LeaseExpiredError,
2148
2861
  LoopNotDefinedError,
2149
2862
  NoInterruptError,
2150
2863
  REQUIRED_TAG,
2151
2864
  RUNTIME_TAG,
2152
2865
  SessionBusyError,
2866
+ SessionInDebugError,
2153
2867
  SessionInFlightError,
2154
2868
  SessionPendingInterruptError,
2155
2869
  StoreLoadError,