@tangle-network/traces 0.11.2 → 0.11.3

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.
@@ -765,9 +765,9 @@ function serializeSpans(spans) {
765
765
  }
766
766
  async function writeOtlpFile(spans, outPath) {
767
767
  const { mkdtemp: mkdtemp8, writeFile: writeFile10 } = await import("fs/promises");
768
- const { tmpdir: tmpdir8 } = await import("os");
769
- const { join: join31 } = await import("path");
770
- const path = outPath ?? join31(await mkdtemp8(join31(tmpdir8(), "traces-")), "spans.otlp.jsonl");
768
+ const { tmpdir: tmpdir9 } = await import("os");
769
+ const { join: join33 } = await import("path");
770
+ const path = outPath ?? join33(await mkdtemp8(join33(tmpdir9(), "traces-")), "spans.otlp.jsonl");
771
771
  await writeFile10(path, serializeSpans(spans), "utf8");
772
772
  return path;
773
773
  }
@@ -5997,9 +5997,9 @@ function normalizeRemote(url) {
5997
5997
  }
5998
5998
  async function readGit(cwd) {
5999
5999
  await stat13(`${cwd}/.git`);
6000
- const { execFile } = await import("child_process");
6001
- const { promisify } = await import("util");
6002
- const run = promisify(execFile);
6000
+ const { execFile: execFile2 } = await import("child_process");
6001
+ const { promisify: promisify2 } = await import("util");
6002
+ const run = promisify2(execFile2);
6003
6003
  const git = async (args) => {
6004
6004
  try {
6005
6005
  const { stdout } = await run("git", ["-C", cwd, ...args], { timeout: 5e3 });
@@ -6025,9 +6025,9 @@ async function readGit(cwd) {
6025
6025
  };
6026
6026
  }
6027
6027
  async function gitOutput(cwd, args) {
6028
- const { execFile } = await import("child_process");
6029
- const { promisify } = await import("util");
6030
- const run = promisify(execFile);
6028
+ const { execFile: execFile2 } = await import("child_process");
6029
+ const { promisify: promisify2 } = await import("util");
6030
+ const run = promisify2(execFile2);
6031
6031
  try {
6032
6032
  const { stdout } = await run("git", ["-C", cwd, ...args], { timeout: 5e3 });
6033
6033
  const v = stdout.trim();
@@ -7141,6 +7141,1084 @@ async function cmdReplayVerify(argv) {
7141
7141
  );
7142
7142
  }
7143
7143
 
7144
+ // src/replay-corpus.ts
7145
+ import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as readFileSync3 } from "fs";
7146
+ import { join as join20 } from "path";
7147
+ function parseCorpusFlag(value) {
7148
+ const eq = value.indexOf("=");
7149
+ if (eq <= 0) throw new Error(`--corpus must be name=<labels>::<prepared>, got: ${value}`);
7150
+ const name = value.slice(0, eq);
7151
+ const rest = value.slice(eq + 1);
7152
+ const sep2 = rest.indexOf("::");
7153
+ if (sep2 <= 0 || sep2 === rest.length - 2) {
7154
+ throw new Error(`--corpus must be name=<labels>::<prepared>, got: ${value}`);
7155
+ }
7156
+ return { name, labelsPath: rest.slice(0, sep2), preparedDir: rest.slice(sep2 + 2) };
7157
+ }
7158
+ var SUBMIT_ACTION_SIGNATURE = "COMPLETE_TASK_AND_SUBMIT_FINAL_OUTPUT";
7159
+ function isSubmitAction(action) {
7160
+ return action.includes(SUBMIT_ACTION_SIGNATURE);
7161
+ }
7162
+ function readLabelEntries(labelsPath) {
7163
+ const parsed = JSON.parse(readFileSync3(labelsPath, "utf8"));
7164
+ if (!Array.isArray(parsed)) throw new Error(`${labelsPath} is not a JSON array of label entries`);
7165
+ for (const entry of parsed) {
7166
+ if (typeof entry.traj_id !== "string") {
7167
+ throw new Error(`${labelsPath} has an entry without a string traj_id`);
7168
+ }
7169
+ }
7170
+ return parsed;
7171
+ }
7172
+ function goldIncorrectSteps(entry) {
7173
+ const ids = /* @__PURE__ */ new Set();
7174
+ for (const stage of entry.incorrect_stages ?? []) {
7175
+ for (const id of stage.incorrect_step_ids ?? []) ids.add(id);
7176
+ }
7177
+ return [...ids].sort((a, b) => a - b);
7178
+ }
7179
+ function findRawTrajFiles(sweRawDir) {
7180
+ if (!existsSync2(sweRawDir)) return [];
7181
+ const entries = readdirSync2(sweRawDir, { recursive: true, encoding: "utf8" });
7182
+ return entries.filter((relative5) => relative5.endsWith(".traj.json")).map((relative5) => join20(sweRawDir, relative5)).sort();
7183
+ }
7184
+ function cwdFromPwdObservation(steps) {
7185
+ for (const step of steps) {
7186
+ if (step.action.trim() !== "pwd") continue;
7187
+ const line = parseObservationOutput(step.observation).split("\n").map((l) => l.trim()).find((l) => l.startsWith("/"));
7188
+ if (line) return line;
7189
+ }
7190
+ return null;
7191
+ }
7192
+ function taskStatementOf(preparedDir, trajId, raw) {
7193
+ const taskPath = join20(preparedDir, "normalized", trajId, "task.md");
7194
+ if (existsSync2(taskPath)) {
7195
+ const text2 = readFileSync3(taskPath, "utf8").trim();
7196
+ if (text2.length > 0) return text2;
7197
+ }
7198
+ const userMessage = raw.messages?.find((m) => m.role === "user");
7199
+ if (typeof userMessage?.content === "string" && userMessage.content.trim().length > 0) {
7200
+ return userMessage.content.trim();
7201
+ }
7202
+ return null;
7203
+ }
7204
+ function resolveCaseResources(corpus, trajId) {
7205
+ const sweRawDir = join20(corpus.preparedDir, "extracted", trajId, "swe_raw");
7206
+ const rawFiles = findRawTrajFiles(sweRawDir);
7207
+ if (rawFiles.length === 0) {
7208
+ return { resolved: false, reason: "no-swe-raw-trajectory", detail: sweRawDir };
7209
+ }
7210
+ if (rawFiles.length > 1) {
7211
+ return { resolved: false, reason: "ambiguous-swe-raw-trajectory", detail: rawFiles.join(", ") };
7212
+ }
7213
+ let raw;
7214
+ try {
7215
+ raw = JSON.parse(readFileSync3(rawFiles[0], "utf8"));
7216
+ } catch (err) {
7217
+ return {
7218
+ resolved: false,
7219
+ reason: "unreadable-raw-trajectory",
7220
+ detail: `${rawFiles[0]}: ${err instanceof Error ? err.message : String(err)}`
7221
+ };
7222
+ }
7223
+ const dockerConfig = raw.info?.docker_config;
7224
+ const image = dockerConfig?.base_image;
7225
+ if (typeof image !== "string" || image.length === 0) {
7226
+ return { resolved: false, reason: "no-docker-image", detail: rawFiles[0] };
7227
+ }
7228
+ const stepsPath = join20(corpus.preparedDir, "normalized", trajId, "steps.json");
7229
+ if (!existsSync2(stepsPath)) {
7230
+ return { resolved: false, reason: "missing-steps-json", detail: stepsPath };
7231
+ }
7232
+ const steps = JSON.parse(readFileSync3(stepsPath, "utf8"));
7233
+ const runConfigCwd = raw.info?.config?.environment?.cwd;
7234
+ const dockerCwd = dockerConfig?.cwd;
7235
+ let cwd;
7236
+ let cwdSource;
7237
+ if (typeof runConfigCwd === "string" && runConfigCwd.length > 0) {
7238
+ cwd = runConfigCwd;
7239
+ cwdSource = "run-config";
7240
+ } else if (typeof dockerCwd === "string" && dockerCwd.length > 0) {
7241
+ cwd = dockerCwd;
7242
+ cwdSource = "docker-config";
7243
+ } else {
7244
+ const observed = cwdFromPwdObservation(steps);
7245
+ if (!observed) return { resolved: false, reason: "cwd-underivable" };
7246
+ cwd = observed;
7247
+ cwdSource = "pwd-observation";
7248
+ }
7249
+ const recordedTimeout = raw.info?.config?.environment?.timeout;
7250
+ return {
7251
+ resolved: true,
7252
+ resources: {
7253
+ corpus: corpus.name,
7254
+ trajId,
7255
+ stepsPath,
7256
+ steps,
7257
+ taskStatement: taskStatementOf(corpus.preparedDir, trajId, raw),
7258
+ image,
7259
+ cwd,
7260
+ cwdSource,
7261
+ recordedStepTimeoutMs: typeof recordedTimeout === "number" && recordedTimeout > 0 ? recordedTimeout * 1e3 : null
7262
+ }
7263
+ };
7264
+ }
7265
+ function enumerateReplayableCases(corpora) {
7266
+ const replayable = [];
7267
+ const excluded = [];
7268
+ let labelEntryCount = 0;
7269
+ for (const corpus of corpora) {
7270
+ for (const entry of readLabelEntries(corpus.labelsPath)) {
7271
+ labelEntryCount += 1;
7272
+ const trajId = entry.traj_id;
7273
+ const resolution = resolveCaseResources(corpus, trajId);
7274
+ if (!resolution.resolved) {
7275
+ excluded.push({ corpus: corpus.name, trajId, reason: resolution.reason, detail: resolution.detail });
7276
+ continue;
7277
+ }
7278
+ const gold = goldIncorrectSteps(entry);
7279
+ if (gold.length === 0) {
7280
+ excluded.push({ corpus: corpus.name, trajId, reason: "no-gold-incorrect-step" });
7281
+ continue;
7282
+ }
7283
+ const resources = resolution.resources;
7284
+ let submitGoldsSkipped = 0;
7285
+ let target = null;
7286
+ let missingGoldId = null;
7287
+ for (const goldId of gold) {
7288
+ const step = resources.steps.find((s) => s.step_id === goldId);
7289
+ if (!step) {
7290
+ missingGoldId = goldId;
7291
+ break;
7292
+ }
7293
+ if (isSubmitAction(step.action)) {
7294
+ submitGoldsSkipped += 1;
7295
+ continue;
7296
+ }
7297
+ target = step;
7298
+ break;
7299
+ }
7300
+ if (missingGoldId !== null) {
7301
+ excluded.push({
7302
+ corpus: corpus.name,
7303
+ trajId,
7304
+ reason: "gold-step-outside-steps",
7305
+ detail: `k=${missingGoldId}, steps=${resources.steps.length}`
7306
+ });
7307
+ continue;
7308
+ }
7309
+ if (!target) {
7310
+ excluded.push({
7311
+ corpus: corpus.name,
7312
+ trajId,
7313
+ reason: "gold-only-submit-step",
7314
+ detail: `${submitGoldsSkipped} gold step(s), all submit commands`
7315
+ });
7316
+ continue;
7317
+ }
7318
+ replayable.push({
7319
+ ...resources,
7320
+ goldIncorrectSteps: gold,
7321
+ k: target.step_id,
7322
+ submitGoldsSkipped,
7323
+ recordedReturncodeAtK: parseRecordedReturncode(target.observation)
7324
+ });
7325
+ }
7326
+ }
7327
+ return { replayable, excluded, labelEntryCount };
7328
+ }
7329
+
7330
+ // src/replay-fix.ts
7331
+ function zaiChatCaller(options) {
7332
+ const fetchImpl = options.fetchImpl ?? fetch;
7333
+ return {
7334
+ async complete(system, user) {
7335
+ const baseCap = options.maxTokens ?? 16384;
7336
+ let lastError = "empty completion content";
7337
+ for (const cap of [baseCap, baseCap * 2]) {
7338
+ const outcome = await callOnce(cap);
7339
+ if (outcome.succeeded) return outcome;
7340
+ lastError = outcome.error;
7341
+ if (!/finish_reason":"length/.test(outcome.error)) return outcome;
7342
+ }
7343
+ return { succeeded: false, error: lastError };
7344
+ async function callOnce(maxTokens) {
7345
+ const controller = new AbortController();
7346
+ const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? 18e4);
7347
+ try {
7348
+ const response = await fetchImpl(`${options.baseUrl.replace(/\/$/, "")}/chat/completions`, {
7349
+ method: "POST",
7350
+ headers: {
7351
+ authorization: `Bearer ${options.apiKey}`,
7352
+ "content-type": "application/json"
7353
+ },
7354
+ body: JSON.stringify({
7355
+ model: options.model,
7356
+ messages: [
7357
+ { role: "system", content: system },
7358
+ { role: "user", content: user }
7359
+ ],
7360
+ temperature: 0,
7361
+ max_tokens: maxTokens
7362
+ }),
7363
+ signal: controller.signal
7364
+ });
7365
+ const bodyText = await response.text();
7366
+ if (!response.ok) {
7367
+ return { succeeded: false, error: `HTTP ${response.status}: ${bodyText.slice(0, 400)}` };
7368
+ }
7369
+ const body = JSON.parse(bodyText);
7370
+ if (body.error) {
7371
+ return { succeeded: false, error: String(body.error.message ?? "provider error") };
7372
+ }
7373
+ const content = body.choices?.[0]?.message?.content;
7374
+ if (typeof content !== "string" || content.length === 0) {
7375
+ return { succeeded: false, error: `empty completion content: ${bodyText.slice(0, 400)}` };
7376
+ }
7377
+ const promptTokens = body.usage?.prompt_tokens;
7378
+ const completionTokens = body.usage?.completion_tokens;
7379
+ const usage = typeof promptTokens === "number" && typeof completionTokens === "number" ? { promptTokens, completionTokens } : null;
7380
+ return { succeeded: true, value: { content, usage } };
7381
+ } catch (err) {
7382
+ return { succeeded: false, error: err instanceof Error ? err.message : String(err) };
7383
+ } finally {
7384
+ clearTimeout(timer);
7385
+ }
7386
+ }
7387
+ }
7388
+ };
7389
+ }
7390
+ function clip(text2, limit) {
7391
+ if (text2.length <= limit) return text2;
7392
+ const half = Math.floor(limit / 2);
7393
+ return `${text2.slice(0, half)}
7394
+ \u2026 [${text2.length - limit} chars elided] \u2026
7395
+ ${text2.slice(-half)}`;
7396
+ }
7397
+ function renderStep(step, marker) {
7398
+ const rc = parseRecordedReturncode(step.observation);
7399
+ const output = clip(parseObservationOutput(step.observation).trim(), 1600);
7400
+ return [
7401
+ `### step ${step.step_id}${marker}`,
7402
+ "```sh",
7403
+ clip(step.action, 2e3),
7404
+ "```",
7405
+ `returncode: ${rc ?? "none recorded"}`,
7406
+ output.length > 0 ? `output:
7407
+ \`\`\`
7408
+ ${output}
7409
+ \`\`\`` : "output: (empty)"
7410
+ ].join("\n");
7411
+ }
7412
+ function buildFixPrompt(input) {
7413
+ const radius = input.contextRadius ?? 3;
7414
+ const target = input.steps.find((s) => s.step_id === input.k);
7415
+ if (!target) throw new Error(`replay-fix: no step with step_id ${input.k}`);
7416
+ const context = input.steps.filter(
7417
+ (s) => s.step_id !== input.k && Math.abs(s.step_id - input.k) <= radius
7418
+ );
7419
+ const system = [
7420
+ "You repair one failed shell command from a recorded coding-agent trajectory.",
7421
+ "The trajectory replays inside the original docker image; every command runs as a fresh /bin/sh subshell from a fixed working directory.",
7422
+ "You are given the failing step, its recorded output, surrounding steps, and the task statement.",
7423
+ "Reply with exactly ONE corrected shell command (compound commands with && or pipes are fine) inside a single ```sh fenced block.",
7424
+ "The corrected command must accomplish the failing step's intent and exit 0. No prose outside the fenced block."
7425
+ ].join("\n");
7426
+ const user = [
7427
+ "## Task the agent was solving",
7428
+ input.taskStatement ? clip(input.taskStatement, 4e3) : "(no task statement recorded)",
7429
+ "",
7430
+ "## Surrounding steps",
7431
+ ...context.map((s) => renderStep(s, "")),
7432
+ "",
7433
+ "## Failing step to correct",
7434
+ renderStep(target, " (INCORRECT \u2014 correct this one)"),
7435
+ "",
7436
+ "Output the single corrected replacement for the failing step now."
7437
+ ].join("\n");
7438
+ return { system, user };
7439
+ }
7440
+ function extractFixCommand(content) {
7441
+ const blocks = [...content.matchAll(/```(?:sh|bash|shell)?\n([\s\S]*?)```/g)];
7442
+ const candidate = blocks.length > 0 ? blocks[blocks.length - 1][1] : content;
7443
+ const command = candidate.trim();
7444
+ if (command.length === 0 || command.includes("```")) return null;
7445
+ return command;
7446
+ }
7447
+ async function generateFixCommand(caller, input) {
7448
+ const { system, user } = buildFixPrompt(input);
7449
+ const outcome = await caller.complete(system, user);
7450
+ if (!outcome.succeeded) return outcome;
7451
+ const command = extractFixCommand(outcome.value.content);
7452
+ if (command === null) {
7453
+ return {
7454
+ succeeded: false,
7455
+ error: `completion carried no usable command: ${clip(outcome.value.content, 300)}`
7456
+ };
7457
+ }
7458
+ return { succeeded: true, value: { command, usage: outcome.value.usage } };
7459
+ }
7460
+
7461
+ // src/replay-batch.ts
7462
+ import { execFile } from "child_process";
7463
+ import { createHash as createHash8 } from "crypto";
7464
+ import { appendFileSync, mkdirSync as mkdirSync2, mkdtempSync, writeFileSync as writeFileSync2 } from "fs";
7465
+ import { tmpdir as tmpdir3 } from "os";
7466
+ import { join as join21 } from "path";
7467
+ import { promisify } from "util";
7468
+ import {
7469
+ InMemoryTraceStore as InMemoryTraceStore3,
7470
+ runCounterfactual as runCounterfactual2
7471
+ } from "@tangle-network/agent-eval";
7472
+ var execFileAsync = promisify(execFile);
7473
+ function derivedImageTag(image, cwd) {
7474
+ const digest = createHash8("sha256").update(`${image}
7475
+ ${cwd}`).digest("hex").slice(0, 12);
7476
+ return `ctb-replay:${digest}-uid1000`;
7477
+ }
7478
+ function dockerImagePreparer(options = {}) {
7479
+ const pullTimeoutMs = options.pullTimeoutMs ?? 12e5;
7480
+ const buildTimeoutMs = options.buildTimeoutMs ?? 9e5;
7481
+ const imageExists = async (tag) => {
7482
+ try {
7483
+ await execFileAsync("docker", ["image", "inspect", tag], { maxBuffer: 8 * 1024 * 1024 });
7484
+ return true;
7485
+ } catch {
7486
+ return false;
7487
+ }
7488
+ };
7489
+ const errorTail = (err) => {
7490
+ const raw = err && typeof err === "object" && "stderr" in err && typeof err.stderr === "string" && err.stderr.length > 0 ? err.stderr : err instanceof Error ? err.message : String(err);
7491
+ return raw.trim().split("\n").slice(-3).join(" | ").slice(0, 400);
7492
+ };
7493
+ return {
7494
+ async ensure(image, cwd) {
7495
+ if (cwd === "/") {
7496
+ if (await imageExists(image)) {
7497
+ return { succeeded: true, value: { derivedImage: image, pulled: false, built: false } };
7498
+ }
7499
+ try {
7500
+ await execFileAsync("docker", ["pull", image], {
7501
+ timeout: pullTimeoutMs,
7502
+ maxBuffer: 32 * 1024 * 1024
7503
+ });
7504
+ } catch (err) {
7505
+ return { succeeded: false, error: `pull ${image}: ${errorTail(err)}` };
7506
+ }
7507
+ return { succeeded: true, value: { derivedImage: image, pulled: true, built: false } };
7508
+ }
7509
+ const derived = derivedImageTag(image, cwd);
7510
+ if (await imageExists(derived)) {
7511
+ return { succeeded: true, value: { derivedImage: derived, pulled: false, built: false } };
7512
+ }
7513
+ let pulled = false;
7514
+ if (!await imageExists(image)) {
7515
+ try {
7516
+ await execFileAsync("docker", ["pull", image], {
7517
+ timeout: pullTimeoutMs,
7518
+ maxBuffer: 32 * 1024 * 1024
7519
+ });
7520
+ pulled = true;
7521
+ } catch (err) {
7522
+ return { succeeded: false, error: `pull ${image}: ${errorTail(err)}` };
7523
+ }
7524
+ }
7525
+ const contextDir = mkdtempSync(join21(tmpdir3(), "ctb-replay-image-"));
7526
+ const quotedCwd = `'${cwd.replaceAll("'", `'\\''`)}'`;
7527
+ writeFileSync2(
7528
+ join21(contextDir, "Dockerfile"),
7529
+ `FROM ${image}
7530
+ RUN chown -R 1000:1000 ${quotedCwd}
7531
+ `
7532
+ );
7533
+ try {
7534
+ await execFileAsync("docker", ["build", "-t", derived, contextDir], {
7535
+ timeout: buildTimeoutMs,
7536
+ maxBuffer: 32 * 1024 * 1024
7537
+ });
7538
+ } catch (err) {
7539
+ return { succeeded: false, error: `build ${derived} from ${image}: ${errorTail(err)}` };
7540
+ }
7541
+ return { succeeded: true, value: { derivedImage: derived, pulled, built: true } };
7542
+ }
7543
+ };
7544
+ }
7545
+ function mulberry32(seed) {
7546
+ let state = seed >>> 0;
7547
+ return () => {
7548
+ state = state + 1831565813 >>> 0;
7549
+ let t = state;
7550
+ t = Math.imul(t ^ t >>> 15, t | 1);
7551
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
7552
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
7553
+ };
7554
+ }
7555
+ function seededSample(items, size, seed) {
7556
+ const pool = [...items];
7557
+ const random = mulberry32(seed);
7558
+ for (let i = pool.length - 1; i > 0; i--) {
7559
+ const j = Math.floor(random() * (i + 1));
7560
+ [pool[i], pool[j]] = [pool[j], pool[i]];
7561
+ }
7562
+ return new Set(pool.slice(0, size));
7563
+ }
7564
+ function caseOutDirName(row) {
7565
+ return `${row.corpus}--${row.trajId}`.replaceAll(/[^A-Za-z0-9._-]/g, "_").slice(0, 180);
7566
+ }
7567
+ async function executeArmB(replayCase, fixCommand, backend, signature, stepTimeoutMs, prefixLimit, onProgress) {
7568
+ const store = new InMemoryTraceStore3();
7569
+ const { runId } = await ingestCodeTraceBenchSteps(store, replayCase.steps, replayCase.trajId);
7570
+ const runner = new SandboxCounterfactualRunner(backend, {
7571
+ cwd: replayCase.cwd,
7572
+ stepTimeoutMs,
7573
+ prefixLimit,
7574
+ onProgress
7575
+ });
7576
+ await runCounterfactual2(
7577
+ store,
7578
+ runId,
7579
+ {
7580
+ kind: "custom",
7581
+ at: replayCase.k - 1,
7582
+ describe: "arm-B corrected step",
7583
+ apply: (step) => ({
7584
+ ...step,
7585
+ span: { ...step.span, args: { command: fixCommand } }
7586
+ })
7587
+ },
7588
+ runner
7589
+ );
7590
+ const exec = runner.lastArm;
7591
+ if (!exec) throw new Error("arm B finished without executing the corrected step");
7592
+ const output = `${exec.stdout}
7593
+ ${exec.stderr}`;
7594
+ return {
7595
+ exitCode: exec.exitCode,
7596
+ prefixExecuted: runner.lastPrefix?.prefixExecuted ?? 0,
7597
+ prefixDivergences: runner.lastPrefix?.prefixDivergences.length ?? 0,
7598
+ failureVanished: exec.exitCode === 0 && (signature ? !output.includes(signature) : true),
7599
+ stdout: exec.stdout,
7600
+ stderr: exec.stderr
7601
+ };
7602
+ }
7603
+ function rate(numerator, denominator) {
7604
+ return denominator > 0 ? numerator / denominator : null;
7605
+ }
7606
+ async function runReplayBatch(options) {
7607
+ const onProgress = options.onProgress ?? (() => {
7608
+ });
7609
+ const enumeration = enumerateReplayableCases(options.corpora);
7610
+ let selected = enumeration.replayable;
7611
+ if (options.caseFilter) {
7612
+ selected = selected.filter((c) => c.trajId.includes(options.caseFilter));
7613
+ }
7614
+ if (options.caseLimit !== void 0) selected = selected.slice(0, options.caseLimit);
7615
+ onProgress(
7616
+ `enumerated ${enumeration.labelEntryCount} label entries \u2192 ${enumeration.replayable.length} replayable, ${enumeration.excluded.length} excluded; executing ${selected.length}`
7617
+ );
7618
+ const preparer = options.preparer ?? dockerImagePreparer();
7619
+ const backendFactory = options.backendFactory ?? ((derivedImage) => sandboxReplayBackend({
7620
+ image: derivedImage,
7621
+ apiKey: requireOption2(options.apiKey, "apiKey"),
7622
+ baseUrl: requireOption2(options.baseUrl, "baseUrl"),
7623
+ maxLifetimeSeconds: options.maxLifetimeSeconds ?? 3600
7624
+ }));
7625
+ mkdirSync2(options.out, { recursive: true });
7626
+ const progressPath = join21(options.out, "cases.jsonl");
7627
+ const rows = [];
7628
+ const pullFailures = [];
7629
+ const verdictByTraj = /* @__PURE__ */ new Map();
7630
+ for (const [index, replayCase] of selected.entries()) {
7631
+ const caseStart = Date.now();
7632
+ const label = `[${index + 1}/${selected.length}] ${replayCase.corpus}/${replayCase.trajId}`;
7633
+ onProgress(`${label}: preparing image ${replayCase.image}`);
7634
+ const preparation = await preparer.ensure(replayCase.image, replayCase.cwd);
7635
+ const base = {
7636
+ corpus: replayCase.corpus,
7637
+ trajId: replayCase.trajId,
7638
+ image: replayCase.image,
7639
+ cwd: replayCase.cwd,
7640
+ cwdSource: replayCase.cwdSource,
7641
+ k: replayCase.k,
7642
+ stepCount: replayCase.steps.length,
7643
+ goldIncorrectSteps: replayCase.goldIncorrectSteps,
7644
+ submitGoldsSkipped: replayCase.submitGoldsSkipped,
7645
+ recordedReturncodeAtK: replayCase.recordedReturncodeAtK
7646
+ };
7647
+ if (!preparation.succeeded) {
7648
+ pullFailures.push({
7649
+ corpus: replayCase.corpus,
7650
+ trajId: replayCase.trajId,
7651
+ image: replayCase.image,
7652
+ error: preparation.error
7653
+ });
7654
+ const row = {
7655
+ ...base,
7656
+ derivedImage: null,
7657
+ signature: null,
7658
+ status: "image-unavailable",
7659
+ error: preparation.error,
7660
+ imagePulled: false,
7661
+ imageBuilt: false,
7662
+ prefixExecuted: null,
7663
+ prefixDivergences: null,
7664
+ prefixDivergencePct: null,
7665
+ armAExit: null,
7666
+ armAReturncodeMatch: false,
7667
+ armASignatureMatch: false,
7668
+ replayed: false,
7669
+ fix: null,
7670
+ wallMs: Date.now() - caseStart
7671
+ };
7672
+ rows.push(row);
7673
+ appendFileSync(progressPath, `${JSON.stringify(row)}
7674
+ `);
7675
+ onProgress(`${label}: image unavailable \u2014 ${preparation.error}`);
7676
+ continue;
7677
+ }
7678
+ const { derivedImage, pulled, built } = preparation.value;
7679
+ const stepTimeoutMs = options.stepTimeoutMs ?? replayCase.recordedStepTimeoutMs ?? 12e4;
7680
+ const caseOut = join21(options.out, caseOutDirName(replayCase));
7681
+ onProgress(`${label}: arm A on ${derivedImage} (k=${replayCase.k}, timeout ${stepTimeoutMs}ms)`);
7682
+ try {
7683
+ const verdict = await replayVerify({
7684
+ stepsPath: replayCase.stepsPath,
7685
+ image: derivedImage,
7686
+ at: replayCase.k,
7687
+ cwd: replayCase.cwd,
7688
+ out: caseOut,
7689
+ caseId: replayCase.trajId,
7690
+ stepTimeoutMs,
7691
+ prefixLimit: options.prefixLimit,
7692
+ backend: backendFactory(derivedImage),
7693
+ onProgress: (message) => onProgress(`${label}: ${message}`)
7694
+ });
7695
+ verdictByTraj.set(replayCase.trajId, verdict);
7696
+ const divergencePct = verdict.prefixExecuted > 0 ? verdict.prefixDivergences.length / verdict.prefixExecuted * 100 : 0;
7697
+ const returncodeMatch = verdict.recordedReturncode !== null && verdict.armA.exitCode === verdict.recordedReturncode;
7698
+ const row = {
7699
+ ...base,
7700
+ derivedImage,
7701
+ signature: verdict.signature,
7702
+ status: "ok",
7703
+ error: null,
7704
+ imagePulled: pulled,
7705
+ imageBuilt: built,
7706
+ prefixExecuted: verdict.prefixExecuted,
7707
+ prefixDivergences: verdict.prefixDivergences.length,
7708
+ prefixDivergencePct: Number(divergencePct.toFixed(1)),
7709
+ armAExit: verdict.armA.exitCode,
7710
+ armAReturncodeMatch: returncodeMatch,
7711
+ armASignatureMatch: verdict.armA.failureSignatureMatch,
7712
+ replayed: divergencePct <= 10 && returncodeMatch,
7713
+ fix: null,
7714
+ wallMs: Date.now() - caseStart
7715
+ };
7716
+ rows.push(row);
7717
+ appendFileSync(progressPath, `${JSON.stringify(row)}
7718
+ `);
7719
+ onProgress(
7720
+ `${label}: armA exit=${verdict.armA.exitCode} rcMatch=${returncodeMatch} divergences=${verdict.prefixDivergences.length}/${verdict.prefixExecuted}`
7721
+ );
7722
+ } catch (err) {
7723
+ const message = err instanceof Error ? err.message : String(err);
7724
+ const row = {
7725
+ ...base,
7726
+ derivedImage,
7727
+ signature: null,
7728
+ status: "replay-error",
7729
+ error: message.slice(0, 500),
7730
+ imagePulled: pulled,
7731
+ imageBuilt: built,
7732
+ prefixExecuted: null,
7733
+ prefixDivergences: null,
7734
+ prefixDivergencePct: null,
7735
+ armAExit: null,
7736
+ armAReturncodeMatch: false,
7737
+ armASignatureMatch: false,
7738
+ replayed: false,
7739
+ fix: null,
7740
+ wallMs: Date.now() - caseStart
7741
+ };
7742
+ rows.push(row);
7743
+ appendFileSync(progressPath, `${JSON.stringify(row)}
7744
+ `);
7745
+ onProgress(`${label}: replay error \u2014 ${message.slice(0, 200)}`);
7746
+ }
7747
+ }
7748
+ let llm = null;
7749
+ if (options.fix === "generate") {
7750
+ const caller = options.fixCaller;
7751
+ if (!caller) throw new Error("replay-verify-batch: fix=generate requires a fixCaller");
7752
+ const maxFixCases = options.maxFixCases ?? 30;
7753
+ const seed = options.seed ?? 17;
7754
+ const eligible = rows.filter((r) => r.status === "ok" && r.replayed);
7755
+ const sampled = eligible.length > maxFixCases ? seededSample(eligible, maxFixCases, seed) : new Set(eligible);
7756
+ if (eligible.length > maxFixCases) {
7757
+ onProgress(
7758
+ `fix phase: ${eligible.length} eligible > cap ${maxFixCases}; seeded sample (seed=${seed})`
7759
+ );
7760
+ }
7761
+ let calls = 0;
7762
+ let failures = 0;
7763
+ let promptTokens = 0;
7764
+ let completionTokens = 0;
7765
+ for (const row of eligible) {
7766
+ const index = rows.indexOf(row);
7767
+ if (!sampled.has(row)) {
7768
+ rows[index] = {
7769
+ ...row,
7770
+ fix: {
7771
+ attempted: false,
7772
+ sampledOut: true,
7773
+ command: null,
7774
+ llmError: null,
7775
+ usage: null,
7776
+ armBExit: null,
7777
+ armBPrefixExecuted: null,
7778
+ armBPrefixDivergences: null,
7779
+ failureVanished: null,
7780
+ armBError: null
7781
+ }
7782
+ };
7783
+ continue;
7784
+ }
7785
+ const replayCase = selected.find((c) => c.trajId === row.trajId && c.corpus === row.corpus);
7786
+ const label = `fix ${row.corpus}/${row.trajId}`;
7787
+ onProgress(`${label}: generating corrected command`);
7788
+ calls += 1;
7789
+ const generated = await generateFixCommand(caller, {
7790
+ taskStatement: replayCase.taskStatement,
7791
+ steps: replayCase.steps,
7792
+ k: replayCase.k
7793
+ });
7794
+ if (!generated.succeeded) {
7795
+ failures += 1;
7796
+ rows[index] = {
7797
+ ...row,
7798
+ fix: {
7799
+ attempted: true,
7800
+ sampledOut: false,
7801
+ command: null,
7802
+ llmError: generated.error,
7803
+ usage: null,
7804
+ armBExit: null,
7805
+ armBPrefixExecuted: null,
7806
+ armBPrefixDivergences: null,
7807
+ failureVanished: null,
7808
+ armBError: null
7809
+ }
7810
+ };
7811
+ onProgress(`${label}: LLM failed \u2014 ${generated.error.slice(0, 200)}`);
7812
+ continue;
7813
+ }
7814
+ promptTokens += generated.value.usage?.promptTokens ?? 0;
7815
+ completionTokens += generated.value.usage?.completionTokens ?? 0;
7816
+ const signature = verdictByTraj.get(row.trajId)?.signature ?? null;
7817
+ const stepTimeoutMs = options.stepTimeoutMs ?? replayCase.recordedStepTimeoutMs ?? 12e4;
7818
+ onProgress(`${label}: arm B \u2014 ${generated.value.command.split("\n")[0].slice(0, 120)}`);
7819
+ try {
7820
+ const armB = await executeArmB(
7821
+ replayCase,
7822
+ generated.value.command,
7823
+ backendFactory(row.derivedImage),
7824
+ signature,
7825
+ stepTimeoutMs,
7826
+ options.prefixLimit,
7827
+ (message) => onProgress(`${label}: ${message}`)
7828
+ );
7829
+ rows[index] = {
7830
+ ...row,
7831
+ fix: {
7832
+ attempted: true,
7833
+ sampledOut: false,
7834
+ command: generated.value.command,
7835
+ llmError: null,
7836
+ usage: generated.value.usage,
7837
+ armBExit: armB.exitCode,
7838
+ armBPrefixExecuted: armB.prefixExecuted,
7839
+ armBPrefixDivergences: armB.prefixDivergences,
7840
+ failureVanished: armB.failureVanished,
7841
+ armBError: null
7842
+ }
7843
+ };
7844
+ const caseOut = join21(options.out, caseOutDirName(row));
7845
+ mkdirSync2(caseOut, { recursive: true });
7846
+ writeFileSync2(
7847
+ join21(caseOut, "armB-result.json"),
7848
+ `${JSON.stringify({ command: generated.value.command, ...armB }, null, 2)}
7849
+ `
7850
+ );
7851
+ onProgress(`${label}: armB exit=${armB.exitCode} failureVanished=${armB.failureVanished}`);
7852
+ } catch (err) {
7853
+ const message = err instanceof Error ? err.message : String(err);
7854
+ rows[index] = {
7855
+ ...row,
7856
+ fix: {
7857
+ attempted: true,
7858
+ sampledOut: false,
7859
+ command: generated.value.command,
7860
+ llmError: null,
7861
+ usage: generated.value.usage,
7862
+ armBExit: null,
7863
+ armBPrefixExecuted: null,
7864
+ armBPrefixDivergences: null,
7865
+ failureVanished: null,
7866
+ armBError: message.slice(0, 500)
7867
+ }
7868
+ };
7869
+ onProgress(`${label}: arm B error \u2014 ${message.slice(0, 200)}`);
7870
+ }
7871
+ }
7872
+ llm = {
7873
+ model: options.fixModelLabel ?? "unknown",
7874
+ calls,
7875
+ failures,
7876
+ promptTokens,
7877
+ completionTokens
7878
+ };
7879
+ }
7880
+ const executedRows = rows.filter((r) => r.status === "ok");
7881
+ const replayed = executedRows.filter((r) => r.replayed);
7882
+ const signatureStrict = executedRows.filter(
7883
+ (r) => r.replayed && r.armASignatureMatch
7884
+ );
7885
+ const armBExecuted = rows.filter((r) => r.fix?.attempted && r.fix.failureVanished !== null);
7886
+ const flipped = armBExecuted.filter((r) => r.fix.failureVanished === true);
7887
+ const armBNonzeroRc = armBExecuted.filter(
7888
+ (r) => r.recordedReturncodeAtK !== null && r.recordedReturncodeAtK !== 0
7889
+ );
7890
+ const flippedNonzeroRc = armBNonzeroRc.filter((r) => r.fix.failureVanished === true);
7891
+ const excludedByReason = {};
7892
+ for (const excluded of enumeration.excluded) {
7893
+ excludedByReason[excluded.reason] = (excludedByReason[excluded.reason] ?? 0) + 1;
7894
+ }
7895
+ const submitGoldsByCorpus = {};
7896
+ const submitEntry = (corpus) => submitGoldsByCorpus[corpus] ??= { submitOnlyCases: 0, goldsSkippedWithinReplayable: 0 };
7897
+ for (const excluded of enumeration.excluded) {
7898
+ if (excluded.reason === "gold-only-submit-step") submitEntry(excluded.corpus).submitOnlyCases += 1;
7899
+ }
7900
+ for (const replayCase of enumeration.replayable) {
7901
+ if (replayCase.submitGoldsSkipped > 0) {
7902
+ submitEntry(replayCase.corpus).goldsSkippedWithinReplayable += replayCase.submitGoldsSkipped;
7903
+ }
7904
+ }
7905
+ const report = {
7906
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
7907
+ corpora: options.corpora.map((c) => ({
7908
+ name: c.name,
7909
+ labelsPath: c.labelsPath,
7910
+ preparedDir: c.preparedDir
7911
+ })),
7912
+ totals: {
7913
+ labelEntries: enumeration.labelEntryCount,
7914
+ replayable: enumeration.replayable.length,
7915
+ executed: selected.length,
7916
+ excludedByReason,
7917
+ submitGoldsByCorpus
7918
+ },
7919
+ headline: {
7920
+ replayabilityRate: {
7921
+ numerator: replayed.length,
7922
+ denominator: selected.length,
7923
+ value: rate(replayed.length, selected.length)
7924
+ },
7925
+ signatureStrictRate: {
7926
+ numerator: signatureStrict.length,
7927
+ denominator: selected.length,
7928
+ value: rate(signatureStrict.length, selected.length)
7929
+ },
7930
+ fixFlipRate: options.fix === "generate" ? {
7931
+ numerator: flipped.length,
7932
+ denominator: armBExecuted.length,
7933
+ value: rate(flipped.length, armBExecuted.length)
7934
+ } : null,
7935
+ fixFlipRateNonzeroRc: options.fix === "generate" ? {
7936
+ numerator: flippedNonzeroRc.length,
7937
+ denominator: armBNonzeroRc.length,
7938
+ value: rate(flippedNonzeroRc.length, armBNonzeroRc.length)
7939
+ } : null
7940
+ },
7941
+ llm,
7942
+ excluded: enumeration.excluded,
7943
+ pullFailures,
7944
+ cases: rows
7945
+ };
7946
+ writeFileSync2(join21(options.out, "batch-report.json"), `${JSON.stringify(report, null, 2)}
7947
+ `);
7948
+ writeFileSync2(join21(options.out, "batch-report.md"), renderBatchReport(report));
7949
+ return report;
7950
+ }
7951
+ function requireOption2(value, name) {
7952
+ if (value === void 0) throw new Error(`replay-verify-batch: ${name} is required`);
7953
+ return value;
7954
+ }
7955
+ function pct(value) {
7956
+ return value === null ? "\u2014" : `${(value * 100).toFixed(1)}%`;
7957
+ }
7958
+ function renderBatchReport(report) {
7959
+ const lines = [];
7960
+ const { headline, totals } = report;
7961
+ lines.push("# Replay-verify batch report");
7962
+ lines.push("");
7963
+ lines.push(`Generated ${report.generatedAt}.`);
7964
+ lines.push("");
7965
+ lines.push("## Headline");
7966
+ lines.push("");
7967
+ lines.push(
7968
+ `- **Replayability rate: ${pct(headline.replayabilityRate.value)}** (${headline.replayabilityRate.numerator}/${headline.replayabilityRate.denominator} replayable cases where the prefix replayed with \u226410% returncode divergence AND arm A reproduced the recorded returncode at the gold step k).`
7969
+ );
7970
+ lines.push(
7971
+ `- Signature-strict rate: ${pct(headline.signatureStrictRate.value)} (${headline.signatureStrictRate.numerator}/${headline.signatureStrictRate.denominator}; additionally requires the recorded error substring in arm A output).`
7972
+ );
7973
+ if (headline.fixFlipRate) {
7974
+ lines.push(
7975
+ `- **Fix-flip rate: ${pct(headline.fixFlipRate.value)}** (${headline.fixFlipRate.numerator}/${headline.fixFlipRate.denominator} arm-B-executed cases where the generated fix made the failure vanish).`
7976
+ );
7977
+ }
7978
+ if (headline.fixFlipRateNonzeroRc) {
7979
+ lines.push(
7980
+ `- Fix-flip rate on recorded-rc\u22600 cases: ${pct(headline.fixFlipRateNonzeroRc.value)} (${headline.fixFlipRateNonzeroRc.numerator}/${headline.fixFlipRateNonzeroRc.denominator}; real recorded failures \u2014 a gold step recorded with rc 0 flips vacuously).`
7981
+ );
7982
+ }
7983
+ lines.push("");
7984
+ lines.push("## Enumeration");
7985
+ lines.push("");
7986
+ lines.push(
7987
+ `${totals.labelEntries} label entries across ${report.corpora.length} corpora \u2192 ${totals.replayable} replayable (SWE-style docker image + \u22651 gold incorrect step), ${totals.executed} executed.`
7988
+ );
7989
+ lines.push("");
7990
+ lines.push("| exclusion reason | count |");
7991
+ lines.push("| --- | --- |");
7992
+ for (const [reason, count2] of Object.entries(totals.excludedByReason).sort((a, b) => b[1] - a[1])) {
7993
+ lines.push(`| ${reason} | ${count2} |`);
7994
+ }
7995
+ const submitEntries = Object.entries(totals.submitGoldsByCorpus);
7996
+ if (submitEntries.length > 0) {
7997
+ lines.push("");
7998
+ lines.push(
7999
+ "Submit-command golds are never counterfactual targets (a gold on the submit step marks a bad submit decision, not a failed command):"
8000
+ );
8001
+ lines.push("");
8002
+ lines.push("| corpus | cases excluded (all golds = submit) | golds skipped within replayable cases |");
8003
+ lines.push("| --- | --- | --- |");
8004
+ for (const [corpus, stats] of submitEntries.sort((a, b) => a[0].localeCompare(b[0]))) {
8005
+ lines.push(`| ${corpus} | ${stats.submitOnlyCases} | ${stats.goldsSkippedWithinReplayable} |`);
8006
+ }
8007
+ }
8008
+ if (report.pullFailures.length > 0) {
8009
+ lines.push("");
8010
+ lines.push("## Image pull/build failures");
8011
+ lines.push("");
8012
+ lines.push("| corpus | trajectory | image | error |");
8013
+ lines.push("| --- | --- | --- | --- |");
8014
+ for (const failure of report.pullFailures) {
8015
+ lines.push(
8016
+ `| ${failure.corpus} | ${failure.trajId} | \`${failure.image}\` | ${failure.error.replaceAll("|", "\\|")} |`
8017
+ );
8018
+ }
8019
+ }
8020
+ lines.push("");
8021
+ lines.push("## Per-case results");
8022
+ lines.push("");
8023
+ lines.push(
8024
+ "| corpus | trajectory | k | rc@k | prefix | div | div% | armA exit | rc match | sig match | replayed | fix | armB exit | vanished | wall s |"
8025
+ );
8026
+ lines.push(
8027
+ "| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |"
8028
+ );
8029
+ for (const row of report.cases) {
8030
+ const fix = row.fix;
8031
+ const fixCell = !fix ? "\u2014" : fix.sampledOut ? "sampled-out" : fix.llmError ? "llm-failed" : fix.armBError ? "armB-error" : "generated";
8032
+ lines.push(
8033
+ [
8034
+ row.corpus,
8035
+ row.trajId.length > 48 ? `${row.trajId.slice(0, 45)}\u2026` : row.trajId,
8036
+ row.k,
8037
+ row.recordedReturncodeAtK ?? "null",
8038
+ row.prefixExecuted ?? "\u2014",
8039
+ row.prefixDivergences ?? "\u2014",
8040
+ row.prefixDivergencePct ?? "\u2014",
8041
+ row.status === "ok" ? row.armAExit : row.status,
8042
+ row.armAReturncodeMatch ? "yes" : "no",
8043
+ row.armASignatureMatch ? "yes" : "no",
8044
+ row.replayed ? "**yes**" : "no",
8045
+ fixCell,
8046
+ fix?.armBExit ?? "\u2014",
8047
+ fix?.failureVanished === null || fix === null ? "\u2014" : fix.failureVanished ? "**yes**" : "no",
8048
+ (row.wallMs / 1e3).toFixed(1)
8049
+ ].join(" | ")
8050
+ );
8051
+ }
8052
+ if (report.llm) {
8053
+ lines.push("");
8054
+ lines.push("## LLM fix generation");
8055
+ lines.push("");
8056
+ lines.push(
8057
+ `Model ${report.llm.model}: ${report.llm.calls} calls (${report.llm.failures} failed), ${report.llm.promptTokens} prompt + ${report.llm.completionTokens} completion tokens.`
8058
+ );
8059
+ }
8060
+ lines.push("");
8061
+ return lines.join("\n");
8062
+ }
8063
+ function parseReplayBatchArgs(argv) {
8064
+ const corpora = [];
8065
+ const values = /* @__PURE__ */ new Map();
8066
+ let enumerateOnly = false;
8067
+ for (let i = 0; i < argv.length; i++) {
8068
+ const arg = argv[i];
8069
+ if (arg === "--help" || arg === "-h") return "help";
8070
+ if (arg === "--enumerate-only") {
8071
+ enumerateOnly = true;
8072
+ continue;
8073
+ }
8074
+ if (!arg.startsWith("--")) throw new Error(`unexpected positional argument: ${arg}`);
8075
+ const value = argv[++i];
8076
+ if (value === void 0) throw new Error(`missing value for ${arg}`);
8077
+ if (arg === "--corpus") corpora.push(parseCorpusFlag(value));
8078
+ else values.set(arg, value);
8079
+ }
8080
+ if (corpora.length === 0) {
8081
+ throw new Error("replay-verify-batch: at least one --corpus name=<labels>::<prepared> is required");
8082
+ }
8083
+ const out = values.get("--out");
8084
+ if (!out && !enumerateOnly) throw new Error("replay-verify-batch: --out is required");
8085
+ const optionalNumber = (flag) => {
8086
+ const v = values.get(flag);
8087
+ if (v === void 0) return void 0;
8088
+ const n = Number(v);
8089
+ if (!Number.isFinite(n)) throw new Error(`replay-verify-batch: ${flag} must be a number, got ${v}`);
8090
+ return n;
8091
+ };
8092
+ const fix = values.get("--fix") ?? "none";
8093
+ if (fix !== "none" && fix !== "generate") {
8094
+ throw new Error(`replay-verify-batch: --fix must be none or generate, got ${fix}`);
8095
+ }
8096
+ return {
8097
+ corpora,
8098
+ out: out ?? ".",
8099
+ fix,
8100
+ fixModel: values.get("--fix-model") ?? "glm-5.2",
8101
+ fixBaseUrl: values.get("--fix-base-url") ?? "https://api.z.ai/api/coding/paas/v4",
8102
+ fixApiKeyEnv: values.get("--fix-api-key-env") ?? "ZAI_GLM_API_KEY",
8103
+ maxFixCases: optionalNumber("--max-fix-cases") ?? 30,
8104
+ seed: optionalNumber("--seed") ?? 17,
8105
+ stepTimeoutMs: optionalNumber("--step-timeout"),
8106
+ prefixLimit: optionalNumber("--prefix-limit"),
8107
+ caseFilter: values.get("--case-filter"),
8108
+ caseLimit: optionalNumber("--case-limit"),
8109
+ baseUrl: values.get("--base-url") ?? process.env.SANDBOX_API_URL ?? "http://127.0.0.1:4097",
8110
+ apiKeyEnv: values.get("--api-key-env") ?? "SANDBOX_API_KEY",
8111
+ maxLifetimeSeconds: optionalNumber("--max-lifetime"),
8112
+ enumerateOnly
8113
+ };
8114
+ }
8115
+ function replayBatchUsage() {
8116
+ return `traces replay-verify-batch \u2014 measure replayability and fix-flip rates across gold-labeled corpora
8117
+
8118
+ Usage:
8119
+ traces replay-verify-batch \\
8120
+ --corpus NAME=<labels.json>::<prepared-dir> [--corpus ...] \\
8121
+ --out DIR [--fix none|generate] [--enumerate-only] \\
8122
+ [--fix-model glm-5.2] [--fix-base-url URL] [--fix-api-key-env ZAI_GLM_API_KEY] \\
8123
+ [--max-fix-cases 30] [--seed 17] [--step-timeout MS] [--prefix-limit N] \\
8124
+ [--case-filter SUBSTRING] [--case-limit N] \\
8125
+ [--base-url URL] [--api-key-env SANDBOX_API_KEY] [--max-lifetime SECONDS]
8126
+
8127
+ Replayable = the raw trajectory carries info.docker_config.base_image AND the
8128
+ labels mark at least one gold incorrect step that is a real mid-trajectory
8129
+ action. k = the first such gold step; golds whose action is the agent's
8130
+ submit command (COMPLETE_TASK_AND_SUBMIT_FINAL_OUTPUT) are skipped and
8131
+ counted, and cases whose golds are all submit steps are excluded as
8132
+ gold-only-submit-step. Excluded cases and image pull failures are report
8133
+ rows, never skipped silently. --enumerate-only prints the enumeration
8134
+ without touching a sandbox.
8135
+
8136
+ --fix generate runs ONE LLM call per arm-A-reproduced case (cap
8137
+ --max-fix-cases, seeded sample beyond it) and executes the corrected command
8138
+ as arm B in a fresh sandbox.
8139
+
8140
+ Outputs batch-report.json, batch-report.md, cases.jsonl (incremental), and one
8141
+ directory per case with the full replay-verify artifacts.
8142
+ See docs/replay-verify.md for orchestrator setup and honest limits.
8143
+ `;
8144
+ }
8145
+ async function cmdReplayVerifyBatch(argv) {
8146
+ const parsed = parseReplayBatchArgs(argv);
8147
+ if (parsed === "help") {
8148
+ process.stdout.write(replayBatchUsage());
8149
+ return;
8150
+ }
8151
+ if (parsed.enumerateOnly) {
8152
+ const enumeration = enumerateReplayableCases(parsed.corpora);
8153
+ process.stdout.write(
8154
+ `${JSON.stringify(
8155
+ {
8156
+ labelEntries: enumeration.labelEntryCount,
8157
+ replayable: enumeration.replayable.map((c) => ({
8158
+ corpus: c.corpus,
8159
+ trajId: c.trajId,
8160
+ image: c.image,
8161
+ cwd: c.cwd,
8162
+ cwdSource: c.cwdSource,
8163
+ k: c.k,
8164
+ submitGoldsSkipped: c.submitGoldsSkipped,
8165
+ recordedReturncodeAtK: c.recordedReturncodeAtK,
8166
+ goldIncorrectSteps: c.goldIncorrectSteps
8167
+ })),
8168
+ excluded: enumeration.excluded
8169
+ },
8170
+ null,
8171
+ 2
8172
+ )}
8173
+ `
8174
+ );
8175
+ return;
8176
+ }
8177
+ const apiKey = process.env[parsed.apiKeyEnv];
8178
+ if (!apiKey) {
8179
+ throw new Error(
8180
+ `replay-verify-batch: env var ${parsed.apiKeyEnv} is empty \u2014 pass --api-key-env or export it`
8181
+ );
8182
+ }
8183
+ let fixCaller;
8184
+ if (parsed.fix === "generate") {
8185
+ const fixApiKey = process.env[parsed.fixApiKeyEnv];
8186
+ if (!fixApiKey) {
8187
+ throw new Error(
8188
+ `replay-verify-batch: env var ${parsed.fixApiKeyEnv} is empty \u2014 pass --fix-api-key-env or export it`
8189
+ );
8190
+ }
8191
+ fixCaller = zaiChatCaller({
8192
+ baseUrl: parsed.fixBaseUrl,
8193
+ apiKey: fixApiKey,
8194
+ model: parsed.fixModel
8195
+ });
8196
+ }
8197
+ const report = await runReplayBatch({
8198
+ corpora: parsed.corpora,
8199
+ out: parsed.out,
8200
+ baseUrl: parsed.baseUrl,
8201
+ apiKey,
8202
+ fix: parsed.fix,
8203
+ fixCaller,
8204
+ fixModelLabel: parsed.fixModel,
8205
+ maxFixCases: parsed.maxFixCases,
8206
+ seed: parsed.seed,
8207
+ stepTimeoutMs: parsed.stepTimeoutMs,
8208
+ prefixLimit: parsed.prefixLimit,
8209
+ caseFilter: parsed.caseFilter,
8210
+ caseLimit: parsed.caseLimit,
8211
+ maxLifetimeSeconds: parsed.maxLifetimeSeconds,
8212
+ onProgress: (message) => process.stderr.write(`${message}
8213
+ `)
8214
+ });
8215
+ const { replayabilityRate, fixFlipRate } = report.headline;
8216
+ process.stderr.write(
8217
+ `replay-verify-batch: replayability ${replayabilityRate.numerator}/${replayabilityRate.denominator}` + (fixFlipRate ? `, fix-flip ${fixFlipRate.numerator}/${fixFlipRate.denominator}` : "") + ` \u2192 ${parsed.out}
8218
+ `
8219
+ );
8220
+ }
8221
+
7144
8222
  // src/external.ts
