@deepthonk/core 0.1.3 → 0.1.5

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.d.ts CHANGED
@@ -7,8 +7,10 @@ export * from "./errors.js";
7
7
  export * from "./json.js";
8
8
  export * from "./lifecycle.js";
9
9
  export * from "./pairScheduler.js";
10
+ export * from "./phaseRunner.js";
10
11
  export * from "./prompts.js";
11
12
  export * from "./rng.js";
13
+ export { assertNoLegacyRedactedBudget, repairLegacyBudgetConfig } from "./resume.js";
12
14
  export * from "./runner.js";
13
15
  export * from "./schemas.js";
14
16
  export * from "./services.js";
package/dist/index.js CHANGED
@@ -7,8 +7,10 @@ export * from "./errors.js";
7
7
  export * from "./json.js";
8
8
  export * from "./lifecycle.js";
9
9
  export * from "./pairScheduler.js";
10
+ export * from "./phaseRunner.js";
10
11
  export * from "./prompts.js";
11
12
  export * from "./rng.js";
13
+ export { assertNoLegacyRedactedBudget, repairLegacyBudgetConfig } from "./resume.js";
12
14
  export * from "./runner.js";
13
15
  export * from "./schemas.js";
14
16
  export * from "./services.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,4BAA4B,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACrF,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC"}
@@ -27,8 +27,37 @@ export interface RunStatus {
27
27
  fix?: string;
28
28
  };
29
29
  }
30
+ export interface RunLockData {
31
+ schema_version: 1;
32
+ claim_id: string;
33
+ job_id?: string;
34
+ hostname: string;
35
+ worker_pid: number;
36
+ claimed_at: string;
37
+ }
38
+ export interface RunLockClaim {
39
+ claimId: string;
40
+ fingerprint: string;
41
+ lock: RunLockData;
42
+ }
43
+ export type RunLockInspection = {
44
+ state: "missing";
45
+ } | {
46
+ state: "valid";
47
+ fingerprint: string;
48
+ lock: RunLockData;
49
+ sameHost: boolean;
50
+ workerAlive: boolean;
51
+ } | {
52
+ state: "malformed";
53
+ fingerprint: string;
54
+ };
30
55
  export declare function claimRunLock(runDir: string, jobId?: string): Promise<boolean>;
31
- export declare function releaseRunLock(runDir: string): Promise<void>;
56
+ export declare function claimRunLockOwnership(runDir: string, jobId?: string): Promise<RunLockClaim | undefined>;
57
+ export declare function inspectRunLock(runDir: string): Promise<RunLockInspection>;
58
+ export declare function reclaimRunLock(runDir: string, expectedFingerprint: string): Promise<boolean>;
59
+ export declare function verifyRunLockClaim(runDir: string, claim: RunLockClaim, jobId?: string): Promise<boolean>;
60
+ export declare function releaseRunLock(runDir: string, claimId?: string): Promise<void>;
32
61
  export declare function readRunStatus(runDir: string): Promise<RunStatus | undefined>;
33
62
  export declare function requestRunCancel(runDir: string): Promise<void>;
34
63
  export declare function isRunCancelRequested(runDir: string): Promise<boolean>;
@@ -62,4 +91,6 @@ export interface UsageRecord {
62
91
  total_usd?: number;
63
92
  latency_ms?: number;
64
93
  retry_count?: number;
94
+ outcome?: "success" | "failed";
95
+ error_code?: string;
65
96
  }
package/dist/lifecycle.js CHANGED
@@ -1,36 +1,237 @@
1
+ import { createHash, randomUUID } from "node:crypto";
1
2
  import { mkdir, readFile, unlink, writeFile } from "node:fs/promises";
3
+ import { hostname } from "node:os";
2
4
  import { join } from "node:path";
3
5
  import { runArtifactFiles } from "./artifacts.js";
4
6
  import { ConfigError } from "./errors.js";
