@tangle-network/agent-runtime 0.259.1 → 0.261.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.
Files changed (73) hide show
  1. package/dist/{activation-Cus2oZaT.d.ts → activation-BpJ2CmXx.d.ts} +2 -2
  2. package/dist/{activation-Btw2npV2.js → activation-BwrMmguq.js} +3 -3
  3. package/dist/{activation-Btw2npV2.js.map → activation-BwrMmguq.js.map} +1 -1
  4. package/dist/agent.d.ts +2 -2
  5. package/dist/agent.js +2 -2
  6. package/dist/{bundle-DXrBPEFw.js → bundle-CvLGRVO-.js} +2 -2
  7. package/dist/{bundle-DXrBPEFw.js.map → bundle-CvLGRVO-.js.map} +1 -1
  8. package/dist/candidate-execution/index.js +5 -4
  9. package/dist/candidate-execution/index.js.map +1 -1
  10. package/dist/{coordination-driver-BmtrkNPu.js → coordination-driver-LmDxHvAO.js} +77 -11
  11. package/dist/coordination-driver-LmDxHvAO.js.map +1 -0
  12. package/dist/{delegate-DJgmZZKH.js → delegate-rkhJf5w7.js} +2 -2
  13. package/dist/{delegate-DJgmZZKH.js.map → delegate-rkhJf5w7.js.map} +1 -1
  14. package/dist/durable.d.ts +285 -57
  15. package/dist/durable.js +637 -108
  16. package/dist/durable.js.map +1 -1
  17. package/dist/{graph-BKRvWzHx.js → graph-Bn3wSH8-.js} +4 -3
  18. package/dist/graph-Bn3wSH8-.js.map +1 -0
  19. package/dist/{improvement-cycle-D0Os0Hg_.js → improvement-cycle-DYiwfjWY.js} +8 -7
  20. package/dist/improvement-cycle-DYiwfjWY.js.map +1 -0
  21. package/dist/{index-sGOO254Q.d.ts → index-tubViIQJ.d.ts} +122 -12
  22. package/dist/index.d.ts +6 -6
  23. package/dist/index.js +9 -9
  24. package/dist/intelligence.d.ts +3 -3
  25. package/dist/intelligence.js +7 -6
  26. package/dist/intelligence.js.map +1 -1
  27. package/dist/kernel.d.ts +4 -4
  28. package/dist/kernel.js +8 -8
  29. package/dist/{loop-runner-bin-DAU4rNIL.d.ts → loop-runner-bin-CL4W2pu1.d.ts} +3 -3
  30. package/dist/{loop-runner-bin-pRlDgm1i.js → loop-runner-bin-Dxqxf91b.js} +3 -3
  31. package/dist/{loop-runner-bin-pRlDgm1i.js.map → loop-runner-bin-Dxqxf91b.js.map} +1 -1
  32. package/dist/loop-runner-bin.d.ts +1 -1
  33. package/dist/loop-runner-bin.js +1 -1
  34. package/dist/mcp/bin.js +3 -3
  35. package/dist/mcp/index.d.ts +5 -5
  36. package/dist/mcp/index.js +4 -4
  37. package/dist/{openai-tools-BsqD1_Zr.d.ts → openai-tools-BlZHd0Nz.d.ts} +2 -2
  38. package/dist/{prepare-RuaAclli.js → prepare-oHUQdC5-.js} +22 -452
  39. package/dist/prepare-oHUQdC5-.js.map +1 -0
  40. package/dist/profile-D3eXNBQV.js +438 -0
  41. package/dist/profile-D3eXNBQV.js.map +1 -0
  42. package/dist/profiles.d.ts +2 -2
  43. package/dist/{protected-model-port-BFhSgMAy.js → protected-model-port-DqF0qDU6.js} +4 -3
  44. package/dist/{protected-model-port-BFhSgMAy.js.map → protected-model-port-DqF0qDU6.js.map} +1 -1
  45. package/dist/{provision-supervisor-B9tf4soF.js → provision-supervisor-By5oGk3t.js} +3 -3
  46. package/dist/{provision-supervisor-B9tf4soF.js.map → provision-supervisor-By5oGk3t.js.map} +1 -1
  47. package/dist/{runtime-DKiucqGX.js → runtime-BbQmK7qe.js} +218 -20
  48. package/dist/runtime-BbQmK7qe.js.map +1 -0
  49. package/dist/{server-DIsiQ6mu.js → server-CHlpLE22.js} +3 -3
  50. package/dist/{server-DIsiQ6mu.js.map → server-CHlpLE22.js.map} +1 -1
  51. package/dist/{stream-agent-turn-DRTFLphI.d.ts → stream-agent-turn-DTgxWXbR.d.ts} +2 -2
  52. package/dist/{structural-rollout-BIBxgW64.js → structural-rollout-Dby60zHe.js} +2 -2
  53. package/dist/{structural-rollout-BIBxgW64.js.map → structural-rollout-Dby60zHe.js.map} +1 -1
  54. package/dist/{substrate-BToFBpfh.d.ts → substrate-CxwdhZri.d.ts} +2 -2
  55. package/dist/{supervise-DsQjhLN-.js → supervise-1QzYAyS2.js} +78 -7
  56. package/dist/supervise-1QzYAyS2.js.map +1 -0
  57. package/dist/{supervisor-CJyzUjc7.js → supervisor-yavsoQQo.js} +84 -9
  58. package/dist/{supervisor-CJyzUjc7.js.map → supervisor-yavsoQQo.js.map} +1 -1
  59. package/dist/testing.d.ts +2 -2
  60. package/dist/testing.js +14 -14
  61. package/dist/tui/index.d.ts +1 -1
  62. package/dist/tui/index.js +1 -1
  63. package/dist/{types-ChewVlpE.d.ts → types-DNCNRu0E.d.ts} +24 -2
  64. package/dist/{workspace-archive-DQGFwvM6.js → workspace-archive-C9lgf77y.js} +11 -11
  65. package/dist/workspace-archive-C9lgf77y.js.map +1 -0
  66. package/package.json +4 -4
  67. package/dist/coordination-driver-BmtrkNPu.js.map +0 -1
  68. package/dist/graph-BKRvWzHx.js.map +0 -1
  69. package/dist/improvement-cycle-D0Os0Hg_.js.map +0 -1
  70. package/dist/prepare-RuaAclli.js.map +0 -1
  71. package/dist/runtime-DKiucqGX.js.map +0 -1
  72. package/dist/supervise-DsQjhLN-.js.map +0 -1
  73. package/dist/workspace-archive-DQGFwvM6.js.map +0 -1
package/dist/durable.js CHANGED
@@ -1,15 +1,17 @@
1
- import { $r as readCommittedJsonLines, Li as addSpend, Qr as prepareJsonlAppend, Si as detachedSnapshot, Zi as zeroSpend, Zr as isNoEntError, ei as writeAllBytes, zi as cloneSpend } from "./supervisor-CJyzUjc7.js";
1
+ import { $i as zeroSpend, $r as parseCommittedJsonLines, Qr as isNoEntError, St as loadSpawnForest, Vi as cloneSpend, ai as authoredProfileDigest, ei as prepareJsonlAppend, ni as writeAllBytes, ti as readCommittedJsonLines, tr as contentAddress, vt as FileSpawnJournal, wi as detachedSnapshot, zi as addSpend } from "./supervisor-yavsoQQo.js";
2
+ import { f as RuntimeRunStateError, i as ConfigError, m as ValidationError } from "./errors-DodWX-cb.js";
2
3
  import { a as withPursuitContext, t as composeRuntimeHooks } from "./runtime-hooks-tXpAarhW.js";
