@noetaris/harness 0.8.0 → 0.10.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
@@ -510,6 +510,14 @@ function safeInvoke(hookName, call, sink) {
510
510
  reportObserverError(error, { hookName }, sink);
511
511
  }
512
512
  }
513
+ function safeCall(hookName, call, sink) {
514
+ try {
515
+ return call();
516
+ } catch (error) {
517
+ reportObserverError(error, { hookName }, sink);
518
+ return void 0;
519
+ }
520
+ }
513
521
  async function runWrapped(wrap, fn, onObserverError) {
514
522
  let p;
515
523
  const inner = () => {
@@ -556,6 +564,16 @@ function composeObservers(observers, onObserverError) {
556
564
  onStepSettled: (ctx, event) => {
557
565
  for (const o of observers) safeInvoke("onStepSettled", () => o.onStepSettled?.(ctx, event), onObserverError);
558
566
  },
567
+ // Each observer isolated on its own; later keys win.
568
+ carry: (ctx) => {
569
+ let merged;
570
+ for (const o of observers) {
571
+ const carried = safeCall("carry", () => o.carry?.(ctx), onObserverError);
572
+ if (typeof carried !== "object" || carried === null || Array.isArray(carried)) continue;
573
+ merged = Object.assign(merged ?? {}, carried);
574
+ }
575
+ return merged;
576
+ },
559
577
  // First observer outermost; each layer isolated on its own by runWrapped.
560
578
  wrap: (ctx, scope, fn) => {
561
579
  const layer = (i) => i === wrappers.length ? fn() : runWrapped((inner) => wrappers[i].wrap(ctx, scope, inner), () => layer(i + 1), onObserverError);
@@ -706,6 +724,11 @@ function stepContextOf(runCtx, stepName) {
706
724
  function asError(e) {
707
725
  return e instanceof Error ? e : new Error(String(e));
708
726
  }
727
+ function normalizeCarrier(carried) {
728
+ if (typeof carried !== "object" || carried === null || Array.isArray(carried)) return void 0;
729
+ const entries = Object.entries(carried).filter(([, v]) => typeof v === "string");
730
+ return entries.length > 0 ? Object.freeze(Object.fromEntries(entries)) : void 0;
731
+ }
709
732
  async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callbacks) {
710
733
  const { onBeforeStep, onAfterStep, onError, onComplete, onInterrupt } = callbacks ?? {};
711
734
  const obs = callbacks?.observer;
@@ -715,15 +738,22 @@ async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callb
715
738
  if (!("$interrupt" in state)) state.$interrupt = null;
716
739
  if (!("$interruptResponses" in state)) state.$interruptResponses = {};
717
740
  const runStart = Date.now();
741
+ const branchPath = callbacks?.branchPath;
742
+ const isBranch = branchPath !== void 0 && branchPath.length > 0;
718
743
  const runCtx = {
719
744
  agentId: ctx.agentId,
720
745
  sessionId: ctx.sessionId,
721
746
  runId: typeof callbacks?.runId === "string" ? callbacks.runId : "",
722
747
  ...typeof callbacks?.parentRunId === "string" ? { parentRunId: callbacks.parentRunId } : {},
723
748
  ...typeof ctx.instanceId === "string" ? { instanceId: ctx.instanceId } : {},
724
- ...callbacks?.branchPath !== void 0 && callbacks.branchPath.length > 0 ? { branchPath: callbacks.branchPath } : {}
749
+ ...isBranch ? { branchPath } : {},
750
+ // Branch runLoops receive the outer callbacks by spread, so the refs are dropped here.
751
+ ...!isBranch && callbacks?.resumedFrom !== void 0 ? { resumedFrom: callbacks.resumedFrom } : {},
752
+ ...!isBranch && callbacks?.branchedFrom !== void 0 ? { branchedFrom: callbacks.branchedFrom } : {}
725
753
  };
726
754
  safeInvoke("onRunStart", () => obs?.onRunStart?.(runCtx), onObserverError);
755
+ const carrier = isBranch ? void 0 : normalizeCarrier(safeCall("carry", () => obs?.carry?.(runCtx), onObserverError));
756
+ const withCarrier = (result) => carrier !== void 0 ? { ...result, carrier } : result;
727
757
  let runEnded = false;
728
758
  const endRun = (signal, failure) => {
729
759
  runEnded = true;
@@ -741,7 +771,7 @@ async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callb
741
771
  while (true) {
742
772
  if (shouldStop?.()) {
743
773
  endRun("$stopped");
744
- return { state, signal: null, cursor, paused: true };
774
+ return withCarrier({ state, signal: null, cursor, paused: true });
745
775
  }
746
776
  if (isForkCursor(cursor)) {
747
777
  const forkCursor = cursor;
@@ -765,7 +795,7 @@ async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callb
765
795
  graph
766
796
  );
767
797
  if (outcome.done) {
768
- return outcome.result;
798
+ return withCarrier(outcome.result);
769
799
  }
770
800
  cursor = outcome.nextCursor;
771
801
  continue;
@@ -788,7 +818,7 @@ async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callb
788
818
  graph
789
819
  );
790
820
  if (outcome.done) {
791
- return outcome.result;
821
+ return withCarrier(outcome.result);
792
822
  }
793
823
  cursor = outcome.nextCursor;
794
824
  continue;
@@ -834,7 +864,7 @@ async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callb
834
864
  safeInvoke("onInterrupt", () => obs?.onInterrupt?.(stepCtx, { prompt: e.prompt, interruptId: e.interruptId }), onObserverError);
835
865
  settle({ outcome: "interrupt", interrupt: { interruptId: e.interruptId, prompt: e.prompt }, next: { kind: "pause" } });
836
866
  endRun("$interrupt");
837
- return { state, signal: "$interrupt", cursor, paused: true };
867
+ return withCarrier({ state, signal: "$interrupt", cursor, paused: true });
838
868
  }
839
869
  state.$error = e instanceof Error ? e : new Error(String(e));
840
870
  onError?.(state.$error, cursor);
@@ -856,7 +886,7 @@ async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callb
856
886
  state.$error = e instanceof Error ? e : new Error(String(e));
857
887
  settle({ outcome: "error", error: state.$error, next: { kind: "pause" } });
858
888
  endRun("$error", { error: state.$error });
859
- return { state, signal: "$error", cursor, paused: true };
889
+ return withCarrier({ state, signal: "$error", cursor, paused: true });
860
890
  }
861
891
  const routed = runSucceeded || step.run === void 0 ? { outcome: "ok", signal } : { outcome: "error", signal, error: state.$error };
862
892
  const transition = step.transitions.find((t) => t.signal === signal);
@@ -869,7 +899,7 @@ async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callb
869
899
  settle({ ...routed, next: { kind: "end" } });
870
900
  onComplete?.(state, signal);
871
901
  endRun(signal);
872
- return { state, signal, cursor: null, paused: false };
902
+ return withCarrier({ state, signal, cursor: null, paused: false });
873
903
  }
874
904
  settle({ ...routed, next: { kind: "step", name: transition.target.name } });
875
905
  cursor = transition.target.name;
@@ -880,7 +910,7 @@ async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callb
880
910
  } else {
881
911
  settle({ outcome: "error", error: state.$error, next: { kind: "pause" } });
882
912
  endRun("$error", { error: state.$error });
883
- return { state, signal: "$error", cursor, paused: true };
913
+ return withCarrier({ state, signal: "$error", cursor, paused: true });
884
914
  }
885
915
  } else {
886
916
  const next = step.next ?? implicitNextMap.get(cursor) ?? null;
@@ -1054,6 +1084,13 @@ async function handleFork(forkNode, priorBranches, state, ctx, schema, shouldSto
1054
1084
  }
1055
1085
 
1056
1086
  // src/agent/session-store.ts
1087
+ function isValidTtlMs(value) {
1088
+ return typeof value === "number" && Number.isFinite(value) && value > 0;
1089
+ }
1090
+ function defaultClaimTtlMs() {
1091
+ const ttlMs = Number(process.env["HARNESS_CLAIM_TTL_MS"]);
1092
+ return isValidTtlMs(ttlMs) ? ttlMs : 3e4;
1093
+ }
1057
1094
  function collectPendingInterrupts(cursor, state) {
1058
1095
  if (cursor !== void 0 && isForkCursor(cursor)) {
1059
1096
  const result = [];
@@ -1249,9 +1286,16 @@ async function querySessionPhase(store, agentId, sessionId) {
1249
1286
  const loaded = await store.load(agentId, sessionId);
1250
1287
  return storedSessionToPhase(loaded);
1251
1288
  }
1289
+ 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
+ }
1294
+ return loaded.branchedFrom !== void 0 ? { branchedFrom: loaded.branchedFrom } : {};
1295
+ }
1252
1296
  async function runWithSession(store, agentId, sessionId, runId, graph, initialStateArg, schema, ctx, options) {
1253
1297
  const leaseRef = options?.leaseRef ?? { current: null };
1254
- const keepAlive = createKeepAliveFn(leaseRef, store, options?.claimTtlMs ?? 3e4);
1298
+ const keepAlive = createKeepAliveFn(leaseRef, store, options?.claimTtlMs ?? defaultClaimTtlMs());
1255
1299
  ctx["keepAlive"] = keepAlive;
1256
1300
  let leaseExpired = false;
1257
1301
  const composedShouldStop = () => {
@@ -1300,7 +1344,8 @@ async function runWithSession(store, agentId, sessionId, runId, graph, initialSt
1300
1344
  const result = await runLoop(graph, state, ctx, schema, composedShouldStop, loaded?.step, {
1301
1345
  ...options,
1302
1346
  runId,
1303
- ...options?.parentRunId !== void 0 ? { parentRunId: options.parentRunId } : {}
1347
+ ...options?.parentRunId !== void 0 ? { parentRunId: options.parentRunId } : {},
1348
+ ...runRefsOf(loaded)
1304
1349
  });
1305
1350
  if (leaseExpired) {
1306
1351
  const expiredError = new LeaseExpiredError(sessionId);
@@ -1324,7 +1369,8 @@ async function runWithSession(store, agentId, sessionId, runId, graph, initialSt
1324
1369
  finalState: result.state,
1325
1370
  step: result.cursor,
1326
1371
  ...result.signal !== null ? { signal: result.signal } : {},
1327
- ...metadata !== void 0 ? { metadata } : {}
1372
+ ...metadata !== void 0 ? { metadata } : {},
1373
+ ...result.carrier !== void 0 ? { carrier: result.carrier } : {}
1328
1374
  };
1329
1375
  await store.save(agentId, sessionId, saved);
1330
1376
  } else {
@@ -1339,7 +1385,8 @@ async function runWithSession(store, agentId, sessionId, runId, graph, initialSt
1339
1385
  initialState: initialStateSnapshot,
1340
1386
  finalState: result.state,
1341
1387
  ...result.signal !== null ? { signal: result.signal } : {},
1342
- ...metadata !== void 0 ? { metadata } : {}
1388
+ ...metadata !== void 0 ? { metadata } : {},
1389
+ ...result.carrier !== void 0 ? { carrier: result.carrier } : {}
1343
1390
  };
1344
1391
  await store.save(agentId, sessionId, saved);
1345
1392
  }
@@ -1349,6 +1396,50 @@ async function runWithSession(store, agentId, sessionId, runId, graph, initialSt
1349
1396
  return result;
1350
1397
  }
1351
1398
 
1399
+ // src/agent/claim-scope.ts
1400
+ function failOutcome(failState, error) {
1401
+ const state = failState();
1402
+ state["$error"] = error;
1403
+ return { state, signal: "$error" };
1404
+ }
1405
+ async function runClaimed(args) {
1406
+ const { store, agentId, sessionId, claimOptions, onStoreError, failState, body } = args;
1407
+ const fail = (error) => failOutcome(failState, error);
1408
+ let leaseRef = void 0;
1409
+ if (typeof store?.claim === "function") {
1410
+ let lease;
1411
+ try {
1412
+ lease = await store.claim(agentId, sessionId, claimOptions);
1413
+ } catch (claimError) {
1414
+ const loadError = new StoreLoadError(claimError);
1415
+ onStoreError?.(loadError, "claim");
1416
+ return fail(loadError);
1417
+ }
1418
+ if (lease === null) {
1419
+ const busyError = new SessionBusyError(sessionId);
1420
+ onStoreError?.(busyError, "claim");
1421
+ return fail(busyError);
1422
+ }
1423
+ leaseRef = { current: lease };
1424
+ }
1425
+ try {
1426
+ return await body(leaseRef !== void 0 ? { leaseRef, claimTtlMs: claimOptions.ttlMs } : {});
1427
+ } catch (error) {
1428
+ if (error instanceof LeaseExpiredError) return fail(error);
1429
+ throw error;
1430
+ } finally {
1431
+ if (leaseRef !== void 0 && typeof store?.release === "function") {
1432
+ try {
1433
+ await store.release(leaseRef.current);
1434
+ } catch {
1435
+ }
1436
+ }
1437
+ }
1438
+ }
1439
+ function parseClaimOptions(raw, fallback) {
1440
+ return typeof raw === "object" && raw !== null && typeof raw["ttlMs"] === "number" ? raw : fallback;
1441
+ }
1442
+
1352
1443
  // src/agent/interrupt-resume.ts
1353
1444
  var NoInterruptError = class extends Error {
1354
1445
  constructor() {
@@ -1416,10 +1507,25 @@ async function injectInterruptResponse(store, agentId, sessionId, interruptId, r
1416
1507
  initialState: loaded.initialState,
1417
1508
  finalState: result.state,
1418
1509
  ...result.cursor !== void 0 ? { step: result.cursor } : {},
1419
- ...loaded.signal !== void 0 ? { signal: loaded.signal } : {}
1510
+ ...loaded.signal !== void 0 ? { signal: loaded.signal } : {},
1511
+ ...loaded.metadata !== void 0 ? { metadata: loaded.metadata } : {},
1512
+ ...loaded.carrier !== void 0 ? { carrier: loaded.carrier } : {},
1513
+ ...loaded.branchedFrom !== void 0 ? { branchedFrom: loaded.branchedFrom } : {}
1420
1514
  };
1421
1515
  await store.save(agentId, sessionId, updated);
1422
1516
  }
1517
+ async function injectThenRun(args) {
1518
+ const { store, agentId, sessionId, interruptId, response, onStoreError, failState, run } = args;
1519
+ try {
1520
+ await injectInterruptResponse(store, agentId, sessionId, interruptId, response);
1521
+ } catch (injectError) {
1522
+ if (injectError instanceof NoInterruptError) throw injectError;
1523
+ const storeError = new StoreLoadError(injectError);
1524
+ onStoreError?.(storeError, "claim");
1525
+ return failOutcome(failState, storeError);
1526
+ }
1527
+ return run();
1528
+ }
1423
1529
 
1424
1530
  // src/agent/run-handle.ts
1425
1531
  function createRunHandle(sessionId, runId, execution, stopFlag, stepRef, resumeFn) {
@@ -1579,6 +1685,18 @@ var UnexpectedStoreSlotError = class extends Error {
1579
1685
  this.key = key;
1580
1686
  }
1581
1687
  };
1688
+ function describeValue(value) {
1689
+ return typeof value === "string" ? JSON.stringify(value) : String(value);
1690
+ }
1691
+ var InvalidClaimOptionsError = class extends Error {
1692
+ /** The rejected `ttlMs` value, as given. */
1693
+ ttlMs;
1694
+ constructor(ttlMs) {
1695
+ super(`claimOptions.ttlMs must be a finite number of milliseconds greater than 0 \u2014 got ${describeValue(ttlMs)} in createAgent options`);
1696
+ this.name = "InvalidClaimOptionsError";
1697
+ this.ttlMs = ttlMs;
1698
+ }
1699
+ };
1582
1700
  var _agentInternals = /* @__PURE__ */ Symbol("_agentInternals");
1583
1701
  function createAgent(id, h, slots, agentOptions) {
1584
1702
  const internals = getInternals(h);
@@ -1609,6 +1727,13 @@ function createAgent(id, h, slots, agentOptions) {
1609
1727
  }
1610
1728
  }
1611
1729
  }
1730
+ let agentClaimOptions = void 0;
1731
+ if (agentOptions?.claimOptions !== void 0) {
1732
+ const ttlMs = agentOptions.claimOptions?.ttlMs;
1733
+ if (!isValidTtlMs(ttlMs)) throw new InvalidClaimOptionsError(ttlMs);
1734
+ agentClaimOptions = { ttlMs };
1735
+ }
1736
+ const fallbackClaimOptions = () => agentClaimOptions ?? { ttlMs: defaultClaimTtlMs() };
1612
1737
  const resolvedProviders = /* @__PURE__ */ new Map();
1613
1738
  const storeEntries = [];
1614
1739
  const runtimeKeys = /* @__PURE__ */ new Set();
@@ -1661,7 +1786,9 @@ function createAgent(id, h, slots, agentOptions) {
1661
1786
  const reservedRunKeys = /* @__PURE__ */ new Set(["sessionId", "signal", "events", "listeners", "observer", "onObserverError", "claimOptions", "parentRunId"]);
1662
1787
  const inFlightSessions = /* @__PURE__ */ new Set();
1663
1788
  const interruptPendingSessions = /* @__PURE__ */ new Set();
1664
- const makeAgentResumeHandle = (resp, sId, iId, resumeOpts, observer, onObserverError, telemetrySlots) => {
1789
+ const resumeFailState = () => initializeState(null, {}, agentInternals.stateSchema);
1790
+ const makeAgentResumeHandle = (resp, sId, iId, resumeResources) => {
1791
+ const { onStoreError, observer, onObserverError, telemetrySlots, parentRunId, claimOptions } = resumeResources;
1665
1792
  let _stopped = false;
1666
1793
  const abortController = new AbortController();
1667
1794
  const flag = {
@@ -1677,86 +1804,63 @@ function createAgent(id, h, slots, agentOptions) {
1677
1804
  const rId = randomUUID();
1678
1805
  const exec = (async () => {
1679
1806
  await Promise.resolve();
1680
- const claimOptions = { ttlMs: 3e4 };
1681
- let leaseRef = void 0;
1682
1807
  try {
1683
- if (typeof capturedStore?.claim === "function") {
1684
- let lease;
1685
- try {
1686
- lease = await capturedStore.claim(id, sId, claimOptions);
1687
- } catch (claimError) {
1688
- const loadError = new StoreLoadError(claimError);
1689
- resumeOpts?.onStoreError?.(loadError, "claim");
1690
- const failState = initializeState(null, {}, agentInternals.stateSchema);
1691
- failState["$error"] = loadError;
1692
- return { state: failState, signal: "$error" };
1693
- }
1694
- if (lease === null) {
1695
- const busyError = new SessionBusyError(sId);
1696
- resumeOpts?.onStoreError?.(busyError, "claim");
1697
- const failState = initializeState(null, {}, agentInternals.stateSchema);
1698
- failState["$error"] = busyError;
1699
- return { state: failState, signal: "$error" };
1700
- }
1701
- leaseRef = { current: lease };
1702
- }
1703
- try {
1704
- if (capturedStore === void 0) throw new NoInterruptError();
1705
- try {
1706
- await injectInterruptResponse(capturedStore, id, sId, iId, resp);
1707
- } catch (injectError) {
1708
- if (injectError instanceof NoInterruptError) throw injectError;
1709
- const storeError = new StoreLoadError(injectError);
1710
- resumeOpts?.onStoreError?.(storeError, "claim");
1711
- const failState = initializeState(null, {}, agentInternals.stateSchema);
1712
- failState["$error"] = storeError;
1713
- return { state: failState, signal: "$error" };
1714
- }
1715
- const agentCtx = {
1716
- ...Object.fromEntries(agentInternals.resolvedProviders),
1717
- agentId: id,
1718
- sessionId: sId,
1719
- runId: rId,
1720
- signal: abortController.signal
1721
- };
1722
- const r = await runWithSession(
1723
- capturedStore,
1724
- id,
1725
- sId,
1726
- rId,
1727
- agentInternals.loopDef,
1728
- {},
1729
- agentInternals.stateSchema,
1730
- agentCtx,
1731
- {
1732
- shouldStop: () => flag.stopped,
1733
- onBeforeStep: (n) => {
1734
- ref.current = n;
1735
- },
1736
- ...resumeOpts?.onStoreError !== void 0 ? { onStoreError: resumeOpts.onStoreError } : {},
1737
- ...leaseRef !== void 0 ? { leaseRef, claimTtlMs: claimOptions.ttlMs } : {},
1738
- ...observer !== void 0 ? { observer } : {},
1739
- ...onObserverError !== void 0 ? { onObserverError } : {},
1740
- ...telemetrySlots !== void 0 && telemetrySlots.length > 0 ? { telemetrySlots } : {}
1741
- }
1742
- );
1743
- if (r.signal === "$interrupt") interruptPendingSessions.add(sId);
1744
- return { state: r.state, signal: r.signal };
1745
- } catch (error) {
1746
- if (error instanceof LeaseExpiredError) {
1747
- const failState = initializeState(null, {}, agentInternals.stateSchema);
1748
- failState["$error"] = error;
1749
- return { state: failState, signal: "$error" };
1750
- }
1751
- throw error;
1752
- } finally {
1753
- if (leaseRef !== void 0 && typeof capturedStore?.release === "function") {
1754
- try {
1755
- await capturedStore.release(leaseRef.current);
1756
- } catch {
1757
- }
1808
+ return await runClaimed({
1809
+ store: capturedStore,
1810
+ agentId: id,
1811
+ sessionId: sId,
1812
+ claimOptions,
1813
+ ...onStoreError !== void 0 ? { onStoreError } : {},
1814
+ failState: resumeFailState,
1815
+ body: async (lease) => {
1816
+ if (capturedStore === void 0) throw new NoInterruptError();
1817
+ const store = capturedStore;
1818
+ return injectThenRun({
1819
+ store,
1820
+ agentId: id,
1821
+ sessionId: sId,
1822
+ interruptId: iId,
1823
+ response: resp,
1824
+ ...onStoreError !== void 0 ? { onStoreError } : {},
1825
+ failState: resumeFailState,
1826
+ run: async () => {
1827
+ const agentCtx = {
1828
+ ...Object.fromEntries(agentInternals.resolvedProviders),
1829
+ agentId: id,
1830
+ sessionId: sId,
1831
+ runId: rId,
1832
+ signal: abortController.signal,
1833
+ ...agentOptions?.instanceId !== void 0 ? { instanceId: agentOptions.instanceId } : {}
1834
+ };
1835
+ const r = await runWithSession(
1836
+ store,
1837
+ id,
1838
+ sId,
1839
+ rId,
1840
+ agentInternals.loopDef,
1841
+ {},
1842
+ agentInternals.stateSchema,
1843
+ agentCtx,
1844
+ {
1845
+ shouldStop: () => flag.stopped,
1846
+ onBeforeStep: (n) => {
1847
+ ref.current = n;
1848
+ },
1849
+ ...onStoreError !== void 0 ? { onStoreError } : {},
1850
+ ...lease,
1851
+ ...observer !== void 0 ? { observer } : {},
1852
+ ...onObserverError !== void 0 ? { onObserverError } : {},
1853
+ ...telemetrySlots !== void 0 && telemetrySlots.length > 0 ? { telemetrySlots } : {},
1854
+ ...agentOptions?.instanceId !== void 0 ? { instanceId: agentOptions.instanceId } : {},
1855
+ ...parentRunId !== void 0 ? { parentRunId } : {}
1856
+ }
1857
+ );
1858
+ if (r.signal === "$interrupt") interruptPendingSessions.add(sId);
1859
+ return { state: r.state, signal: r.signal };
1860
+ }
1861
+ });
1758
1862
  }
1759
- }
1863
+ });
1760
1864
  } finally {
1761
1865
  inFlightSessions.delete(sId);
1762
1866
  }
@@ -1765,7 +1869,8 @@ function createAgent(id, h, slots, agentOptions) {
1765
1869
  if (inFlightSessions.has(sId)) throw new SessionInFlightError(sId);
1766
1870
  interruptPendingSessions.delete(sId);
1767
1871
  inFlightSessions.add(sId);
1768
- return makeAgentResumeHandle(r, sId, i);
1872
+ const { parentRunId: _staleParentRunId, ...inherited } = resumeResources;
1873
+ return makeAgentResumeHandle(r, sId, i, inherited);
1769
1874
  };
1770
1875
  return createRunHandle(sId, rId, exec, flag, ref, resumeFn);
1771
1876
  };
@@ -1839,6 +1944,7 @@ function createAgent(id, h, slots, agentOptions) {
1839
1944
  ...Object.entries(runtimeSlots)
1840
1945
  ]);
1841
1946
  const parentRunId = typeof resources["parentRunId"] === "string" ? resources["parentRunId"] : void 0;
1947
+ const claimOptions = parseClaimOptions(resources["claimOptions"], fallbackClaimOptions());
1842
1948
  const options = {
1843
1949
  shouldStop: () => stopFlag.stopped,
1844
1950
  onBeforeStep: (name, state) => {
@@ -1861,63 +1967,31 @@ function createAgent(id, h, slots, agentOptions) {
1861
1967
  let lastResult = null;
1862
1968
  const execution = (async () => {
1863
1969
  await Promise.resolve();
1864
- const rawClaimOpts = resources["claimOptions"];
1865
- const claimOptions = typeof rawClaimOpts === "object" && rawClaimOpts !== null && typeof rawClaimOpts["ttlMs"] === "number" ? rawClaimOpts : { ttlMs: 3e4 };
1866
- let leaseRef = void 0;
1867
1970
  try {
1868
- if (typeof capturedStore?.claim === "function") {
1869
- let lease;
1870
- try {
1871
- lease = await capturedStore.claim(id, sessionId, claimOptions);
1872
- } catch (claimError) {
1873
- const loadError = new StoreLoadError(claimError);
1874
- events.onStoreError?.(loadError, "claim");
1875
- const failState = initializeState(null, initialState, agentInternals.stateSchema);
1876
- failState["$error"] = loadError;
1877
- return { state: failState, signal: "$error" };
1878
- }
1879
- if (lease === null) {
1880
- const busyError = new SessionBusyError(sessionId);
1881
- events.onStoreError?.(busyError, "claim");
1882
- const failState = initializeState(null, initialState, agentInternals.stateSchema);
1883
- failState["$error"] = busyError;
1884
- return { state: failState, signal: "$error" };
1885
- }
1886
- leaseRef = { current: lease };
1887
- }
1888
- try {
1889
- const r = await runWithSession(
1890
- capturedStore,
1891
- id,
1892
- sessionId,
1893
- runId,
1894
- agentInternals.loopDef,
1895
- initialState,
1896
- agentInternals.stateSchema,
1897
- ctx,
1898
- {
1899
- ...options,
1900
- ...leaseRef !== void 0 ? { leaseRef, claimTtlMs: claimOptions.ttlMs } : {}
1901
- }
1902
- );
1903
- if (r.signal === "$interrupt") interruptPendingSessions.add(sessionId);
1904
- lastResult = { state: r.state, cursor: r.cursor };
1905
- return { state: r.state, signal: r.signal };
1906
- } catch (error) {
1907
- if (error instanceof LeaseExpiredError) {
1908
- const failState = initializeState(null, initialState, agentInternals.stateSchema);
1909
- failState["$error"] = error;
1910
- return { state: failState, signal: "$error" };
1911
- }
1912
- throw error;
1913
- } finally {
1914
- if (leaseRef !== void 0 && typeof capturedStore?.release === "function") {
1915
- try {
1916
- await capturedStore.release(leaseRef.current);
1917
- } catch {
1918
- }
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,
1982
+ 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 };
1919
1993
  }
1920
- }
1994
+ });
1921
1995
  } finally {
1922
1996
  inFlightSessions.delete(sessionId);
1923
1997
  }
@@ -1949,37 +2023,59 @@ function createAgent(id, h, slots, agentOptions) {
1949
2023
  signal: resumeAbortController.signal
1950
2024
  };
1951
2025
  if (capturedStore !== void 0) {
1952
- await injectInterruptResponse(capturedStore, id, sessionId, interruptId, response);
1953
- const r2 = await runWithSession(
1954
- capturedStore,
1955
- id,
2026
+ const store = capturedStore;
2027
+ const onStoreError = events.onStoreError;
2028
+ return await runClaimed({
2029
+ store,
2030
+ agentId: id,
1956
2031
  sessionId,
1957
- resumeRunId,
1958
- agentInternals.loopDef,
1959
- {},
1960
- agentInternals.stateSchema,
1961
- resumeCtx,
1962
- {
1963
- shouldStop: () => resumeStopFlag.stopped,
1964
- onBeforeStep: (n, s) => {
1965
- resumeStepRef.current = n;
1966
- events.onBeforeStep?.(n, s);
1967
- },
1968
- // spread optional event callbacks only when defined (exactOptionalPropertyTypes requires omission, not undefined)
1969
- ...events.onStoreError !== void 0 ? { onStoreError: events.onStoreError } : {},
1970
- ...events.onAfterStep !== void 0 ? { onAfterStep: events.onAfterStep } : {},
1971
- ...events.onError !== void 0 ? { onError: events.onError } : {},
1972
- ...events.onComplete !== void 0 ? { onComplete: events.onComplete } : {},
1973
- ...events.onInterrupt !== void 0 ? { onInterrupt: events.onInterrupt } : {},
1974
- ...Object.keys(listeners).length > 0 ? { listeners } : {},
1975
- ...observer !== void 0 ? { observer } : {},
1976
- ...onObserverError !== void 0 ? { onObserverError } : {},
1977
- ...telemetrySlots.length > 0 ? { telemetrySlots } : {}
1978
- }
1979
- );
1980
- if (r2.signal === "$interrupt") interruptPendingSessions.add(sessionId);
1981
- lastResult = { state: r2.state, cursor: r2.cursor };
1982
- return { state: r2.state, signal: r2.signal };
2032
+ claimOptions,
2033
+ ...onStoreError !== void 0 ? { onStoreError } : {},
2034
+ 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
+ });
1983
2079
  }
1984
2080
  const prev = lastResult;
1985
2081
  if (prev === null) throw new NoInterruptError();
@@ -1996,6 +2092,7 @@ function createAgent(id, h, slots, agentOptions) {
1996
2092
  cursor,
1997
2093
  {
1998
2094
  runId: resumeRunId,
2095
+ resumedFrom: { runId: prev.runId, ...prev.carrier !== void 0 ? { carrier: prev.carrier } : {} },
1999
2096
  onBeforeStep: (n, s) => {
2000
2097
  resumeStepRef.current = n;
2001
2098
  events.onBeforeStep?.(n, s);
@@ -2011,15 +2108,8 @@ function createAgent(id, h, slots, agentOptions) {
2011
2108
  }
2012
2109
  );
2013
2110
  if (r.signal === "$interrupt") interruptPendingSessions.add(sessionId);
2014
- lastResult = { state: r.state, cursor: r.cursor };
2111
+ lastResult = { state: r.state, cursor: r.cursor, runId: resumeRunId, ...r.carrier !== void 0 ? { carrier: r.carrier } : {} };
2015
2112
  return { state: r.state, signal: r.signal };
2016
- } catch (error) {
2017
- if (error instanceof LeaseExpiredError) {
2018
- const failState = initializeState(null, initialState, agentInternals.stateSchema);
2019
- failState["$error"] = error;
2020
- return { state: failState, signal: "$error" };
2021
- }
2022
- throw error;
2023
2113
  } finally {
2024
2114
  inFlightSessions.delete(sessionId);
2025
2115
  }
@@ -2034,11 +2124,19 @@ function createAgent(id, h, slots, agentOptions) {
2034
2124
  interruptPendingSessions.delete(sessionId);
2035
2125
  inFlightSessions.add(sessionId);
2036
2126
  const events = extractRunEvents(resources ?? {});
2037
- const resumeOpts = events.onStoreError !== void 0 ? { onStoreError: events.onStoreError } : void 0;
2038
2127
  const observer = extractRunObserver(resources ?? {});
2039
2128
  const onObserverError = extractRunErrorSink(resources ?? {});
2040
2129
  const telemetrySlots = buildTelemetrySlots(agentInternals.resolvedProviders.entries());
2041
- return makeAgentResumeHandle(response, sessionId, interruptId, resumeOpts, observer ?? void 0, onObserverError ?? void 0, telemetrySlots.length > 0 ? telemetrySlots : void 0);
2130
+ const parentRunId = resources?.["parentRunId"];
2131
+ const claimOptions = parseClaimOptions(resources?.["claimOptions"], fallbackClaimOptions());
2132
+ return makeAgentResumeHandle(response, sessionId, interruptId, {
2133
+ claimOptions,
2134
+ ...events.onStoreError !== void 0 ? { onStoreError: events.onStoreError } : {},
2135
+ ...observer !== void 0 ? { observer } : {},
2136
+ ...onObserverError !== void 0 ? { onObserverError } : {},
2137
+ ...telemetrySlots.length > 0 ? { telemetrySlots } : {},
2138
+ ...typeof parentRunId === "string" ? { parentRunId } : {}
2139
+ });
2042
2140
  },
2043
2141
  status: (sessionId) => querySessionPhase(capturedStore, id, sessionId),
2044
2142
  [_agentInternals]: agentInternals