7
+ const localClaims = new Map();
8
+ const runLockGuardFile = ".run.lock.guard";
9
+ const lockWaitTimeoutMs = 1_000;
5
10
  export async function claimRunLock(runDir, jobId) {
11
+ return Boolean(await claimRunLockOwnership(runDir, jobId));
12
+ }
13
+ export async function claimRunLockOwnership(runDir, jobId) {
6
14
  await mkdir(runDir, { recursive: true });
15
+ const path = join(runDir, runArtifactFiles.lock);
16
+ const guard = await acquireRunLockGuard(runDir);
7
17
  try {
8
- await writeFile(join(runDir, runArtifactFiles.lock), `${JSON.stringify({ job_id: jobId, worker_pid: process.pid, claimed_at: new Date().toISOString() }, null, 2)}\n`, { encoding: "utf8", flag: "wx" });
9
- return true;
18
+ const inspection = await inspectRunLock(runDir);
19
+ if (inspection.state !== "missing") {
20
+ if (inspection.state !== "valid" || !inspection.sameHost || inspection.workerAlive)
21
+ return undefined;
22
+ const current = await readFile(path, "utf8").catch(() => undefined);
23
+ if (current === undefined || lockFingerprint(current) !== inspection.fingerprint)
24
+ return undefined;
25
+ await unlink(path);
26
+ localClaims.delete(path);
27
+ }
28
+ const lock = {
29
+ schema_version: 1,
30
+ claim_id: randomUUID(),
31
+ job_id: jobId,
32
+ hostname: hostname(),
33
+ worker_pid: process.pid,
34
+ claimed_at: new Date().toISOString()
35
+ };
36
+ const raw = `${JSON.stringify(lock, null, 2)}\n`;
37
+ await writeFile(path, raw, { encoding: "utf8", flag: "wx" });
38
+ const claim = { claimId: lock.claim_id, fingerprint: lockFingerprint(raw), lock };
39
+ localClaims.set(path, claim.claimId);
40
+ return claim;
10
41
  }
11
42
  catch (error) {
12
- if (error.code === "EEXIST")
13
- return false;
43
+ if (error instanceof ConfigError)
44
+ throw error;
14
45
  throw new ConfigError(`Could not claim run directory lock: ${error.message}`, {
15
46
  code: "run.lock_failed",
16
47
  retryable: false,
17
48
  fix: "Choose a writable run directory."
18
49
  });
19
50
  }
51
+ finally {
52
+ await releaseRunLockGuard(guard);
53
+ }
54
+ }
55
+ export async function inspectRunLock(runDir) {
56
+ const path = join(runDir, runArtifactFiles.lock);
57
+ let raw;
58
+ try {
59
+ raw = await readFile(path, "utf8");
60
+ }
61
+ catch (error) {
62
+ if (error.code === "ENOENT")
63
+ return { state: "missing" };
64
+ throw new ConfigError(`Could not inspect run directory lock: ${error.message}`, {
65
+ code: "run.lock_inspect_failed",
66
+ retryable: false,
67
+ fix: "Inspect the run directory lock file and permissions."
68
+ });
69
+ }
70
+ const fingerprint = lockFingerprint(raw);
71
+ try {
72
+ const value = JSON.parse(raw);
73
+ if (value.schema_version !== 1 ||
74
+ typeof value.claim_id !== "string" ||
75
+ typeof value.hostname !== "string" ||
76
+ typeof value.worker_pid !== "number" ||
77
+ !Number.isInteger(value.worker_pid) ||
78
+ typeof value.claimed_at !== "string") {
79
+ return { state: "malformed", fingerprint };
80
+ }
81
+ const lock = value;
82
+ const sameHost = lock.hostname === hostname();
83
+ return { state: "valid", fingerprint, lock, sameHost, workerAlive: sameHost ? isLivePid(lock.worker_pid) : true };
84
+ }
85
+ catch {
86
+ return { state: "malformed", fingerprint };
87
+ }
20
88
  }