3
4
  import { a as writeAtomicDurableFile, r as publishExclusiveDurableFile } from "./durable-file-DWQA4ooo.js";
4
- import { _ as readRootStreamReceipt, g as readRootStream, h as ROOT_STREAM_FILE, r as supervise } from "./supervise-DsQjhLN-.js";
5
+ import { i as applyExactAgentProfileDiff } from "./profile-D3eXNBQV.js";
6
+ import { _ as readRootStream, g as ROOT_STREAM_FILE, i as superviseRootProfile, r as supervise, v as readRootStreamReceipt } from "./supervise-1QzYAyS2.js";
5
7
  import { h as supervisorRunDir } from "./run-layout-vwd4SwX_.js";
6
- import { canonicalCandidateJson } from "@tangle-network/agent-interface";
8
+ import { canonicalCandidateDigest, canonicalCandidateJson, sha256Bytes } from "@tangle-network/agent-interface";
7
9
  import { createHash } from "node:crypto";
8
10
  import { execFile } from "node:child_process";
9
- import { resolve } from "node:path";
10
- import { mkdir, readFile, rmdir, unlink } from "node:fs/promises";
11
+ import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
12
+ import { mkdir, open, readFile, realpath, rmdir, unlink } from "node:fs/promises";
11
13
  import { promisify } from "node:util";
12
- import { setTimeout } from "node:timers/promises";
14
+ import { setTimeout as setTimeout$1 } from "node:timers/promises";
13
15
  //#region src/durable/chat-engine.ts
14
16
  const encoder = new TextEncoder();