7145
8223
  import { spawn } from "child_process";
7146
8224
  import { readdir as readdir14, readFile as readFile7 } from "fs/promises";
@@ -7902,21 +8980,21 @@ function commandRedactor(spec) {
7902
8980
  }
7903
8981
 
7904
8982
  // src/hodoscope.ts
7905
- import { createHash as createHash8 } from "crypto";
8983
+ import { createHash as createHash9 } from "crypto";
7906
8984
  import { mkdir as mkdir3, mkdtemp as mkdtemp3, readFile as readFile8, readdir as readdir15, writeFile as writeFile4 } from "fs/promises";
7907
- import { endianness, tmpdir as tmpdir3 } from "os";
7908
- import { join as join20, resolve as resolve5 } from "path";
8985
+ import { endianness, tmpdir as tmpdir4 } from "os";
8986
+ import { join as join22, resolve as resolve5 } from "path";
7909
8987
  var HODOSCOPE_VERSION = "0.2.4";
7910
8988
  async function writeHodoscopeInput(otlpPath, options = {}) {
7911
8989
  const explicitDirectory = options.directory ? resolve5(options.directory) : void 0;
7912
- const directory = explicitDirectory ?? await mkdtemp3(join20(tmpdir3(), "traces-hodoscope-input-"));
8990
+ const directory = explicitDirectory ?? await mkdtemp3(join22(tmpdir4(), "traces-hodoscope-input-"));
7913
8991
  if (explicitDirectory) {
7914
8992
  await mkdir3(directory, { recursive: true });
7915
8993
  if ((await readdir15(directory)).length > 0) {
7916
8994
  throw new Error(`Hodoscope explicit directory must be empty: ${directory}`);
7917
8995
  }
7918
8996
  }
7919
- const samples = join20(directory, "samples");
8997
+ const samples = join22(directory, "samples");
7920
8998
  await mkdir3(samples);
7921
8999
  const rows = [];
7922
9000
  for await (const value of readJsonl(otlpPath)) rows.push(parseOpenInferenceRow(value));
@@ -7941,8 +9019,8 @@ async function writeHodoscopeInput(otlpPath, options = {}) {
7941
9019
  throw new Error(`Hodoscope trace '${traceId}' contains no assistant actions`);
7942
9020
  }
7943
9021
  actionCount += actions;
7944
- const file = `${createHash8("sha256").update(traceId).digest("hex").slice(0, 24)}.json`;
7945
- await writeFile4(join20(samples, file), `${JSON.stringify(trajectory, null, 2)}
9022
+ const file = `${createHash9("sha256").update(traceId).digest("hex").slice(0, 24)}.json`;
9023
+ await writeFile4(join22(samples, file), `${JSON.stringify(trajectory, null, 2)}
7946
9024
  `, "utf8");
7947
9025
  }
7948
9026
  return {
@@ -7964,12 +9042,12 @@ function hodoscopeAnalyzer(options = {}) {
7964
9042
  return {
7965
9043
  name: "hodoscope",
7966
9044
  async analyze(otlpPath, runOptions2 = {}) {
7967
- const outputRoot = options.outputDir ? resolve5(options.outputDir) : tmpdir3();
9045
+ const outputRoot = options.outputDir ? resolve5(options.outputDir) : tmpdir4();
7968
9046
  await mkdir3(outputRoot, { recursive: true });
7969
- const directory = await mkdtemp3(join20(outputRoot, "traces-hodoscope-"));
7970
- const input = join20(directory, "input");
7971
- const analysisFile = join20(directory, "analysis.hodoscope.json");
7972
- const samplesFile = join20(directory, "samples.json");
9047
+ const directory = await mkdtemp3(join22(outputRoot, "traces-hodoscope-"));
9048
+ const input = join22(directory, "input");
9049
+ const analysisFile = join22(directory, "analysis.hodoscope.json");
9050
+ const samplesFile = join22(directory, "samples.json");
7973
9051
  try {
7974
9052
  const prepared = await writeHodoscopeInput(otlpPath, {
7975
9053
  directory: input,
@@ -8600,9 +9678,19 @@ var CODING_REDACTION_RULES = [
8600
9678
  id: "private-key",
8601
9679
  pattern: /-----BEGIN(?:[A-Z ]+)?PRIVATE KEY-----[\s\S]*?-----END(?:[A-Z ]+)?PRIVATE KEY-----/g
8602
9680
  },
9681
+ // Match the whole identifier because underscores prevent a word boundary before a secret suffix.
8603
9682
  {
8604
9683
  id: "assigned-secret",
8605
- pattern: /\b(?:api[_-]?key|secret|token|password|passwd|access[_-]?token|client[_-]?secret)\b\s*[:=]\s*["']?[A-Za-z0-9._\-]{12,}["']?/gi
9684
+ pattern: /(?<![A-Za-z0-9_-])(?:[A-Za-z_][A-Za-z0-9_-]*)?(?:api[_-]?key|secret|token|password|passwd|access[_-]?token|client[_-]?secret)(?![A-Za-z0-9_-])\+?=(?:\$\((?:\\.|[^\\\r\n])*\)|\$\{(?:\\.|[^}\\\r\n])*\}|`(?:\\.|[^`\\\r\n])*`|\$?'(?:\\.|[^'\\\r\n])*'|\$?"(?:\\.|[^"\\\r\n])*"|\\[^\r\n]|[^\s;&|<>()"'`\\\r\n])+/gi
9685
+ },
9686
+ // Config-file secrets (YAML/INI line keys, JSON quoted keys) — the coverage the shell
9687
+ // rule above deliberately gave up to spare prose ("Narrative token: x") and type
9688
+ // annotations ("(token: string)"). The line anchor is the discriminator: config keys
9689
+ // start their line, prose colons never do; JSON keys are safe anywhere because both
9690
+ // sides are quoted. Identifiers must END in a secret word, same as the shell rule.
9691
+ {
9692
+ id: "config-secret",
9693
+ pattern: /(?:^[ \t]*["']?(?:[A-Za-z_][A-Za-z0-9_-]*)?(?:api[_-]?key|secret|token|password|passwd|access[_-]?token|client[_-]?secret)(?![A-Za-z0-9_-])["']?[ \t]*[:=][ \t]*|["'](?:[A-Za-z_][A-Za-z0-9_-]*)?(?:api[_-]?key|secret|token|password|passwd|access[_-]?token|client[_-]?secret)["']\s*:\s*)["']?[A-Za-z0-9._\-]{12,}["']?/gim
8606
9694
  },
8607
9695
  // Credentials embedded in URLs — common when a prompt pastes a curl/clone line.
8608
9696
  { id: "url-userinfo", pattern: /\b[a-z][a-z0-9+.-]*:\/\/[^\s/:@]+:[^\s/@]+@/gi },
@@ -8668,10 +9756,10 @@ async function applyRedactor(spans, redactor) {
8668
9756
  }
8669
9757
 
8670
9758
  // src/file-export.ts
8671
- import { createHash as createHash9 } from "crypto";
9759
+ import { createHash as createHash10 } from "crypto";
8672
9760
  import { mkdtemp as mkdtemp4, readFile as readFile9, writeFile as writeFile5 } from "fs/promises";
8673
- import { tmpdir as tmpdir4 } from "os";
8674
- import { join as join21 } from "path";
9761
+ import { tmpdir as tmpdir5 } from "os";
9762
+ import { join as join23 } from "path";
8675
9763
  import {
8676
9764
  firstNumberAttr,
8677
9765
  LLM_CACHED_TOKEN_ATTR_KEYS,
@@ -8706,7 +9794,7 @@ function stableJson(value) {
8706
9794
  });
8707
9795
  }
8708
9796
  function hashId(value, chars = 16) {
8709
- return createHash9("sha256").update(stableJson(value)).digest("hex").slice(0, chars);
9797
+ return createHash10("sha256").update(stableJson(value)).digest("hex").slice(0, chars);
8710
9798
  }
8711
9799
  function isoTime2(value) {
8712
9800
  if (typeof value === "string") {
@@ -9267,15 +10355,15 @@ async function exportTraceEvidenceFile(inputPath, opts = {}) {
9267
10355
  }
9268
10356
  async function writeTraceEvidenceExportFile(inputPath, outPath, opts = {}) {
9269
10357
  const result = await exportTraceEvidenceFile(inputPath, opts);
9270
- const path = outPath ?? join21(await mkdtemp4(join21(tmpdir4(), "traces-export-")), "spans.openinference.jsonl");
10358
+ const path = outPath ?? join23(await mkdtemp4(join23(tmpdir5(), "traces-export-")), "spans.openinference.jsonl");
9271
10359
  await writeFile5(path, serializeSpans(result.spans), "utf8");
9272
10360
  return { ...result, path };
9273
10361
  }
9274
10362
 
9275
10363
  // src/inspect.ts
9276
10364
  import { mkdtemp as mkdtemp5, readFile as readFile10, writeFile as writeFile6 } from "fs/promises";
9277
- import { tmpdir as tmpdir5 } from "os";
9278
- import { basename as basename13, join as join22 } from "path";
10365
+ import { tmpdir as tmpdir6 } from "os";
10366
+ import { basename as basename13, join as join24 } from "path";
9279
10367
  var severityRank = {
9280
10368
  high: 0,
9281
10369
  medium: 1,
@@ -9291,7 +10379,7 @@ function sessionLabel(row) {
9291
10379
  const id = row.session.sessionId.length > 12 ? row.session.sessionId.slice(0, 12) : row.session.sessionId;
9292
10380
  return `${row.session.harness}:${id} (${repoLabel(row)})`;
9293
10381
  }
9294
- function pct(value) {
10382
+ function pct2(value) {
9295
10383
  return `${(value * 100).toFixed(value >= 0.1 ? 0 : 1)}%`;
9296
10384
  }
9297
10385
  function topToolErrors(row) {
@@ -9370,7 +10458,7 @@ function addSessionFindings(index, findings) {
9370
10458
  area: "session",
9371
10459
  title: `High tool-error count/rate in ${errorSessions.length}/${index.totals.sessions} session(s)`,
9372
10460
  impact: errors,
9373
- evidence: errorSessions.slice(0, 5).map((row) => `${sessionLabel(row)}: ${row.metrics.erroredToolCallCount}/${row.metrics.toolCallCount} tool errors (${pct(row.metrics.toolErrorRate)}); ${topToolErrors(row)}`),
10461
+ evidence: errorSessions.slice(0, 5).map((row) => `${sessionLabel(row)}: ${row.metrics.erroredToolCallCount}/${row.metrics.toolCallCount} tool errors (${pct2(row.metrics.toolErrorRate)}); ${topToolErrors(row)}`),
9374
10462
  next: "Fix the most common failing command/tool first, then rerun `traces index` to confirm the error count drops.",
9375
10463
  refs: errorSessions.slice(0, 5).map((row) => row.session.path)
9376
10464
  });
@@ -9564,14 +10652,14 @@ async function readSessionIndexFile(path) {
9564
10652
  return parsed;
9565
10653
  }
9566
10654
  async function writeInspectionReportFile(report, outPath) {
9567
- const path = outPath ?? join22(await mkdtemp5(join22(tmpdir5(), "traces-inspect-")), "inspection-report.md");
10655
+ const path = outPath ?? join24(await mkdtemp5(join24(tmpdir6(), "traces-inspect-")), "inspection-report.md");
9568
10656
  await writeFile6(path, renderInspectionReport(report), "utf8");
9569
10657
  return path;
9570
10658
  }
9571
10659
 
9572
10660
  // src/adoption.ts
9573
10661
  import { readFile as readFile11 } from "fs/promises";
9574
- import { join as join23 } from "path";
10662
+ import { join as join25 } from "path";
9575
10663
  function toolName2(s) {
9576
10664
  const n = s.attributes["tool.name"];
9577
10665
  if (typeof n === "string") return n;
@@ -9691,7 +10779,7 @@ async function readLoopRuns(cwds, sessionIds) {
9691
10779
  seenCwds.add(cwd);
9692
10780
  const firstSourceByRun = /* @__PURE__ */ new Map();
9693
10781
  for (const artifactDir of [".agent", ".evolve"]) {
9694
- const path = join23(cwd, artifactDir, "skill-runs.jsonl");
10782
+ const path = join25(cwd, artifactDir, "skill-runs.jsonl");
9695
10783
  let raw;
9696
10784
  try {
9697
10785
  raw = await readFile11(path, "utf8");
@@ -11102,8 +12190,8 @@ function renderAdoption(ar) {
11102
12190
 
11103
12191
  // src/improvement.ts
11104
12192
  import { access, mkdir as mkdir4, mkdtemp as mkdtemp6, writeFile as writeFile7 } from "fs/promises";
11105
- import { tmpdir as tmpdir6 } from "os";
11106
- import { dirname as dirname7, join as join24, resolve as resolve6 } from "path";
12193
+ import { tmpdir as tmpdir7 } from "os";
12194
+ import { dirname as dirname7, join as join26, resolve as resolve6 } from "path";
11107
12195
  import { pathToFileURL } from "url";
11108
12196
  import {
11109
12197
  AnalystRegistry,
@@ -11610,13 +12698,13 @@ async function runTraceStoreInvestigation(opts) {
11610
12698
  };
11611
12699
  }
11612
12700
  async function writeTraceImprovementArtifacts(result, outDir) {
11613
- const directory = outDir ? resolve6(outDir) : await mkdtemp6(join24(tmpdir6(), "traces-improvement-"));
12701
+ const directory = outDir ? resolve6(outDir) : await mkdtemp6(join26(tmpdir7(), "traces-improvement-"));
11614
12702
  await mkdir4(directory, { recursive: true });
11615
12703
  const paths = {
11616
12704
  directory,
11617
- result: join24(directory, "result.json"),
11618
- evidence: join24(directory, "evidence.jsonl"),
11619
- report: join24(directory, "report.md"),
12705
+ result: join26(directory, "result.json"),
12706
+ evidence: join26(directory, "evidence.jsonl"),
12707
+ report: join26(directory, "report.md"),
11620
12708
  traces: result.otlpPath
11621
12709
  };
11622
12710
  const { report, ...machineResult } = result;
@@ -11628,11 +12716,11 @@ async function writeTraceImprovementArtifacts(result, outDir) {
11628
12716
  return paths;
11629
12717
  }
11630
12718
  async function runTraceImprovement(opts) {
11631
- const directory = opts.outDir ? resolve6(opts.outDir) : await mkdtemp6(join24(tmpdir6(), "traces-improvement-"));
12719
+ const directory = opts.outDir ? resolve6(opts.outDir) : await mkdtemp6(join26(tmpdir7(), "traces-improvement-"));
11632
12720
  await mkdir4(directory, { recursive: true });
11633
12721
  const investigation = await runTraceInvestigation({
11634
12722
  ...opts,
11635
- otlpOutPath: opts.otlpOutPath ?? join24(directory, "traces.otlp.jsonl")
12723
+ otlpOutPath: opts.otlpOutPath ?? join26(directory, "traces.otlp.jsonl")
11636
12724
  });
11637
12725
  const result = {
11638
12726
  ...investigation,
@@ -11649,7 +12737,7 @@ async function saveReport(path, report) {
11649
12737
  }
11650
12738
 
11651
12739
  // src/live.ts
11652
- import { createHash as createHash10 } from "crypto";
12740
+ import { createHash as createHash11 } from "crypto";
11653
12741
  var VERIFY_RE = /\b(test|tests|vitest|jest|pytest|go test|cargo test|tsc|typecheck|lint|biome|eslint|build|check:invariants|preflight)\b/i;
11654
12742
  var CHANGE_RE = /\b(apply_patch|edit|write|create|delete|rename|replace|update|git apply|sed -i|perl -pi|tee\s|cat\s+>)/i;
11655
12743
  var CLAIM_DONE_RE = /\b(done|fixed|complete|completed|implemented|merged|verified|all green|tests pass|ready|works now)\b/i;
@@ -11669,7 +12757,7 @@ var sleep = (ms2, signal) => new Promise((resolve7) => {
11669
12757
  signal?.addEventListener("abort", onAbort, { once: true });
11670
12758
  });
11671
12759
  function hash(parts) {
11672
- return createHash10("sha256").update(parts.join("\0")).digest("hex").slice(0, 16);
12760
+ return createHash11("sha256").update(parts.join("\0")).digest("hex").slice(0, 16);
11673
12761
  }
11674
12762
  function asString(value) {
11675
12763
  if (typeof value === "string" && value.trim()) return value;
@@ -12075,7 +13163,7 @@ async function streamSessions(opts) {
12075
13163
 
12076
13164
  // src/supervisor-run-context.ts
12077
13165
  import { readFile as readFile12, stat as stat14 } from "fs/promises";
12078
- import { basename as basename14, join as join25 } from "path";
13166
+ import { basename as basename14, join as join27 } from "path";
12079
13167
  import { materializeTreeView } from "@tangle-network/agent-runtime/kernel";
12080
13168
  import {
12081
13169
  NO_SOURCE_LIMITS
@@ -12236,10 +13324,10 @@ function parseInstant(v) {
12236
13324
  return Number.isFinite(t) ? t : null;
12237
13325
  }
12238
13326
  async function readFileRunContext(runDir, opts = {}) {
12239
- const journalText = await readMaybe(join25(runDir, SPAWN_JOURNAL_FILE));
13327
+ const journalText = await readMaybe(join27(runDir, SPAWN_JOURNAL_FILE));
12240
13328
  const journal = readRunContextJournal(journalText);
12241
- const coordination = readCoordinationLog(await readMaybe(join25(runDir, COORDINATION_LOG_FILE)));
12242
- const result = await readMaybe(join25(runDir, RESULT_FILE));
13329
+ const coordination = readCoordinationLog(await readMaybe(join27(runDir, COORDINATION_LOG_FILE)));
13330
+ const result = await readMaybe(join27(runDir, RESULT_FILE));
12243
13331
  return {
12244
13332
  runRef: runDir,
12245
13333
  instanceId: opts.instanceId !== void 0 ? opts.instanceId : journal.root,
@@ -12262,7 +13350,7 @@ async function readFileRunContext(runDir, opts = {}) {
12262
13350
  harnessWorkerTokens: null,
12263
13351
  harnessMissingReason: "the file run context journals each child\u2019s own settled spend, so no external harness join is used",
12264
13352
  limits: sourceLimits(journal, coordination),
12265
- rootTranscriptRef: journal.present ? join25(runDir, SPAWN_JOURNAL_FILE) : null,
13353
+ rootTranscriptRef: journal.present ? join27(runDir, SPAWN_JOURNAL_FILE) : null,
12266
13354
  traceCommand: `traces watch ${runDir}`
12267
13355
  };
12268
13356
  }
@@ -12270,7 +13358,7 @@ function fileRunContextSupervisorRunReader(runDir, opts = {}) {
12270
13358
  return { runRef: runDir, read: () => readFileRunContext(runDir, opts) };
12271
13359
  }
12272
13360
  async function isFileRunContextDir(runDir) {
12273
- return stat14(join25(runDir, SPAWN_JOURNAL_FILE)).then((s) => s.isFile()).catch(() => false);
13361
+ return stat14(join27(runDir, SPAWN_JOURNAL_FILE)).then((s) => s.isFile()).catch(() => false);
12274
13362
  }
12275
13363
  function flattenJournal(journal) {
12276
13364
  return journal.events.map((event) => {
@@ -12416,9 +13504,9 @@ function mergeRolls(a, b) {
12416
13504
  };
12417
13505
  }
12418
13506
  async function readRunContextSnapshot(runDir) {
12419
- const journal = readRunContextJournal(await readMaybe(join25(runDir, SPAWN_JOURNAL_FILE)));
12420
- const coordination = readCoordinationLog(await readMaybe(join25(runDir, COORDINATION_LOG_FILE)));
12421
- const resultText = await readMaybe(join25(runDir, RESULT_FILE));
13507
+ const journal = readRunContextJournal(await readMaybe(join27(runDir, SPAWN_JOURNAL_FILE)));
13508
+ const coordination = readCoordinationLog(await readMaybe(join27(runDir, COORDINATION_LOG_FILE)));
13509
+ const resultText = await readMaybe(join27(runDir, RESULT_FILE));
12422
13510
  const result = parseResult(resultText);
12423
13511
  const view = safeTreeView(journal.events);
12424
13512
  const nodes = view === null ? [] : orderNodes(view, journal);
@@ -12601,7 +13689,7 @@ function wrap(text2, indent) {
12601
13689
 
12602
13690
  // src/run-span-tree.ts
12603
13691
  import { readdir as readdir16 } from "fs/promises";
12604
- import { join as join26 } from "path";
13692
+ import { join as join28 } from "path";
12605
13693
  import {
12606
13694
  firstNumberAttr as firstNumberAttr3,
12607
13695
  LLM_COST_ATTR_KEYS as LLM_COST_ATTR_KEYS2,
@@ -12874,7 +13962,7 @@ async function findSpanSourceFiles(dir) {
12874
13962
  const found = [];
12875
13963
  const entries = await readdir16(dir, { withFileTypes: true }).catch(() => []);
12876
13964
  for (const entry of entries) {
12877
- const child = join26(dir, entry.name);
13965
+ const child = join28(dir, entry.name);
12878
13966
  if (entry.isFile()) {
12879
13967
  if (SPAN_FILE_PATTERN.test(entry.name)) found.push(child);
12880
13968
  continue;
@@ -12882,7 +13970,7 @@ async function findSpanSourceFiles(dir) {
12882
13970
  if (!entry.isDirectory()) continue;
12883
13971
  const nested = await readdir16(child, { withFileTypes: true }).catch(() => []);
12884
13972
  for (const inner of nested) {
12885
- if (inner.isFile() && SPAN_FILE_PATTERN.test(inner.name)) found.push(join26(child, inner.name));
13973
+ if (inner.isFile() && SPAN_FILE_PATTERN.test(inner.name)) found.push(join28(child, inner.name));
12886
13974
  }
12887
13975
  }
12888
13976
  return found.sort();
@@ -12890,7 +13978,7 @@ async function findSpanSourceFiles(dir) {
12890
13978
 
12891
13979
  // src/supervisor-run-watch.ts
12892
13980
  import { readdir as readdir17 } from "fs/promises";
12893
- import { join as join27 } from "path";
13981
+ import { join as join29 } from "path";
12894
13982
  function budget(node) {
12895
13983
  const parts = [`${int(node.budget.maxIterations)}it`, `${int(node.budget.maxTokens)}tok`];
12896
13984
  if (node.budget.maxUsd !== void 0) parts.push(`$${node.budget.maxUsd.toFixed(2)}`);
@@ -13034,7 +14122,7 @@ async function resolveRunContextDir(dir) {
13034
14122
  const candidates = [];
13035
14123
  for (const entry of entries) {
13036
14124
  if (!entry.isDirectory()) continue;
13037
- const child = join27(dir, entry.name);
14125
+ const child = join29(dir, entry.name);
13038
14126
  if (await isFileRunContextDir(child)) candidates.push(child);
13039
14127
  }
13040
14128
  if (candidates.length === 1) return candidates[0];
@@ -13485,11 +14573,11 @@ async function collectSessionWorkflow(adapter, seeds, options = {}) {
13485
14573
  }
13486
14574
 
13487
14575
  // src/session-selection.ts
13488
- import { createHash as createHash11 } from "crypto";
14576
+ import { createHash as createHash12 } from "crypto";
13489
14577
  import { createReadStream as createReadStream2 } from "fs";
13490
14578
  async function fileSha256(path, signal) {
13491
14579
  signal?.throwIfAborted();
13492
- const hash2 = createHash11("sha256");
14580
+ const hash2 = createHash12("sha256");
13493
14581
  try {
13494
14582
  for await (const chunk of createReadStream2(path, { signal })) {
13495
14583
  signal?.throwIfAborted();
@@ -13517,7 +14605,7 @@ async function sourceSnapshot(adapter, ref, signal) {
13517
14605
  signal?.throwIfAborted();
13518
14606
  files.push({ path, sha256: await fileSha256(path, signal) });
13519
14607
  }
13520
- const sha2562 = files.length === 1 ? files[0].sha256 : createHash11("sha256").update(JSON.stringify(files)).digest("hex");
14608
+ const sha2562 = files.length === 1 ? files[0].sha256 : createHash12("sha256").update(JSON.stringify(files)).digest("hex");
13521
14609
  return { sha256: sha2562, files };
13522
14610
  }
13523
14611
  function summarizeWorkflows(workflows) {
@@ -13613,8 +14701,8 @@ async function collectSessionSelection(groups, options = {}) {
13613
14701
 
13614
14702
  // src/session-index.ts
13615
14703
  import { mkdtemp as mkdtemp7, readdir as readdir18, readFile as readFile13, stat as stat16, writeFile as writeFile8 } from "fs/promises";
13616
- import { tmpdir as tmpdir7 } from "os";
13617
- import { dirname as dirname8, join as join28 } from "path";
14704
+ import { tmpdir as tmpdir8 } from "os";
14705
+ import { dirname as dirname8, join as join30 } from "path";
13618
14706
  function indexRow(record2) {
13619
14707
  return {
13620
14708
  session: record2.session,
@@ -13687,7 +14775,7 @@ async function findContextRoot(cwd) {
13687
14775
  if (!s) return null;
13688
14776
  if (!s.isDirectory()) current = dirname8(current);
13689
14777
  for (; ; ) {
13690
- if (await pathStat2(join28(current, ".git")) || await dirExists(join28(current, ".evolve")) || await fileExists(join28(current, "AGENTS.md")) || await fileExists(join28(current, "CLAUDE.md"))) {
14778
+ if (await pathStat2(join30(current, ".git")) || await dirExists(join30(current, ".evolve")) || await fileExists(join30(current, "AGENTS.md")) || await fileExists(join30(current, "CLAUDE.md"))) {
13691
14779
  return current;
13692
14780
  }
13693
14781
  const parent = dirname8(current);
@@ -13755,7 +14843,7 @@ async function walkMarkdown(root, kind, maxFiles = 100) {
13755
14843
  }
13756
14844
  for (const entry of entries) {
13757
14845
  if (out.length >= maxFiles) return;
13758
- const path = join28(dir, entry.name);
14846
+ const path = join30(dir, entry.name);
13759
14847
  if (entry.isDirectory()) {
13760
14848
  await walk(path);
13761
14849
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -13770,20 +14858,20 @@ async function walkMarkdown(root, kind, maxFiles = 100) {
13770
14858
  async function collectContextRoot(root) {
13771
14859
  const files = [];
13772
14860
  for (const name of ["AGENTS.md", "CLAUDE.md", "GEMINI.md"]) {
13773
- const summary = await summarizeFile(join28(root, name), "instruction-doc");
14861
+ const summary = await summarizeFile(join30(root, name), "instruction-doc");
13774
14862
  if (summary) files.push(summary);
13775
14863
  }
13776
- const evolve = join28(root, ".evolve");
14864
+ const evolve = join30(root, ".evolve");
13777
14865
  for (const name of ["skill-runs.jsonl", "governor.jsonl", "experiments.jsonl"]) {
13778
- const summary = await summarizeFile(join28(evolve, name), "evolve-jsonl");
14866
+ const summary = await summarizeFile(join30(evolve, name), "evolve-jsonl");
13779
14867
  if (summary) files.push(summary);
13780
14868
  }
13781
14869
  for (const name of ["scorecard.json"]) {
13782
- const summary = await summarizeFile(join28(evolve, name), "evolve-json");
14870
+ const summary = await summarizeFile(join30(evolve, name), "evolve-json");
13783
14871
  if (summary) files.push(summary);
13784
14872
  }
13785
- files.push(...await walkMarkdown(join28(evolve, "reflections"), "reflection"));
13786
- files.push(...await walkMarkdown(join28(evolve, "handoffs"), "handoff"));
14873
+ files.push(...await walkMarkdown(join30(evolve, "reflections"), "reflection"));
14874
+ files.push(...await walkMarkdown(join30(evolve, "handoffs"), "handoff"));
13787
14875
  return { root, files: files.sort((a, b) => a.path.localeCompare(b.path)) };
13788
14876
  }
13789
14877
  async function collectContextIndex(records) {
@@ -13829,7 +14917,7 @@ function serializeSessionIndex(index) {
13829
14917
  `;
13830
14918
  }
13831
14919
  async function writeSessionIndexFile(index, outPath) {
13832
- const path = outPath ?? join28(await mkdtemp7(join28(tmpdir7(), "traces-index-")), "session-index.json");
14920
+ const path = outPath ?? join30(await mkdtemp7(join30(tmpdir8(), "traces-index-")), "session-index.json");
13833
14921
  await writeFile8(path, serializeSessionIndex(index), "utf8");
13834
14922
  return path;
13835
14923
  }
@@ -13837,11 +14925,11 @@ async function writeSessionIndexFile(index, outPath) {
13837
14925
  // src/upload-state.ts
13838
14926
  import { mkdir as mkdir5, readFile as readFile14, writeFile as writeFile9 } from "fs/promises";
13839
14927
  import { homedir as homedir12 } from "os";
13840
- import { dirname as dirname9, join as join29 } from "path";
14928
+ import { dirname as dirname9, join as join31 } from "path";
13841
14929
  import { contentHash } from "@tangle-network/agent-eval";
13842
14930
  function stateFile() {
13843
- const base = process.env.XDG_STATE_HOME ?? join29(homedir12(), ".local", "state");
13844
- return join29(base, "tangle-traces", "uploaded.json");
14931
+ const base = process.env.XDG_STATE_HOME ?? join31(homedir12(), ".local", "state");
14932
+ return join31(base, "tangle-traces", "uploaded.json");
13845
14933
  }
13846
14934
  function uploadKey(harness, sessionId) {
13847
14935
  return `${harness}:${sessionId}`;
@@ -13873,7 +14961,7 @@ function alreadyUploaded(state, harness, sessionId, hash2) {
13873
14961
  import { readFile as readFile15 } from "fs/promises";
13874
14962
  import { createRequire as createRequire2 } from "module";
13875
14963
  import { hostname as hostname2 } from "os";
13876
- import { basename as basename15, join as join30 } from "path";
14964
+ import { basename as basename15, join as join32 } from "path";
13877
14965
  import { hostedClientFromEnv } from "@tangle-network/agent-eval/hosted";
13878
14966
  import { REDACTION_VERSION } from "@tangle-network/agent-eval/traces";
13879
14967
  var require2 = createRequire2(import.meta.url);
@@ -13897,7 +14985,7 @@ async function planUpload(opts = {}) {
13897
14985
  async function gitBranch(cwd) {
13898
14986
  if (!cwd) return void 0;
13899
14987
  try {
13900
- const head = await readFile15(join30(cwd, ".git", "HEAD"), "utf8");
14988
+ const head = await readFile15(join32(cwd, ".git", "HEAD"), "utf8");
13901
14989
  const m = head.match(/^ref:\s*refs\/heads\/(.+)$/m);
13902
14990
  return m?.[1]?.trim();
13903
14991
  } catch {
@@ -14153,6 +15241,25 @@ export {
14153
15241
  parseReplayVerifyArgs,
14154
15242
  replayVerifyUsage,
14155
15243
  cmdReplayVerify,
15244
+ parseCorpusFlag,
15245
+ SUBMIT_ACTION_SIGNATURE,
15246
+ isSubmitAction,
15247
+ readLabelEntries,
15248
+ goldIncorrectSteps,
15249
+ resolveCaseResources,
15250
+ enumerateReplayableCases,
15251
+ zaiChatCaller,
15252
+ buildFixPrompt,
15253
+ extractFixCommand,
15254
+ generateFixCommand,
15255
+ derivedImageTag,
15256
+ dockerImagePreparer,
15257
+ seededSample,
15258
+ runReplayBatch,
15259
+ renderBatchReport,
15260
+ parseReplayBatchArgs,
15261
+ replayBatchUsage,
15262
+ cmdReplayVerifyBatch,
14156
15263
  runCommand,
14157
15264
  commandAnalyzer,
14158
15265
  haloAnalyzer,