21
- export async function releaseRunLock(runDir) {
89
+ export async function reclaimRunLock(runDir, expectedFingerprint) {
90
+ const path = join(runDir, runArtifactFiles.lock);
91
+ const guard = await acquireRunLockGuard(runDir);
22
92
  try {
23
- await unlink(join(runDir, runArtifactFiles.lock));
93
+ const raw = await readFile(path, "utf8");
94
+ if (lockFingerprint(raw) !== expectedFingerprint)
95
+ return false;
96
+ await unlink(path);
97
+ localClaims.delete(path);
98
+ return true;
24
99
  }
25
100
  catch (error) {
26
101
  if (error.code === "ENOENT")
102
+ return false;
103
+ throw error;
104
+ }
105
+ finally {
106
+ await releaseRunLockGuard(guard);
107
+ }
108
+ }
109
+ export async function verifyRunLockClaim(runDir, claim, jobId) {
110
+ const path = join(runDir, runArtifactFiles.lock);
111
+ if (localClaims.get(path) !== claim.claimId)
112
+ return false;
113
+ const guard = await acquireRunLockGuard(runDir);
114
+ try {
115
+ const inspection = await inspectRunLock(runDir);
116
+ return (inspection.state === "valid" &&
117
+ inspection.fingerprint === claim.fingerprint &&
118
+ inspection.lock.claim_id === claim.claimId &&
119
+ inspection.lock.job_id === jobId);
120
+ }
121
+ finally {
122
+ await releaseRunLockGuard(guard);
123
+ }
124
+ }
125
+ export async function releaseRunLock(runDir, claimId) {
126
+ const path = join(runDir, runArtifactFiles.lock);
127
+ const expectedClaimId = claimId ?? localClaims.get(path);
128
+ const guard = await acquireRunLockGuard(runDir);
129
+ try {
130
+ const inspection = await inspectRunLock(runDir);
131
+ if (inspection.state === "missing") {
132
+ localClaims.delete(path);
27
133
  return;
134
+ }
135
+ if (inspection.state !== "valid" || !expectedClaimId || inspection.lock.claim_id !== expectedClaimId) {
136
+ throw new ConfigError("Refusing to release a run lock not owned by this claimant.", {
137
+ code: "run.lock_not_owned",
138
+ retryable: false,
139
+ fix: "Inspect the current lock and use fingerprinted reclaim only after verifying that its owner is no longer active."
140
+ });
141
+ }
142
+ await unlink(path);
143
+ localClaims.delete(path);
144
+ }
145
+ catch (error) {
146
+ if (error instanceof ConfigError)
147
+ throw error;
28
148
  throw new ConfigError(`Could not release run directory lock: ${error.message}`, {
29
149
  code: "run.lock_release_failed",
30
150
  retryable: false,
31
151
  fix: "Inspect the run directory lock file and permissions."
32
152
  });
33
153
  }
154
+ finally {
155
+ await releaseRunLockGuard(guard);
156
+ }
157
+ }
158
+ async function acquireRunLockGuard(runDir) {
159
+ await mkdir(runDir, { recursive: true });
160
+ const path = join(runDir, runLockGuardFile);
161
+ const deadline = Date.now() + lockWaitTimeoutMs;
162
+ let blockingFingerprint;
163
+ while (true) {
164
+ const owner = {
165
+ schema_version: 1,
166
+ claim_id: randomUUID(),
167
+ job_id: "run-lock-guard",
168
+ hostname: hostname(),
169
+ worker_pid: process.pid,
170
+ claimed_at: new Date().toISOString()
171
+ };
172
+ const raw = `${JSON.stringify(owner)}\n`;
173
+ try {
174
+ await writeFile(path, raw, { encoding: "utf8", flag: "wx" });
175
+ return { path, claimId: owner.claim_id, fingerprint: lockFingerprint(raw) };
176
+ }
177
+ catch (error) {
178
+ if (error.code !== "EEXIST")
179
+ throw error;
180
+ const observed = await readFile(path, "utf8").catch(() => undefined);
181
+ if (observed)
182
+ blockingFingerprint = lockFingerprint(observed);
183
+ if (Date.now() >= deadline) {
184
+ throw new ConfigError(`Timed out waiting for the run-lock guard${blockingFingerprint ? ` (${blockingFingerprint})` : ""}.`, {
185
+ code: "run.lock_guard_timeout",
186
+ retryable: true,
187
+ fix: "Retry after the active lock operation finishes. If its process crashed, inspect .run.lock.guard and remove that exact verified file manually; guards are never auto-reclaimed."
188
+ });
189
+ }
190
+ await delay(5);
191
+ }
192
+ }
193
+ }
194
+ async function releaseRunLockGuard(claim) {
195
+ const raw = await readFile(claim.path, "utf8").catch(() => undefined);
196
+ if (!raw || lockFingerprint(raw) !== claim.fingerprint || parseLockOwner(raw)?.claim_id !== claim.claimId) {
197
+ throw new ConfigError("Run-lock guard ownership changed before release.", {
198
+ code: "run.lock_guard_not_owned",
199
+ retryable: true
200
+ });
201
+ }
202
+ await unlink(claim.path);
203
+ }
204
+ function parseLockOwner(raw) {
205
+ try {
206
+ const value = JSON.parse(raw);
207
+ if (value.schema_version === 1 &&
208
+ typeof value.claim_id === "string" &&
209
+ typeof value.hostname === "string" &&
210
+ typeof value.worker_pid === "number" &&
211
+ Number.isInteger(value.worker_pid) &&
212
+ typeof value.claimed_at === "string") {
213
+ return value;
214
+ }
215
+ }
216
+ catch {
217
+ // Invalid guards are never auto-reclaimed.
218
+ }
219
+ return undefined;
220
+ }
221
+ function delay(ms) {
222
+ return new Promise((resolve) => setTimeout(resolve, ms));
223
+ }
224
+ function lockFingerprint(raw) {
225
+ return `sha256:${createHash("sha256").update(raw).digest("hex")}`;
226
+ }
227
+ function isLivePid(pid) {
228
+ try {
229
+ process.kill(pid, 0);
230
+ return true;
231
+ }
232
+ catch (error) {
233
+ return error.code !== "ESRCH";
234
+ }
34
235
  }
35
236
  export async function readRunStatus(runDir) {
36
237
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { ConfigError } from "./errors.js";
2
2
  export function makeKRegularPairs(candidateIds, k, rng) {
3
- const ids = [...candidateIds];
3
+ const ids = rng.shuffle(candidateIds);
4
4
  const n = ids.length;
5
5
  if (n < 2)
6
6
  throw new ConfigError("Pair scheduling requires at least two candidates.");
@@ -9,54 +9,23 @@ export function makeKRegularPairs(candidateIds, k, rng) {
9
9
  if ((n * k) % 2 !== 0) {
10
10
  throw new ConfigError(`Invalid pair schedule: n * k must be even, got ${n} * ${k}.`);
11
11
  }
12
- for (let attempt = 0; attempt < 300; attempt += 1) {
13
- const stubs = rng.shuffle(ids.flatMap((id) => Array.from({ length: k }, () => id)));
14
- const pairs = [];
15
- const seen = new Set();
16
- let ok = true;
17
- for (let i = 0; i < stubs.length; i += 2) {
18
- const a = stubs[i];
19
- const b = stubs[i + 1];
20
- const key = unorderedKey(a, b);
21
- if (a === b || seen.has(key)) {
22
- ok = false;
23
- break;
24
- }
25
- seen.add(key);
26
- pairs.push({ a, b });
12
+ const pairs = [];
13
+ const halfDegree = Math.floor(k / 2);
14
+ for (let offset = 1; offset <= halfDegree; offset += 1) {
15
+ for (let i = 0; i < n; i += 1) {
16
+ pairs.push({ a: ids[i], b: ids[(i + offset) % n] });
27
17
  }
28
- if (ok && hasDegree(ids, pairs, k))
29
- return pairs;
30
18
  }
31
- return greedyKRegular(ids, k, rng);
32
- }
33
- function greedyKRegular(ids, k, rng) {
34
- const remaining = new Map(ids.map((id) => [id, k]));
35
- const pairs = [];
36
- const seen = new Set();
37
- while ([...remaining.values()].some((degree) => degree > 0)) {
38
- const available = [...remaining.entries()]
39
- .filter(([, degree]) => degree > 0)
40
- .sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0]));
41
- const a = available[0]?.[0];
42
- if (!a)
43
- break;
44
- const candidates = rng.shuffle(available.slice(1).map(([id]) => id)).sort((left, right) => {
45
- return (remaining.get(right) ?? 0) - (remaining.get(left) ?? 0) || left.localeCompare(right);
46
- });
47
- const b = candidates.find((id) => id !== a && !seen.has(unorderedKey(a, id)));
48
- if (!b) {
49
- throw new ConfigError(`Could not construct ${k}-regular pair schedule for ${ids.length} candidates.`);
19
+ if (k % 2 === 1) {
20
+ const opposite = n / 2;
21
+ for (let i = 0; i < opposite; i += 1) {
22
+ pairs.push({ a: ids[i], b: ids[i + opposite] });
50
23
  }
51
- seen.add(unorderedKey(a, b));
52
- remaining.set(a, (remaining.get(a) ?? 0) - 1);
53
- remaining.set(b, (remaining.get(b) ?? 0) - 1);
54
- pairs.push({ a, b });
55
24
  }
56
25
  if (!hasDegree(ids, pairs, k)) {
57
26
  throw new ConfigError(`Could not construct ${k}-regular pair schedule for ${ids.length} candidates.`);
58
27
  }
59
- return pairs;
28
+ return rng.shuffle(pairs);
60
29
  }
61
30
  function unorderedKey(a, b) {
62
31
  return a < b ? `${a}::${b}` : `${b}::${a}`;
@@ -1 +1 @@
1
- {"version":3,"file":"pairScheduler.js","sourceRoot":"","sources":["../src/pairScheduler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pairScheduler.js","sourceRoot":"","sources":["../src/pairScheduler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function runLimitedPhase<T>(jobs: ReadonlyArray<() => Promise<T>>, concurrency: number): Promise<T[]>;
@@ -0,0 +1,26 @@
1
+ import pLimit from "p-limit";
2
+ export async function runLimitedPhase(jobs, concurrency) {
3
+ const limit = pLimit(concurrency);
4
+ const results = new Array(jobs.length);
5
+ const completed = new Array(jobs.length).fill(false);
6
+ let firstError;
7
+ await Promise.all(jobs.map((job, index) => limit(async () => {
8
+ if (firstError !== undefined)
9
+ return;
10
+ try {
11
+ results[index] = await job();
12
+ completed[index] = true;
13
+ }
14
+ catch (error) {
15
+ if (firstError === undefined)
16
+ firstError = error;
17
+ }
18
+ })));
19
+ if (firstError !== undefined)
20
+ throw firstError;
21
+ if (completed.some((value) => !value)) {
22
+ throw new Error("Phase completed without producing every result.");
23
+ }
24
+ return results;
25
+ }
26
+ //# sourceMappingURL=phaseRunner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phaseRunner.js","sourceRoot":"","sources":["../src/phaseRunner.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,SAAS,CAAC;AAE7B,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAqC,EACrC,WAAmB;IAEnB,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;IAClC,MAAM,OAAO,GAAG,IAAI,KAAK,CAAI,IAAI,CAAC,MAAM,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAG,IAAI,KAAK,CAAU,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9D,IAAI,UAAmB,CAAC;IAExB,MAAM,OAAO,CAAC,GAAG,CACf,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CACtB,KAAK,CAAC,KAAK,IAAI,EAAE;QACf,IAAI,UAAU,KAAK,SAAS;YAAE,OAAO;QACrC,IAAI,CAAC;YACH,OAAO,CAAC,KAAK,CAAC,GAAG,MAAM,GAAG,EAAE,CAAC;YAC7B,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;QAC1B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,UAAU,KAAK,SAAS;gBAAE,UAAU,GAAG,KAAK,CAAC;QACnD,CAAC;IACH,CAAC,CAAC,CACH,CACF,CAAC;IAEF,IAAI,UAAU,KAAK,SAAS;QAAE,MAAM,UAAU,CAAC;IAC/C,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
package/dist/resume.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { BudgetTracker } from "./budgetTracker.js";
2
- import type { RunStatus, UsageRecord } from "./lifecycle.js";
2
+ import { type RunStatus, type UsageRecord } from "./lifecycle.js";
3
3
  import type { BtScore, Candidate, Comparison, PhaseName, RunConfig } from "./schemas.js";
4
4
  export declare const pruneSentinelFile = ".prune-in-progress";
5
5
  export interface ResumeTrace {
@@ -41,11 +41,13 @@ export declare function resolveResumeRunId(config: RunConfig & {
41
41
  }, status: RunStatus | undefined, events: Array<Record<string, unknown>>): string;
42
42
  export declare function buildResumePlan(config: RunConfig, events: Array<Record<string, unknown>>): InternalResumePlan;
43
43
  export declare function resumeConfigError(message: string, code: string, fix?: string): never;
44
+ export declare function assertNoLegacyRedactedBudget(config: Record<string, unknown>): void;
45
+ export declare function repairLegacyBudgetConfig(runDir: string, replacements: Record<string, number>): Promise<string[]>;
44
46
  export declare function toResumePlanStatus(runDir: string, runId: string, plan: InternalResumePlan): ResumePlanStatus;
45
47
  export declare function pruneTraceToPlan(trace: ResumeTrace, plan: InternalResumePlan): ResumeTrace;
46
48
  export declare function buildPopulationMap(config: RunConfig, populations: Map<number, Candidate[]>, candidates: Candidate[], plan: InternalResumePlan): Map<number, Candidate[]>;
47
49
  export declare function groupComparisons(comparisons: Comparison[]): Map<number | "final", Comparison[]>;
48
- export declare function reconstructScores(config: RunConfig, populationByGeneration: Map<number, Candidate[]>, comparisonsByGeneration: Map<number | "final", Comparison[]>, existingScores: BtScore[], plan: InternalResumePlan): Map<number | "final", BtScore[]>;
50
+ export declare function reconstructScores(config: RunConfig, populationByGeneration: Map<number, Candidate[]>, comparisonsByGeneration: Map<number | "final", Comparison[]>, existingScores: BtScore[], plan: InternalResumePlan, runId?: string): Map<number | "final", BtScore[]>;
49
51
  export declare function replayBudgetUsage(config: RunConfig, usage: UsageRecord[]): BudgetTracker;
50
52
  export declare function persistPrunedTrace(runDir: string, pruned: ResumeTrace): Promise<void>;
51
53
  export declare function assertNoPruneInProgress(runDir: string): void;