15
17
  function encodeLine(event) {
@@ -487,6 +489,7 @@ function projectSpawnNode(nodes, record, attemptIndex) {
487
489
  const runtime = stringField(payload, "runtime");
488
490
  const depth = numberField(payload, "depth");
489
491
  const assignmentId = stringField(payload, "assignmentId");
492
+ const successorOf = stringField(payload, "successorOf");
490
493
  const attemptId = stringField(payload, "attemptId");
491
494
  const startedAt = numberField(payload, "startedAt");
492
495
  nodes.set(key, {
@@ -498,6 +501,7 @@ function projectSpawnNode(nodes, record, attemptIndex) {
498
501
  ...runtime ? { runtime } : {},
499
502
  ...depth !== void 0 ? { depth } : {},
500
503
  ...assignmentId ? { assignmentId } : {},
504
+ ...successorOf ? { successorOf } : {},
501
505
  ...attemptId ? { attemptId } : {},
502
506
  startedAt: startedAt ?? record.observedAt,
503
507
  ...payload && Object.hasOwn(payload, "identity") ? { identity: payload.identity } : {},
@@ -836,6 +840,227 @@ function booleanField(value, key) {
836
840
  return typeof field === "boolean" ? field : void 0;
837
841
  }
838
842
  //#endregion
843
+ //#region src/durable/settle-record.ts
844
+ /** The settle record: the returned `SupervisedResult` as canonical JSON, written once. */
845
+ const SETTLE_RECORD_FILE = "result.json";
846
+ /** The failure record: the most recent throw, replaced by a later throw. */
847
+ const FAILURE_RECORD_FILE = "failure.json";
848
+ /** The directory already holds a settle record, so the run it records must not be re-entered. */
849
+ var SettledRunDirectoryError = class extends Error {
850
+ path;
851
+ /** The root of the recorded run, which is its `runId`. */
852
+ recordedRunId;
853
+ constructor(path, recordedRunId, requestedRunId) {
854
+ super(recordedRunId === requestedRunId ? `supervisePursuit: ${path} records that run '${recordedRunId}' already settled; a settled run is not re-entered` : `supervisePursuit: ${path} records that run '${recordedRunId}' already settled in this directory; a directory holds one settle record, so run '${requestedRunId}' needs its own runDir`);
855
+ this.name = "SettledRunDirectoryError";
856
+ this.path = path;
857
+ this.recordedRunId = recordedRunId;
858
+ }
859
+ };
860
+ /** A result member whose JSON form would misstate it. */
861
+ var UnrecordableSettleValueError = class extends TypeError {
862
+ path;
863
+ constructor(path, kind) {
864
+ super(`supervisePursuit: result${path} is a ${kind}, which JSON cannot record faithfully; a settle record holds plain data only`);
865
+ this.name = "UnrecordableSettleValueError";
866
+ this.path = path;
867
+ }
868
+ };
869
+ const PLAIN_PROTOTYPES = /* @__PURE__ */ new Set([
870
+ Object.prototype,
871
+ Array.prototype,
872
+ null
873
+ ]);
874
+ /**
875
+ * The JSON value of a result, or a throw naming the first member JSON would misstate. An
876
+ * `undefined` member and a function are dropped, exactly as any JSON consumer drops them; a Map,
877
+ * a Set, a Date, a typed array, or a class instance would be rewritten to `{}` or a string and
878
+ * so is refused.
879
+ */
880
+ function jsonValue(result) {
881
+ const seen = [];
882
+ const visit = (value, path) => {
883
+ if (value === null || typeof value !== "object") return;
884
+ if (value instanceof Map) throw new UnrecordableSettleValueError(path, "Map");
885
+ if (value instanceof Set) throw new UnrecordableSettleValueError(path, "Set");
886
+ if (value instanceof Date) throw new UnrecordableSettleValueError(path, "Date");
887
+ if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) throw new UnrecordableSettleValueError(path, "binary buffer");
888
+ if (!PLAIN_PROTOTYPES.has(Object.getPrototypeOf(value))) throw new UnrecordableSettleValueError(path, "class instance");
889
+ if (seen.includes(value)) throw new UnrecordableSettleValueError(path, "cycle");
890
+ seen.push(value);
891
+ if (Array.isArray(value)) for (const [index, item] of value.entries()) visit(item, `${path}[${index}]`);
892
+ else for (const [key, item] of Object.entries(value)) visit(item, `${path}.${key}`);
893
+ seen.pop();
894
+ };
895
+ visit(result, "");
896
+ return JSON.parse(JSON.stringify(result));
897
+ }
898
+ /**
899
+ * The exact bytes `result.json` holds for a result: its JSON value serialized as RFC 8785
900
+ * canonical JSON. Throws `UnrecordableSettleValueError` before any byte is written when the
901
+ * result carries a value JSON would misstate.
902
+ */
903
+ function settleRecordJson(result) {
904
+ return canonicalCandidateJson(jsonValue(result));
905
+ }
906
+ /** Publish `runDir/result.json` once. Throws when a record is already there. */
907
+ async function writeSettleRecord(runDir, result) {
908
+ const path = resolve(runDir, SETTLE_RECORD_FILE);
909
+ if (!publishExclusiveDurableFile(path, settleRecordJson(result))) throw new Error(`supervisePursuit: ${path} already exists; a settle record is written once`);
910
+ return path;
911
+ }
912
+ /** Replace `runDir/failure.json` with the most recent throw. */
913
+ async function writeFailureRecord(runDir, record) {
914
+ const path = resolve(runDir, FAILURE_RECORD_FILE);
915
+ writeAtomicDurableFile(path, `${JSON.stringify(record, null, 2)}\n`);
916
+ return path;
917
+ }
918
+ /**
919
+ * Read the settle record a run directory holds, or `undefined` when it holds none. A file that
920
+ * is present but is not a settle record is corruption and fails loud.
921
+ */
922
+ async function readSettleRecord(runDir) {
923
+ const path = resolve(runDir, SETTLE_RECORD_FILE);
924
+ let text;
925
+ try {
926
+ text = await readFile(path, "utf8");
927
+ } catch (error) {
928
+ if (isNoEntError(error)) return void 0;
929
+ throw error;
930
+ }
931
+ let parsed;
932
+ try {
933
+ parsed = JSON.parse(text);
934
+ } catch (cause) {
935
+ throw new Error(`supervisePursuit: ${path} is not valid JSON`, { cause });
936
+ }
937
+ const record = parsed;
938
+ if (typeof record !== "object" || record === null || typeof record.kind !== "string" || typeof record.tree !== "object" || record.tree === null || typeof record.tree.root !== "string") throw new Error(`supervisePursuit: ${path} is not a settle record (no kind or tree.root)`);
939
+ return parsed;
940
+ }
941
+ /** Read the most recent failure record, or `undefined` when the directory holds none. */
942
+ async function readFailureRecord(runDir) {
943
+ const path = resolve(runDir, FAILURE_RECORD_FILE);
944
+ let text;
945
+ try {
946
+ text = await readFile(path, "utf8");
947
+ } catch (error) {
948
+ if (isNoEntError(error)) return void 0;
949
+ throw error;
950
+ }
951
+ let parsed;
952
+ try {
953
+ parsed = JSON.parse(text);
954
+ } catch (cause) {
955
+ throw new Error(`supervisePursuit: ${path} is not valid JSON`, { cause });
956
+ }
957
+ const record = parsed;
958
+ if (typeof record !== "object" || record === null || typeof record.runId !== "string" || typeof record.pursuitId !== "string" || typeof record.at !== "string" || typeof record.error?.name !== "string" || typeof record.error.message !== "string" || record.rootStream !== void 0 && (typeof record.rootStream.ref !== "string" || typeof record.rootStream.events !== "number")) throw new Error(`supervisePursuit: ${path} is not a failure record`);
959
+ return parsed;
960
+ }
961
+ //#endregion
962
+ //#region src/durable/run-fork.ts
963
+ /**
964
+ * The `execution.correlation` keys a fork records on its root. Runtime writes them; a caller that
965
+ * supplies one is refused. `lineageRootRunId` is the first run of the chain of parents, so the
966
+ * spend of every version of one lineage groups under one id.
967
+ */
968
+ const RUN_FORK_CORRELATION_KEYS = Object.freeze([
969
+ "forkParentRunId",
970
+ "forkParentSettleDigest",
971
+ "forkProfileDiffId",
972
+ "lineageRootRunId"
973
+ ]);
974
+ /** The correlation key an accepted uncertain parent adds: its uncertain node ids, comma-joined. */
975
+ const FORK_PARENT_UNCERTAIN_NODES_KEY = "forkParentUncertainNodes";
976
+ /**
977
+ * Verify the parent and derive the fork's profile and attribution. It reads only the parent, so
978
+ * a refused fork spends nothing and writes nothing.
979
+ */
980
+ async function prepareRunFork(profile, task, opts, fork) {
981
+ const change = fork.change;
982
+ if (typeof change?.id !== "string" || change.id.trim().length === 0) throw new ValidationError("supervisePursuit fork: change.id must be a non-empty string");
983
+ const parentDir = await realDirectory(fork.runDir, "fork.runDir");
984
+ const forkDir = await realDirectory(opts.runDir, "runDir", true);
985
+ if (contains(parentDir, forkDir) || contains(forkDir, parentDir)) throw new ValidationError(`supervisePursuit fork: runDir ${forkDir} overlaps the parent's runDir ${parentDir}; a fork needs its own directory outside the parent's`);
986
+ let bytes;
987
+ try {
988
+ bytes = await readFile(resolve(parentDir, SETTLE_RECORD_FILE));
989
+ } catch (error) {
990
+ if (!isNoEntError(error)) throw error;
991
+ throw new RuntimeRunStateError(`supervisePursuit fork: ${parentDir} holds no ${SETTLE_RECORD_FILE}; only a settled run is forked`);
992
+ }
993
+ const observed = sha256Bytes(bytes);
994
+ if (observed !== fork.settleDigest) throw new RuntimeRunStateError(`supervisePursuit fork: ${SETTLE_RECORD_FILE} hashes to ${observed}, not the requested ${fork.settleDigest}`);
995
+ const settled = await readSettleRecord(parentDir);
996
+ if (settled === void 0) throw new RuntimeRunStateError(`supervisePursuit fork: ${parentDir} lost its settle record`);
997
+ const parentRunId = settled.tree.root;
998
+ if ((opts.runId ?? "supervise") === parentRunId) throw new ValidationError(`supervisePursuit fork: runId '${parentRunId}' is the parent's; a fork needs its own runId`);
999
+ const forest = await loadSpawnForest(new FileSpawnJournal(resolve(parentDir, "spawn-journal.jsonl")), parentRunId);
1000
+ const uncertain = [
1001
+ ...forest.inDoubt.map((node) => `${node.nodeId} (no terminal record)`),
1002
+ ...forest.missingTrees.map((tree) => `${tree.ownerNodeId} (tree ${tree.root} never begun)`),
1003
+ ...(settled.teardownUnconfirmed ?? []).map((node) => `${node.id} (teardown unconfirmed)`)
1004
+ ];
1005
+ if (settled.tree.inFlight > 0 && uncertain.length === 0) uncertain.push(`${settled.tree.inFlight} in flight`);
1006
+ if (uncertain.length > 0 && fork.acceptUncertain !== true) throw new RuntimeRunStateError(`supervisePursuit fork: run '${parentRunId}' has uncertain nodes: ${uncertain.join(", ")}; set fork.acceptUncertain to fork it anyway`);
1007
+ const root = forest.trees[0]?.events.find((event) => event.kind === "spawned" && event.parent === void 0);
1008
+ const recorded = root?.identity;
1009
+ if (recorded?.profileDigest === void 0 || recorded.taskDigest === void 0) throw new RuntimeRunStateError(`supervisePursuit fork: run '${parentRunId}' records no root profile and task digest`);
1010
+ const mismatches = [
1011
+ authoredProfileDigest(superviseRootProfile(profile, opts.profileGuidance)) !== recorded.profileDigest ? `profile is not the parent's ${recorded.profileDigest}` : void 0,
1012
+ digestOrUndefined(task) !== recorded.taskDigest ? `task is not the parent's ${recorded.taskDigest}` : void 0,
1013
+ contentAddress(root?.budget) !== contentAddress(opts.budget) ? "budget is not the parent's" : void 0
1014
+ ].filter((mismatch) => mismatch !== void 0);
1015
+ if (mismatches.length > 0) throw new ValidationError(`supervisePursuit fork: ${mismatches.join("; ")}`);
1016
+ const forked = applyExactAgentProfileDiff(profile, change, "supervisePursuit fork");
1017
+ if (authoredProfileDigest(superviseRootProfile(forked, opts.profileGuidance)) === recorded.profileDigest) throw new ValidationError(`supervisePursuit fork: change '${change.id}' leaves the parent's profile unchanged`);
1018
+ const supplied = opts.execution?.correlation ?? {};
1019
+ const owned = [...RUN_FORK_CORRELATION_KEYS, FORK_PARENT_UNCERTAIN_NODES_KEY].filter((key) => Object.hasOwn(supplied, key));
1020
+ if (owned.length > 0) throw new ValidationError(`supervisePursuit fork: execution.correlation cannot set ${owned.join(", ")}; Runtime records them`);
1021
+ return Object.freeze({
1022
+ profile: forked,
1023
+ execution: Object.freeze({
1024
+ ...opts.execution,
1025
+ correlation: Object.freeze({
1026
+ ...supplied,
1027
+ forkParentRunId: parentRunId,
1028
+ forkParentSettleDigest: fork.settleDigest,
1029
+ forkProfileDiffId: change.id,
1030
+ lineageRootRunId: recorded.correlation?.lineageRootRunId ?? parentRunId,
1031
+ ...uncertain.length === 0 ? {} : { [FORK_PARENT_UNCERTAIN_NODES_KEY]: uncertain.join(", ") }
1032
+ })
1033
+ })
1034
+ });
1035
+ }
1036
+ function digestOrUndefined(value) {
1037
+ try {
1038
+ return canonicalCandidateDigest(value);
1039
+ } catch {
1040
+ return;
1041
+ }
1042
+ }
1043
+ /**
1044
+ * Resolve a directory through symlinks. The fork's own directory may not exist yet; it resolves
1045
+ * through its nearest existing ancestor, so a symlinked ancestor cannot hide an overlap.
1046
+ */
1047
+ async function realDirectory(path, name, mayBeAbsent = false) {
1048
+ if (typeof path !== "string" || path.trim().length === 0) throw new ValidationError(`supervisePursuit fork: ${name} must be a non-empty string`);
1049
+ const absolute = resolve(path.trim());
1050
+ try {
1051
+ return await realpath(absolute);
1052
+ } catch (error) {
1053
+ const ancestor = dirname(absolute);
1054
+ if (!mayBeAbsent || !isNoEntError(error) || ancestor === absolute) throw error;
1055
+ return join(await realDirectory(ancestor, name, true), basename(absolute));
1056
+ }
1057
+ }
1058
+ /** Whether `inner` is `outer` or lies beneath it. */
1059
+ function contains(outer, inner) {
1060
+ const path = relative(outer, inner);
1061
+ return path === "" || !isAbsolute(path) && path !== ".." && !path.startsWith(`..${sep}`);
1062
+ }
1063
+ //#endregion
839
1064
  //#region src/durable/run-lock.ts
840
1065
  /** The lock file `supervisePursuit` holds inside a run directory for the life of one call. */
841
1066
  const RUN_DIRECTORY_LOCK_FILE = "supervise.lock";
@@ -1010,7 +1235,7 @@ async function withMutationGuard(path, mutate, waitForRelease = false) {
1010
1235
  break;
1011
1236
  } catch (cause) {
1012
1237
  if (waitForRelease && cause.code === "EEXIST" && performance.now() < deadline) {
1013
- await setTimeout(10);
1238
+ await setTimeout$1(10);
1014
1239
  continue;
1015
1240
  }
1016
1241
  throw new Error(`supervisePursuit: cannot mutate ${path}; inspect ${guard} and remove it only after confirming no lock mutation is active`, { cause });
@@ -1029,123 +1254,424 @@ async function removeIfPresent(path) {
1029
1254
  }
1030
1255
  }
1031
1256
  //#endregion
1032
- //#region src/durable/settle-record.ts
1033
- /** The settle record: the returned `SupervisedResult` as canonical JSON, written once. */
1034
- const SETTLE_RECORD_FILE = "result.json";
1035
- /** The failure record: the most recent throw, replaced by a later throw. */
1036
- const FAILURE_RECORD_FILE = "failure.json";
1037
- /** The directory already holds a settle record, so the run it records must not be re-entered. */
1038
- var SettledRunDirectoryError = class extends Error {
1039
- path;
1040
- /** The root of the recorded run, which is its `runId`. */
1041
- recordedRunId;
1042
- constructor(path, recordedRunId, requestedRunId) {
1043
- super(recordedRunId === requestedRunId ? `supervisePursuit: ${path} records that run '${recordedRunId}' already settled; a settled run is not re-entered` : `supervisePursuit: ${path} records that run '${recordedRunId}' already settled in this directory; a directory holds one settle record, so run '${requestedRunId}' needs its own runDir`);
1044
- this.name = "SettledRunDirectoryError";
1045
- this.path = path;
1046
- this.recordedRunId = recordedRunId;
1047
- }
1048
- };
1049
- /** A result member whose JSON form would misstate it. */
1050
- var UnrecordableSettleValueError = class extends TypeError {
1051
- path;
1052
- constructor(path, kind) {
1053
- super(`supervisePursuit: result${path} is a ${kind}, which JSON cannot record faithfully; a settle record holds plain data only`);
1054
- this.name = "UnrecordableSettleValueError";
1055
- this.path = path;
1056
- }
1057
- };
1058
- const PLAIN_PROTOTYPES = /* @__PURE__ */ new Set([
1059
- Object.prototype,
1060
- Array.prototype,
1061
- null
1062
- ]);
1063
- /**
1064
- * The JSON value of a result, or a throw naming the first member JSON would misstate. An
1065
- * `undefined` member and a function are dropped, exactly as any JSON consumer drops them; a Map,
1066
- * a Set, a Date, a typed array, or a class instance would be rewritten to `{}` or a string and
1067
- * so is refused.
1068
- */
1069
- function jsonValue(result) {
1070
- const seen = [];
1071
- const visit = (value, path) => {
1072
- if (value === null || typeof value !== "object") return;
1073
- if (value instanceof Map) throw new UnrecordableSettleValueError(path, "Map");
1074
- if (value instanceof Set) throw new UnrecordableSettleValueError(path, "Set");
1075
- if (value instanceof Date) throw new UnrecordableSettleValueError(path, "Date");
1076
- if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) throw new UnrecordableSettleValueError(path, "binary buffer");
1077
- if (!PLAIN_PROTOTYPES.has(Object.getPrototypeOf(value))) throw new UnrecordableSettleValueError(path, "class instance");
1078
- if (seen.includes(value)) throw new UnrecordableSettleValueError(path, "cycle");
1079
- seen.push(value);
1080
- if (Array.isArray(value)) for (const [index, item] of value.entries()) visit(item, `${path}[${index}]`);
1081
- else for (const [key, item] of Object.entries(value)) visit(item, `${path}.${key}`);
1082
- seen.pop();
1257
+ //#region src/durable/pursuit-versions.ts
1258
+ /** The longest delay Node's setTimeout honors. */
1259
+ const MAX_TIMER_MS = 2147483647;
1260
+ /** The ledger file inside the lineage directory. */
1261
+ const PURSUIT_VERSIONS_FILE = "versions.jsonl";
1262
+ /** The `<runDir>.v<n>` directory and `<runId>.v<n>` id of version `n`, beside the first. */
1263
+ function pursuitVersionRun(runDir, runId, version) {
1264
+ return version === 1 ? {
1265
+ runDir,
1266
+ runId
1267
+ } : {
1268
+ runDir: `${runDir}.v${version}`,
1269
+ runId: `${runId}.v${version}`
1083
1270
  };
1084
- visit(result, "");
1085
- return JSON.parse(JSON.stringify(result));
1086
1271
  }
1087
1272
  /**
1088
- * The exact bytes `result.json` holds for a result: its JSON value serialized as RFC 8785
1089
- * canonical JSON. Throws `UnrecordableSettleValueError` before any byte is written when the
1090
- * result carries a value JSON would misstate.
1273
+ * Validate a `versions` option before any compute. The same check runs inside `supervisePursuit`;
1274
+ * a caller that records the option as data can run it at preflight.
1091
1275
  */
1092
- function settleRecordJson(result) {
1093
- return canonicalCandidateJson(jsonValue(result));
1094
- }
1095
- /** Publish `runDir/result.json` once. Throws when a record is already there. */
1096
- async function writeSettleRecord(runDir, result) {
1097
- const path = resolve(runDir, SETTLE_RECORD_FILE);
1098
- if (!publishExclusiveDurableFile(path, settleRecordJson(result))) throw new Error(`supervisePursuit: ${path} already exists; a settle record is written once`);
1099
- return path;
1276
+ function assertPursuitVersions(versions) {
1277
+ const fail = (message) => {
1278
+ throw new ValidationError(`supervisePursuit versions: ${message}`);
1279
+ };
1280
+ if (typeof versions !== "object" || versions === null) fail("must be an object");
1281
+ const { judge, next, stop, run } = versions;
1282
+ if (typeof judge === "string") {
1283
+ if (judge.trim().length === 0) fail("judge must name a registry entry");
1284
+ } else if (typeof judge !== "object" || judge === null || typeof judge.judge !== "function") fail("judge must be a VersionJudge or a registry name");
1285
+ else if (!isSha256(judge.digest)) fail("judge.digest must be a sha256 digest (sha256:<64 hex>)");
1286
+ if (typeof next === "string" ? next.trim().length === 0 : typeof next !== "function") fail("next must be a function or a registry name");
1287
+ if (run !== void 0 && typeof run !== "function") fail("run must be a function when set");
1288
+ const { usd } = versions;
1289
+ if (usd !== void 0 && typeof usd !== "function") fail("usd must be a function when set");
1290
+ if (typeof stop !== "object" || stop === null) fail("stop must be an object");
1291
+ const rule = stop;
1292
+ const known = /* @__PURE__ */ new Set([
1293
+ "patience",
1294
+ "maxVersions",
1295
+ "maxUsd",
1296
+ "deadlineMs",
1297
+ "minImprovement"
1298
+ ]);
1299
+ const unknown = Object.keys(rule).filter((key) => !known.has(key));
1300
+ if (unknown.length > 0) fail(`stop has unknown fields: ${unknown.join(", ")}`);
1301
+ for (const key of ["patience", "maxVersions"]) if (!Number.isInteger(rule[key]) || rule[key] < 1) fail(`stop.${key} must be an integer of at least 1`);
1302
+ for (const key of ["maxUsd", "deadlineMs"]) if (typeof rule[key] !== "number" || !Number.isFinite(rule[key]) || rule[key] <= 0) fail(`stop.${key} must be a finite number above 0`);
1303
+ if (rule.minImprovement !== void 0 && (typeof rule.minImprovement !== "number" || !Number.isFinite(rule.minImprovement) || rule.minImprovement < 0)) fail("stop.minImprovement must be a finite number of at least 0");
1100
1304
  }
1101
- /** Replace `runDir/failure.json` with the most recent throw. */
1102
- async function writeFailureRecord(runDir, record) {
1103
- const path = resolve(runDir, FAILURE_RECORD_FILE);
1104
- writeAtomicDurableFile(path, `${JSON.stringify(record, null, 2)}\n`);
1105
- return path;
1305
+ /** Run a version chain. `supervisePursuit` calls this when `versions` is set. */
1306
+ async function runPursuitVersions(profile, task, opts, runOne) {
1307
+ const { versions, ...rest } = opts;
1308
+ assertPursuitVersions(versions);
1309
+ for (const key of [
1310
+ "journal",
1311
+ "blobs",
1312
+ "rootHandle",
1313
+ "steerDir"
1314
+ ]) if (rest[key] !== void 0) throw new ValidationError(`supervisePursuit versions: ${key} names one run, and a version chain has one per version; omit it`);
1315
+ const registry = rest.registry;
1316
+ const judge = resolveNamed("judge", "versionJudges", versions.judge, registry?.versionJudges);
1317
+ if (!isSha256(judge.digest)) throw new ValidationError("supervisePursuit versions: judge.digest must be a sha256 digest");
1318
+ const next = resolveNamed("next", "nextVersions", versions.next, registry?.nextVersions);
1319
+ const stop = versions.stop;
1320
+ const minImprovement = stop.minImprovement ?? 0;
1321
+ const now = rest.now ?? Date.now;
1322
+ const pursuitId = rest.pursuitId.trim();
1323
+ const runDir = resolve(rest.runDir.trim());
1324
+ const runId = rest.runId ?? "supervise";
1325
+ const { fork: firstFork, signal: callerSignal, ...base } = rest;
1326
+ const lineageDir = `${runDir}.versions`;
1327
+ const ledgerPath = resolve(lineageDir, PURSUIT_VERSIONS_FILE);
1328
+ const firstProfile = firstFork === void 0 ? profile : applyExactAgentProfileDiff(profile, firstFork.change, "supervisePursuit versions");
1329
+ await mkdir(lineageDir, { recursive: true });
1330
+ const lock = await acquireRunDirectoryLock(lineageDir, `${runId}:versions`, now);
1331
+ try {
1332
+ const ledger = await readLedger(ledgerPath);
1333
+ let chain = ledger.find((line) => line.kind === "chain");
1334
+ if (chain === void 0) {
1335
+ const elapsed = (await readSettleRecord(runDir))?.spentTotal?.ms;
1336
+ const startedAt = new Date(now() - (typeof elapsed === "number" && Number.isFinite(elapsed) ? elapsed : 0)).toISOString();
1337
+ chain = {
1338
+ kind: "chain",
1339
+ pursuitId,
1340
+ runId,
1341
+ runDir,
1342
+ judgeDigest: judge.digest,
1343
+ stop: canonicalStop(stop),
1344
+ startedAt
1345
+ };
1346
+ await appendLedger(ledgerPath, chain);
1347
+ } else {
1348
+ const mismatch = [
1349
+ chain.pursuitId !== pursuitId ? `pursuitId '${chain.pursuitId}'` : void 0,
1350
+ chain.runId !== runId ? `runId '${chain.runId}'` : void 0,
1351
+ chain.runDir !== runDir ? `runDir '${chain.runDir}'` : void 0,
1352
+ chain.judgeDigest !== judge.digest ? `judge digest ${chain.judgeDigest}` : void 0,
1353
+ contentAddress(chain.stop) !== contentAddress(canonicalStop(stop)) ? "another stop rule" : void 0
1354
+ ].filter((item) => item !== void 0);
1355
+ if (mismatch.length > 0) throw new ValidationError(`supervisePursuit versions: ${ledgerPath} records a chain with ${mismatch.join(", ")}; a chain keeps its identity, judge and stop rule`);
1356
+ }
1357
+ const startedAtMs = Date.parse(chain.startedAt);
1358
+ const deadline = new AbortController();
1359
+ let timer;
1360
+ const arm = () => {
1361
+ const remaining = startedAtMs + stop.deadlineMs - now();
1362
+ if (remaining <= 0) {
1363
+ deadline.abort(/* @__PURE__ */ new Error("the version chain reached its deadline"));
1364
+ return;
1365
+ }
1366
+ timer = setTimeout(arm, Math.min(remaining, MAX_TIMER_MS));
1367
+ timer.unref?.();
1368
+ };
1369
+ arm();
1370
+ const signal = callerSignal === void 0 ? deadline.signal : AbortSignal.any([callerSignal, deadline.signal]);
1371
+ try {
1372
+ const judged = [];
1373
+ const profiles = /* @__PURE__ */ new Map([[1, firstProfile]]);
1374
+ let pending = {
1375
+ version: 1,
1376
+ runId,
1377
+ runDir
1378
+ };
1379
+ let stopped;
1380
+ for (const line of ledger) if (line.kind === "start") {
1381
+ const parentProfile = profiles.get(line.parent.version);
1382
+ if (parentProfile === void 0) throw new RuntimeRunStateError(`supervisePursuit versions: ${ledgerPath} starts version ${line.version} from unknown version ${line.parent.version}`);
1383
+ profiles.set(line.version, applyExactAgentProfileDiff(parentProfile, line.change, "supervisePursuit versions"));
1384
+ pending = {
1385
+ version: line.version,
1386
+ runId: line.runId,
1387
+ runDir: line.runDir,
1388
+ parent: line.parent,
1389
+ change: line.change
1390
+ };
1391
+ } else if (line.kind === "judged") {
1392
+ if (line.version !== judged.length + 1 || line.version !== pending?.version) throw new RuntimeRunStateError(`supervisePursuit versions: ${ledgerPath} judges version ${line.version} out of order`);
1393
+ if (line.verdict.judgeDigest !== judge.digest) throw new ValidationError(`supervisePursuit versions: version ${line.version} was judged under ${line.verdict.judgeDigest}, not ${judge.digest}`);
1394
+ const settled = await readSettled(pending, profiles);
1395
+ if (settled.settleDigest !== line.settleDigest) throw new RuntimeRunStateError(`supervisePursuit versions: version ${line.version}'s result.json changed after it was judged`);
1396
+ judged.push(Object.freeze({
1397
+ ...settled,
1398
+ verdict: line.verdict,
1399
+ improved: line.improved,
1400
+ usd: line.usd,
1401
+ usdKnown: line.usdKnown,
1402
+ usdSource: line.usdSource,
1403
+ ...line.lineage === void 0 ? {} : { lineage: line.lineage }
1404
+ }));
1405
+ pending = void 0;
1406
+ } else if (line.kind === "stopped") stopped = line;
1407
+ while (stopped === void 0) {
1408
+ if (pending !== void 0) {
1409
+ const spec = pending;
1410
+ const settled = await settle(spec);
1411
+ const verdict = await judgeVersion(settled);
1412
+ const bestScore = bestOf(judged)?.verdict.score;
1413
+ const improved = isFiniteNumber(verdict.score) && (!isFiniteNumber(bestScore) || verdict.score > bestScore + minImprovement);
1414
+ const spent = settled.result.spentTotal;
1415
+ const measured = versions.usd === void 0 ? void 0 : await versions.usd(settled, signal);
1416
+ const usdSource = measured === void 0 ? "runtime" : "caller";
1417
+ const usd = measured === void 0 ? isFiniteNumber(spent?.usd) ? spent.usd : null : isFiniteNumber(measured) ? measured : null;
1418
+ const usdKnown = usd !== null && (measured !== void 0 || spent?.usdKnown !== false);
1419
+ const lineage = versionLineage(spec, firstFork);
1420
+ await appendLedger(ledgerPath, {
1421
+ kind: "judged",
1422
+ version: spec.version,
1423
+ runId: spec.runId,
1424
+ runDir: spec.runDir,
1425
+ settleDigest: settled.settleDigest,
1426
+ ...spec.parent === void 0 ? {} : { parent: spec.parent },
1427
+ ...spec.change?.id === void 0 ? {} : { changeId: spec.change.id },
1428
+ verdict,
1429
+ improved,
1430
+ usd,
1431
+ usdKnown,
1432
+ usdSource,
1433
+ ...lineage === void 0 ? {} : { lineage }
1434
+ });
1435
+ judged.push(Object.freeze({
1436
+ ...settled,
1437
+ verdict,
1438
+ improved,
1439
+ usd,
1440
+ usdKnown,
1441
+ usdSource,
1442
+ ...lineage === void 0 ? {} : { lineage }
1443
+ }));
1444
+ pending = void 0;
1445
+ }
1446
+ const reason = stopReason(judged);
1447
+ if (reason !== void 0) {
1448
+ stopped = {
1449
+ kind: "stopped",
1450
+ reason,
1451
+ at: new Date(now()).toISOString(),
1452
+ best: (bestOf(judged) ?? judged[0]).version,
1453
+ spentUsd: spentOf(judged)
1454
+ };
1455
+ await appendLedger(ledgerPath, stopped);
1456
+ break;
1457
+ }
1458
+ const best = bestOf(judged) ?? judged[0];
1459
+ const last = judged[judged.length - 1];
1460
+ const change = await next({
1461
+ best,
1462
+ last,
1463
+ versions: Object.freeze([...judged])
1464
+ }, signal);
1465
+ if (typeof change?.id !== "string" || change.id.trim().length === 0) throw new ValidationError("supervisePursuit versions: next must return a change with an id");
1466
+ const version = judged.length + 1;
1467
+ const spec = {
1468
+ version,
1469
+ ...pursuitVersionRun(runDir, runId, version),
1470
+ parent: {
1471
+ version: best.version,
1472
+ runId: best.runId,
1473
+ settleDigest: best.settleDigest
1474
+ },
1475
+ change
1476
+ };
1477
+ profiles.set(version, applyExactAgentProfileDiff(best.profile, change, "supervisePursuit versions"));
1478
+ await appendLedger(ledgerPath, {
1479
+ kind: "start",
1480
+ version,
1481
+ runId: spec.runId,
1482
+ runDir: spec.runDir,
1483
+ parent: spec.parent,
1484
+ change
1485
+ });
1486
+ pending = spec;
1487
+ }
1488
+ const bestVersion = judged.find((item) => item.version === stopped.best) ?? judged[0];
1489
+ const record = Object.freeze({
1490
+ lineageDir,
1491
+ judgeDigest: judge.digest,
1492
+ stop: canonicalStop(stop),
1493
+ startedAt: chain.startedAt,
1494
+ versions: Object.freeze([...judged]),
1495
+ best: bestVersion.version,
1496
+ spentUsd: stopped.spentUsd,
1497
+ stopped: Object.freeze({
1498
+ reason: stopped.reason,
1499
+ at: stopped.at
1500
+ })
1501
+ });
1502
+ const observerPath = resolve(bestVersion.runDir, "observer.jsonl");
1503
+ return Object.freeze({
1504
+ result: bestVersion.result,
1505
+ pursuit: projectPursuit(await new FileObserverJournal(observerPath, pursuitId).read()),
1506
+ observerPath,
1507
+ settlePath: resolve(bestVersion.runDir, SETTLE_RECORD_FILE),
1508
+ versions: record
1509
+ });
1510
+ /** Run the version unless its directory already settled, then read it back. */
1511
+ async function settle(spec) {
1512
+ if (await readSettleRecord(spec.runDir) === void 0) {
1513
+ if (signal.aborted) throw new RuntimeRunStateError(`supervisePursuit versions: version ${spec.version} was not started: ${abortMessage(signal)}`);
1514
+ if (spec.parent === void 0 || spec.change === void 0) await runOne(profile, task, {
1515
+ ...base,
1516
+ ...firstFork === void 0 ? {} : { fork: firstFork },
1517
+ signal
1518
+ });
1519
+ else {
1520
+ const parent = judged.find((item) => item.version === spec.parent.version);
1521
+ if (parent === void 0) throw new RuntimeRunStateError(`supervisePursuit versions: version ${spec.version}'s parent ${spec.parent.version} is not judged`);
1522
+ const fork = {
1523
+ runDir: parent.runDir,
1524
+ settleDigest: parent.settleDigest,
1525
+ change: spec.change,
1526
+ acceptUncertain: true
1527
+ };
1528
+ const options = {
1529
+ ...base,
1530
+ runId: spec.runId,
1531
+ runDir: spec.runDir,
1532
+ fork,
1533
+ signal
1534
+ };
1535
+ if (versions.run === void 0) await runOne(parent.profile, task, options);
1536
+ else {
1537
+ const prepared = await prepareRunFork(parent.profile, task, options, fork);
1538
+ await versions.run(Object.freeze({
1539
+ version: spec.version,
1540
+ runId: spec.runId,
1541
+ runDir: spec.runDir,
1542
+ pursuitId,
1543
+ profile: prepared.profile,
1544
+ task,
1545
+ budget: base.budget,
1546
+ execution: prepared.execution,
1547
+ fork,
1548
+ parentProfile: parent.profile
1549
+ }), signal);
1550
+ }
1551
+ }
1552
+ }
1553
+ const settled = await readSettled(spec, profiles);
1554
+ if (settled === void 0) throw new RuntimeRunStateError(`supervisePursuit versions: version ${spec.version} returned without a settle record in ${spec.runDir}`);
1555
+ return settled;
1556
+ }
1557
+ async function judgeVersion(settled) {
1558
+ const verdict = await judge.judge(settled, signal);
1559
+ if (typeof verdict !== "object" || verdict === null) throw new ValidationError("supervisePursuit versions: the judge returned no verdict");
1560
+ if (verdict.judgeDigest !== judge.digest) throw new ValidationError(`supervisePursuit versions: the verdict for version ${settled.version} carries ${String(verdict.judgeDigest)}, not the pinned judge ${judge.digest}`);
1561
+ if (verdict.score !== null && !isFiniteNumber(verdict.score)) throw new ValidationError("supervisePursuit versions: a verdict score must be a finite number or null");
1562
+ return JSON.parse(JSON.stringify(verdict));
1563
+ }
1564
+ function stopReason(done) {
1565
+ if (callerSignal?.aborted) return "aborted";
1566
+ if (done.length >= stop.maxVersions) return "max-versions";
1567
+ let stale = 0;
1568
+ for (let index = done.length - 1; index >= 0 && !done[index].improved; index -= 1) stale += 1;
1569
+ if (stale >= stop.patience) return "no-improvement";
1570
+ if (done.some((item) => item.usd === null)) return "spend-unknown";
1571
+ if (spentOf(done) >= stop.maxUsd) return "max-usd";
1572
+ if (deadline.signal.aborted || now() - startedAtMs >= stop.deadlineMs) return "deadline";
1573
+ }
1574
+ } finally {
1575
+ if (timer !== void 0) clearTimeout(timer);
1576
+ }
1577
+ } finally {
1578
+ await lock.release();
1579
+ }
1106
1580
  }
1107
- /**
1108
- * Read the settle record a run directory holds, or `undefined` when it holds none. A file that
1109
- * is present but is not a settle record is corruption and fails loud.
1110
- */
1111
- async function readSettleRecord(runDir) {
1112
- const path = resolve(runDir, SETTLE_RECORD_FILE);
1113
- let text;
1581
+ async function readSettled(spec, profiles) {
1582
+ let bytes;
1114
1583
  try {
1115
- text = await readFile(path, "utf8");
1584
+ bytes = await readFile(resolve(spec.runDir, SETTLE_RECORD_FILE));
1116
1585
  } catch (error) {
1117
- if (isNoEntError(error)) return void 0;
1118
- throw error;
1586
+ if (!isNoEntError(error)) throw error;
1587
+ throw new RuntimeRunStateError(`supervisePursuit versions: version ${spec.version} has no settle record in ${spec.runDir}`);
1119
1588
  }
1120
- let parsed;
1121
- try {
1122
- parsed = JSON.parse(text);
1123
- } catch (cause) {
1124
- throw new Error(`supervisePursuit: ${path} is not valid JSON`, { cause });
1589
+ const result = await readSettleRecord(spec.runDir);
1590
+ const profile = profiles.get(spec.version);
1591
+ if (profile === void 0) throw new RuntimeRunStateError(`supervisePursuit versions: version ${spec.version} has no recorded profile`);
1592
+ return Object.freeze({
1593
+ version: spec.version,
1594
+ runId: spec.runId,
1595
+ runDir: spec.runDir,
1596
+ settleDigest: sha256Bytes(bytes),
1597
+ result,
1598
+ profile,
1599
+ ...spec.parent === void 0 ? {} : { parent: spec.parent },
1600
+ ...spec.change === void 0 ? {} : { change: spec.change }
1601
+ });
1602
+ }
1603
+ function versionLineage(spec, firstFork) {
1604
+ if (spec.parent !== void 0 && spec.change !== void 0) return {
1605
+ source: lineageSource(spec.change),
1606
+ runIds: [spec.parent.runId],
1607
+ profileDiffIds: [spec.change.id]
1608
+ };
1609
+ if (firstFork !== void 0) return {
1610
+ source: lineageSource(firstFork.change),
1611
+ profileDiffIds: [firstFork.change.id]
1612
+ };
1613
+ }
1614
+ /** The change's author, in the lineage's words. A trace-derived change is a compound of runs. */
1615
+ function lineageSource(change) {
1616
+ const kind = change.source?.kind;
1617
+ return kind === "human" || kind === "optimizer" || kind === "frontier-author" ? kind : "compound";
1618
+ }
1619
+ function bestOf(done) {
1620
+ let best;
1621
+ for (const item of done) {
1622
+ if (!isFiniteNumber(item.verdict.score)) continue;
1623
+ if (best === void 0 || item.verdict.score > best.verdict.score) best = item;
1125
1624
  }
1126
- const record = parsed;
1127
- if (typeof record !== "object" || record === null || typeof record.kind !== "string" || typeof record.tree !== "object" || record.tree === null || typeof record.tree.root !== "string") throw new Error(`supervisePursuit: ${path} is not a settle record (no kind or tree.root)`);
1128
- return parsed;
1625
+ return best;
1129
1626
  }
1130
- /** Read the most recent failure record, or `undefined` when the directory holds none. */
1131
- async function readFailureRecord(runDir) {
1132
- const path = resolve(runDir, FAILURE_RECORD_FILE);
1627
+ function spentOf(done) {
1628
+ return done.reduce((sum, item) => sum + (item.usd ?? 0), 0);
1629
+ }
1630
+ function canonicalStop(stop) {
1631
+ return {
1632
+ patience: stop.patience,
1633
+ maxVersions: stop.maxVersions,
1634
+ maxUsd: stop.maxUsd,
1635
+ deadlineMs: stop.deadlineMs,
1636
+ ...stop.minImprovement === void 0 ? {} : { minImprovement: stop.minImprovement }
1637
+ };
1638
+ }
1639
+ function resolveNamed(option, table, value, registry) {
1640
+ if (typeof value !== "string") return value;
1641
+ if (registry === void 0) throw new ConfigError(`supervisePursuit versions: ${option} = ${JSON.stringify(value)} names a registry entry, but no registry.${table} was provided`);
1642
+ const entry = registry.resolve(value);
1643
+ if (entry === void 0) throw new ConfigError(`supervisePursuit versions: ${option} = ${JSON.stringify(value)} is not in registry.${table}`);
1644
+ return entry;
1645
+ }
1646
+ async function readLedger(path) {
1133
1647
  let text;
1134
1648
  try {
1135
1649
  text = await readFile(path, "utf8");
1136
1650
  } catch (error) {
1137
- if (isNoEntError(error)) return void 0;
1651
+ if (isNoEntError(error)) return [];
1138
1652
  throw error;
1139
1653
  }
1140
- let parsed;
1654
+ return parseCommittedJsonLines(text, path);
1655
+ }
1656
+ async function appendLedger(path, line) {
1657
+ const endsWithRecord = await prepareJsonlAppend(path);
1658
+ const handle = await open(path, "a");
1141
1659
  try {
1142
- parsed = JSON.parse(text);
1143
- } catch (cause) {
1144
- throw new Error(`supervisePursuit: ${path} is not valid JSON`, { cause });
1660
+ await writeAllBytes(handle, `${endsWithRecord ? "\n" : ""}${JSON.stringify(line)}\n`);
1661
+ await handle.sync();
1662
+ } finally {
1663
+ await handle.close();
1145
1664
  }
1146
- const record = parsed;
1147
- if (typeof record !== "object" || record === null || typeof record.runId !== "string" || typeof record.pursuitId !== "string" || typeof record.at !== "string" || typeof record.error?.name !== "string" || typeof record.error.message !== "string" || record.rootStream !== void 0 && (typeof record.rootStream.ref !== "string" || typeof record.rootStream.events !== "number")) throw new Error(`supervisePursuit: ${path} is not a failure record`);
1148
- return parsed;
1665
+ }
1666
+ function isSha256(value) {
1667
+ return typeof value === "string" && /^sha256:[0-9a-f]{64}$/u.test(value);
1668
+ }
1669
+ function isFiniteNumber(value) {
1670
+ return typeof value === "number" && Number.isFinite(value);
1671
+ }
1672
+ function abortMessage(signal) {
1673
+ const reason = signal.reason;
1674
+ return reason instanceof Error ? reason.message : String(reason ?? "aborted");
1149
1675
  }
1150
1676
  //#endregion
1151
1677
  //#region src/durable/supervise-pursuit.ts
@@ -1189,12 +1715,14 @@ async function supervisePursuit(profile, task, opts) {
1189
1715
  const runDir = opts.runDir.trim();
1190
1716
  if (runDir.length === 0) throw new TypeError("supervisePursuit: runDir must be non-empty");
1191
1717
  if (opts.retainedAtSettlement === "keep") throw new TypeError("supervisePursuit: retainedAtSettlement 'keep' cannot hold, because a settled pursuit is never re-entered");
1718
+ if (opts.versions !== void 0) return runPursuitVersions(profile, task, opts, supervisePursuit);
1192
1719
  const observerPath = resolve(runDir, "observer.jsonl");
1193
1720
  const settlePath = resolve(runDir, SETTLE_RECORD_FILE);
1194
1721
  const failurePath = resolve(runDir, FAILURE_RECORD_FILE);
1195
- const { pursuitId: _pursuitId, hooks, ...superviseOptions } = opts;
1722
+ const { pursuitId: _pursuitId, hooks, fork, versions: _versions, ...superviseOptions } = opts;
1196
1723
  const runId = superviseOptions.runId ?? "supervise";
1197
1724
  const now = superviseOptions.now ?? Date.now;
1725
+ const forked = fork === void 0 ? void 0 : await prepareRunFork(profile, task, superviseOptions, fork);
1198
1726
  const lock = await acquireRunDirectoryLock(runDir, runId, now);
1199
1727
  try {
1200
1728
  const settled = await readSettleRecord(runDir);
@@ -1203,8 +1731,9 @@ async function supervisePursuit(profile, task, opts) {
1203
1731
  await observer.journal.appendEvent(rootEvent(pursuitId, runId, "before", now()));
1204
1732
  let result;
1205
1733
  try {
1206
- result = await supervise(profile, task, {
1734
+ result = await supervise(forked?.profile ?? profile, task, {
1207
1735
  ...superviseOptions,
1736
+ ...forked === void 0 ? {} : { execution: forked.execution },
1208
1737
  steerDir: superviseOptions.steerDir ?? supervisorRunDir(runDir, runId),
1209
1738
  retainedAtSettlement: "release",
1210
1739
  hooks: withPursuitContext(pursuitId, composeRuntimeHooks(observer.hooks, hooks))
@@ -1343,6 +1872,6 @@ function compareText(left, right) {
1343
1872
  return left < right ? -1 : left > right ? 1 : 0;
1344
1873
  }
1345
1874
  //#endregion
1346
- export { FAILURE_RECORD_FILE, FileObserverJournal, ROOT_STREAM_FILE, RUN_DIRECTORY_LOCK_FILE, RunDirectoryLockedError, SETTLE_RECORD_FILE, SettledRunDirectoryError, SupervisePursuitError, acquireRunDirectoryLock, createFileObserverHooks, deriveExecutionId, discoverDurableSupervisionRun, handleChatTurn, observerRecordDigest, projectPursuit, readFailureRecord, readRootStream, readRootStreamReceipt, readRunDirectoryLock, readSettleRecord, runDirectoryHolderIsLive, settleRecordJson, supervisePursuit, verifyObserverRecords };
1875
+ export { FAILURE_RECORD_FILE, FORK_PARENT_UNCERTAIN_NODES_KEY, FileObserverJournal, PURSUIT_VERSIONS_FILE, ROOT_STREAM_FILE, RUN_DIRECTORY_LOCK_FILE, RUN_FORK_CORRELATION_KEYS, RunDirectoryLockedError, SETTLE_RECORD_FILE, SettledRunDirectoryError, SupervisePursuitError, acquireRunDirectoryLock, assertPursuitVersions, createFileObserverHooks, deriveExecutionId, discoverDurableSupervisionRun, handleChatTurn, observerRecordDigest, projectPursuit, pursuitVersionRun, readFailureRecord, readRootStream, readRootStreamReceipt, readRunDirectoryLock, readSettleRecord, runDirectoryHolderIsLive, settleRecordJson, supervisePursuit, verifyObserverRecords };
1347
1876
 
1348
1877
  //# sourceMappingURL=durable.js.map