@algosuite/vo-mcp 0.2.0-beta.67 → 0.2.0-beta.69

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.
@@ -314,21 +314,21 @@ function backupConfigOnce(configPath) {
314
314
  copyFileSync(configPath, backupPath);
315
315
  return backupPath;
316
316
  }
317
- function writeFileAtomic(path22, content) {
318
- sweepStaleTempFiles(path22);
319
- const temp = `${path22}.vo-mcp-tmp-${process.pid}-${Date.now()}`;
317
+ function writeFileAtomic(path23, content) {
318
+ sweepStaleTempFiles(path23);
319
+ const temp = `${path23}.vo-mcp-tmp-${process.pid}-${Date.now()}`;
320
320
  try {
321
321
  writeFileSync2(temp, content, { encoding: "utf8", mode: 384 });
322
- if (existsSync3(path22)) {
322
+ if (existsSync3(path23)) {
323
323
  try {
324
- chmodSync2(temp, statSync(path22).mode & 511);
324
+ chmodSync2(temp, statSync(path23).mode & 511);
325
325
  } catch {
326
326
  }
327
327
  }
328
328
  let lastErr = null;
329
329
  for (let attempt = 0; attempt < RENAME_RETRIES; attempt += 1) {
330
330
  try {
331
- renameSync(temp, path22);
331
+ renameSync(temp, path23);
332
332
  return;
333
333
  } catch (err) {
334
334
  lastErr = err;
@@ -337,7 +337,7 @@ function writeFileAtomic(path22, content) {
337
337
  sleepSync(RENAME_RETRY_MS);
338
338
  }
339
339
  }
340
- writeFileSync2(path22, content, "utf8");
340
+ writeFileSync2(path23, content, "utf8");
341
341
  try {
342
342
  unlinkSync2(temp);
343
343
  } catch {
@@ -503,8 +503,8 @@ function tablePath(line) {
503
503
  function tableSections(lines) {
504
504
  const starts = [];
505
505
  for (let index = 0; index < lines.length; index += 1) {
506
- const path22 = tablePath(lines[index] ?? "");
507
- if (path22) starts.push({ path: path22, start: index });
506
+ const path23 = tablePath(lines[index] ?? "");
507
+ if (path23) starts.push({ path: path23, start: index });
508
508
  }
509
509
  return starts.map((section, index) => ({
510
510
  ...section,
@@ -696,11 +696,11 @@ function resolveLinuxConfigHome(home, env2) {
696
696
  const configured = env2["XDG_CONFIG_HOME"]?.trim();
697
697
  return configured && isAbsolute2(configured) ? configured : join5(home, ".config");
698
698
  }
699
- function launcherIsCurrent(path22, desiredContent, label, log2) {
700
- if (!existsSync6(path22)) return false;
701
- if (readFileSync5(path22, "utf8") === desiredContent) return true;
702
- const backupPath = `${path22}.backup-${Date.now()}`;
703
- copyFileSync2(path22, backupPath);
699
+ function launcherIsCurrent(path23, desiredContent, label, log2) {
700
+ if (!existsSync6(path23)) return false;
701
+ if (readFileSync5(path23, "utf8") === desiredContent) return true;
702
+ const backupPath = `${path23}.backup-${Date.now()}`;
703
+ copyFileSync2(path23, backupPath);
704
704
  log2(` Backed up existing ${label} to: ${backupPath}`);
705
705
  return false;
706
706
  }
@@ -916,17 +916,17 @@ function resolveDesktopConfigPath(home, plat, appData) {
916
916
  }
917
917
  return join6(home, ".config", "Claude", "claude_desktop_config.json");
918
918
  }
919
- function readClaudeConfig(path22) {
920
- if (!existsSync7(path22)) return { kind: "absent", config: {}, mtimeMs: null };
919
+ function readClaudeConfig(path23) {
920
+ if (!existsSync7(path23)) return { kind: "absent", config: {}, mtimeMs: null };
921
921
  for (let attempt = 0; attempt < 3; attempt += 1) {
922
- const before = statSync3(path22).mtimeMs;
922
+ const before = statSync3(path23).mtimeMs;
923
923
  let raw;
924
924
  try {
925
- raw = readFileSync6(path22, "utf8");
925
+ raw = readFileSync6(path23, "utf8");
926
926
  } catch {
927
927
  return { kind: "invalid", config: {}, mtimeMs: before };
928
928
  }
929
- if (!existsSync7(path22) || statSync3(path22).mtimeMs !== before) continue;
929
+ if (!existsSync7(path23) || statSync3(path23).mtimeMs !== before) continue;
930
930
  const text = raw.replace(/^\uFEFF/u, "");
931
931
  if (!text.trim()) return { kind: "empty", config: {}, mtimeMs: before };
932
932
  try {
@@ -938,9 +938,9 @@ function readClaudeConfig(path22) {
938
938
  }
939
939
  return { kind: "invalid", config: {}, mtimeMs: null };
940
940
  }
941
- function writeClaudeConfig(path22, config) {
942
- mkdirSync6(dirname4(path22), { recursive: true });
943
- writeFileAtomic(path22, `${JSON.stringify(config, null, 2)}
941
+ function writeClaudeConfig(path23, config) {
942
+ mkdirSync6(dirname4(path23), { recursive: true });
943
+ writeFileAtomic(path23, `${JSON.stringify(config, null, 2)}
944
944
  `);
945
945
  }
946
946
  function carriedEntryKeys(entry) {
@@ -1344,6 +1344,7 @@ var init_pnpm_link_detach = __esm({
1344
1344
 
1345
1345
  // src/runner/process-runner.mjs
1346
1346
  import { spawn } from "node:child_process";
1347
+ import path2 from "node:path";
1347
1348
  function sleepMs(ms) {
1348
1349
  if (ms <= 0) return Promise.resolve();
1349
1350
  return new Promise((resolve3) => setTimeout(resolve3, ms));
@@ -1351,15 +1352,25 @@ function sleepMs(ms) {
1351
1352
  async function spawnUtility(command, args, timeoutMs) {
1352
1353
  return await new Promise((resolve3) => {
1353
1354
  let settled = false;
1355
+ let stdout = "";
1356
+ let stderr = "";
1354
1357
  const finish = (result) => {
1355
1358
  if (settled) return;
1356
1359
  settled = true;
1357
- resolve3(result);
1360
+ resolve3({ stdout, stderr, ...result });
1358
1361
  };
1359
1362
  const child = spawn(command, args, {
1360
- stdio: "ignore",
1363
+ stdio: ["ignore", "pipe", "pipe"],
1361
1364
  windowsHide: true
1362
1365
  });
1366
+ child.stdout?.setEncoding("utf8");
1367
+ child.stderr?.setEncoding("utf8");
1368
+ child.stdout?.on("data", (chunk) => {
1369
+ stdout = `${stdout}${chunk}`.slice(-MAX_UTILITY_OUTPUT_CHARS);
1370
+ });
1371
+ child.stderr?.on("data", (chunk) => {
1372
+ stderr = `${stderr}${chunk}`.slice(-MAX_UTILITY_OUTPUT_CHARS);
1373
+ });
1363
1374
  let timer = null;
1364
1375
  if (timeoutMs > 0) {
1365
1376
  timer = setTimeout(() => {
@@ -1381,27 +1392,48 @@ async function spawnUtility(command, args, timeoutMs) {
1381
1392
  });
1382
1393
  });
1383
1394
  }
1395
+ function windowsTaskkillExe(env2 = process.env) {
1396
+ const systemRoot = env2.SystemRoot || env2.WINDIR || "C:\\Windows";
1397
+ return path2.win32.join(systemRoot, "System32", "taskkill.exe");
1398
+ }
1384
1399
  async function killProcessTreeDefault(pid, options = {}) {
1385
1400
  if (!Number.isInteger(pid) || pid <= 0) return;
1386
- if (process.platform === "win32") {
1387
- await spawnUtility(
1388
- "taskkill",
1401
+ if ((options.platform || process.platform) === "win32") {
1402
+ const utilityRunner = options.spawnUtility || spawnUtility;
1403
+ return await utilityRunner(
1404
+ windowsTaskkillExe(options.env || process.env),
1389
1405
  ["/pid", String(pid), "/t", "/f"],
1390
1406
  options.timeoutMs ?? DEFAULT_FORCE_KILL_TIMEOUT_MS
1391
1407
  );
1392
- return;
1393
1408
  }
1394
1409
  try {
1395
1410
  process.kill(pid, "SIGKILL");
1396
1411
  } catch {
1397
1412
  }
1398
1413
  }
1414
+ function cleanupResultError(result) {
1415
+ if (!result || typeof result !== "object") return null;
1416
+ if (result.error) {
1417
+ return result.error instanceof Error ? result.error : new Error(String(result.error));
1418
+ }
1419
+ const detail = String(result.stderr || result.stdout || "").trim().replace(/\s+/g, " ").slice(-400);
1420
+ if (result.timedOut) {
1421
+ return new Error(`process-tree cleanup utility timed out${detail ? `: ${detail}` : ""}`);
1422
+ }
1423
+ if ("status" in result && result.status !== 0) {
1424
+ return new Error(
1425
+ `process-tree cleanup utility exited with status ${String(result.status)}${detail ? `: ${detail}` : ""}`
1426
+ );
1427
+ }
1428
+ return null;
1429
+ }
1399
1430
  async function runProcess(command, args = [], options = {}) {
1400
1431
  const timeoutMs = options.timeoutMs ?? DEFAULT_PROCESS_TIMEOUT_MS;
1401
1432
  const killGraceMs = options.killGraceMs ?? DEFAULT_KILL_GRACE_MS;
1402
1433
  const forceKillTimeoutMs = options.forceKillTimeoutMs ?? DEFAULT_FORCE_KILL_TIMEOUT_MS;
1434
+ const platform4 = options.platform || process.platform;
1403
1435
  const spawnImpl = options.spawnImpl || spawn;
1404
- const killProcessTree2 = options.killProcessTree || killProcessTreeDefault;
1436
+ const killProcessTree2 = options.killProcessTree || ((pid, killOptions) => killProcessTreeDefault(pid, { ...killOptions, platform: platform4 }));
1405
1437
  const spawnOptions = {
1406
1438
  cwd: options.cwd,
1407
1439
  env: options.env,
@@ -1417,6 +1449,9 @@ async function runProcess(command, args = [], options = {}) {
1417
1449
  let graceTimeout = null;
1418
1450
  let forceKillTimeout = null;
1419
1451
  let child = null;
1452
+ let parentExit = null;
1453
+ let cleanupInFlight = false;
1454
+ let pendingTerminalResult = null;
1420
1455
  const clearTimers = () => {
1421
1456
  if (timeout) clearTimeout(timeout);
1422
1457
  if (graceTimeout) clearTimeout(graceTimeout);
@@ -1428,8 +1463,33 @@ async function runProcess(command, args = [], options = {}) {
1428
1463
  clearTimers();
1429
1464
  resolve3({ stdout, stderr, timedOut, ...result });
1430
1465
  };
1466
+ const handleTerminalResult = (result) => {
1467
+ const normalizedResult = timedOut && typeof result.status === "number" ? { ...result, exitStatus: result.status, status: null } : result;
1468
+ if (timedOut && cleanupInFlight) {
1469
+ pendingTerminalResult = normalizedResult;
1470
+ return;
1471
+ }
1472
+ finish(normalizedResult);
1473
+ };
1474
+ const exitedParentOwnershipError = () => {
1475
+ return new Error(
1476
+ `process parent exited before timeout cleanup, but inherited stdio remained open; PID ${String(child?.pid ?? "unknown")} may no longer belong to the spawned process`
1477
+ );
1478
+ };
1431
1479
  const beginForceKill = () => {
1432
- if (settled || !child?.pid) return;
1480
+ if (settled) return;
1481
+ if (parentExit) {
1482
+ finish({
1483
+ status: null,
1484
+ error: exitedParentOwnershipError()
1485
+ });
1486
+ return;
1487
+ }
1488
+ if (!Number.isInteger(child?.pid) || child.pid <= 0) {
1489
+ finish({ status: null, error: new Error("process timed out before a process-tree cleanup PID was available") });
1490
+ return;
1491
+ }
1492
+ cleanupInFlight = true;
1433
1493
  forceKillTimeout = setTimeout(() => {
1434
1494
  finish({
1435
1495
  status: null,
@@ -1437,17 +1497,36 @@ async function runProcess(command, args = [], options = {}) {
1437
1497
  });
1438
1498
  }, forceKillTimeoutMs);
1439
1499
  forceKillTimeout.unref?.();
1440
- void (async () => {
1441
- try {
1442
- await killProcessTree2(child.pid, { timeoutMs: forceKillTimeoutMs });
1443
- } catch (error) {
1500
+ const cleanupPromise = Promise.resolve().then(
1501
+ () => {
1502
+ if (settled) return void 0;
1503
+ if (parentExit) throw exitedParentOwnershipError();
1504
+ return killProcessTree2(child.pid, { timeoutMs: forceKillTimeoutMs });
1505
+ }
1506
+ );
1507
+ void cleanupPromise.then(
1508
+ (result) => {
1509
+ const error = cleanupResultError(result);
1510
+ cleanupInFlight = false;
1511
+ if (error) {
1512
+ finish({ status: null, error });
1513
+ return;
1514
+ }
1515
+ if (pendingTerminalResult) finish(pendingTerminalResult);
1516
+ },
1517
+ (error) => {
1518
+ cleanupInFlight = false;
1444
1519
  finish({ status: null, error });
1445
1520
  }
1446
- })();
1521
+ );
1447
1522
  };
1448
1523
  const beginTimeout = () => {
1449
1524
  if (settled || !child) return;
1450
1525
  timedOut = true;
1526
+ if (platform4 === "win32") {
1527
+ beginForceKill();
1528
+ return;
1529
+ }
1451
1530
  try {
1452
1531
  child.kill("SIGTERM");
1453
1532
  } catch (error) {
@@ -1473,8 +1552,11 @@ async function runProcess(command, args = [], options = {}) {
1473
1552
  stderr += chunk;
1474
1553
  if (typeof options.onStderr === "function") options.onStderr(chunk);
1475
1554
  });
1476
- child.on("error", (error) => finish({ status: null, error }));
1477
- child.on("close", (code, signal) => finish({ status: code, signal }));
1555
+ child.on("error", (error) => handleTerminalResult({ status: null, error }));
1556
+ child.on("exit", (code, signal) => {
1557
+ parentExit = { code, signal };
1558
+ });
1559
+ child.on("close", (code, signal) => handleTerminalResult({ status: code, signal }));
1478
1560
  if (timeoutMs > 0) {
1479
1561
  timeout = setTimeout(beginTimeout, timeoutMs);
1480
1562
  timeout.unref?.();
@@ -1499,13 +1581,14 @@ function summarizeProcessFailure(result, options = {}) {
1499
1581
  const timedOutText = result?.timedOut ? "timed out" : "";
1500
1582
  return [errorText, timedOutText, tail].filter(Boolean).join("; ").slice(0, limit) || `command exited with status ${String(result?.status ?? "unknown")}`;
1501
1583
  }
1502
- var DEFAULT_PROCESS_TIMEOUT_MS, DEFAULT_KILL_GRACE_MS, DEFAULT_FORCE_KILL_TIMEOUT_MS;
1584
+ var DEFAULT_PROCESS_TIMEOUT_MS, DEFAULT_KILL_GRACE_MS, DEFAULT_FORCE_KILL_TIMEOUT_MS, MAX_UTILITY_OUTPUT_CHARS;
1503
1585
  var init_process_runner = __esm({
1504
1586
  "src/runner/process-runner.mjs"() {
1505
1587
  "use strict";
1506
1588
  DEFAULT_PROCESS_TIMEOUT_MS = 10 * 60 * 1e3;
1507
1589
  DEFAULT_KILL_GRACE_MS = 5e3;
1508
1590
  DEFAULT_FORCE_KILL_TIMEOUT_MS = 15e3;
1591
+ MAX_UTILITY_OUTPUT_CHARS = 4096;
1509
1592
  }
1510
1593
  });
1511
1594
 
@@ -1616,14 +1699,18 @@ async function addWorktreeWithRetry({
1616
1699
  sleep: sleep3 = sleepMs,
1617
1700
  removeDir = async (target) => {
1618
1701
  await fsp2.rm(target, { recursive: true, force: true });
1619
- }
1702
+ },
1703
+ baseRef = "origin/main"
1620
1704
  }) {
1705
+ if (baseRef !== "origin/main" && !/^[0-9a-f]{40}$/u.test(baseRef)) {
1706
+ throw new Error("worktree base ref must be origin/main or an exact lowercase SHA");
1707
+ }
1621
1708
  const maxAttempts = Math.max(1, Math.floor(attempts));
1622
1709
  let result = null;
1623
1710
  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
1624
1711
  result = await runner(
1625
1712
  "git",
1626
- ["worktree", "add", "-b", branchName, worktreeDir, "origin/main"],
1713
+ ["worktree", "add", "-b", branchName, worktreeDir, baseRef],
1627
1714
  { cwd: root, timeoutMs }
1628
1715
  );
1629
1716
  if (result.status === 0 && await pathExistsFn(worktreeDir)) {
@@ -1648,7 +1735,7 @@ var init_worktree_add = __esm({
1648
1735
  // src/runner/pnpm-canonical-health.mjs
1649
1736
  import fs from "node:fs";
1650
1737
  import fsp3 from "node:fs/promises";
1651
- import path2 from "node:path";
1738
+ import path3 from "node:path";
1652
1739
  function readJsonFile(file) {
1653
1740
  if (!fs.existsSync(file)) return null;
1654
1741
  try {
@@ -1658,7 +1745,7 @@ function readJsonFile(file) {
1658
1745
  }
1659
1746
  }
1660
1747
  function packageJsonAt(root) {
1661
- return readJsonFile(path2.join(root, "package.json"));
1748
+ return readJsonFile(path3.join(root, "package.json"));
1662
1749
  }
1663
1750
  function dependencyNamesForPackage(root) {
1664
1751
  const manifest = packageJsonAt(root);
@@ -1670,7 +1757,7 @@ function dependencyNamesForPackage(root) {
1670
1757
  ])].sort();
1671
1758
  }
1672
1759
  function packageEntryPath(nodeModulesDir, packageName) {
1673
- return path2.join(nodeModulesDir, ...String(packageName || "").split("/"));
1760
+ return path3.join(nodeModulesDir, ...String(packageName || "").split("/"));
1674
1761
  }
1675
1762
  function rootFsApi() {
1676
1763
  return {
@@ -1699,7 +1786,7 @@ async function inspectDependencyEntry(nodeModulesDir, packageName, fsApi) {
1699
1786
  } catch {
1700
1787
  return { issue: `missing or dangling dependency link: ${entry}` };
1701
1788
  }
1702
- const packageJsonPath = path2.join(resolved, "package.json");
1789
+ const packageJsonPath = path3.join(resolved, "package.json");
1703
1790
  if (!await pathExists3(packageJsonPath, fsApi)) {
1704
1791
  return { issue: `dependency target is missing package.json: ${entry} -> ${resolved}` };
1705
1792
  }
@@ -1711,11 +1798,11 @@ async function inspectDependencyEntry(nodeModulesDir, packageName, fsApi) {
1711
1798
  return { issue: null };
1712
1799
  }
1713
1800
  function representativePackages(root, linkedWorkspaceDirs, sampleLimit) {
1714
- const packages = [{ nodeModulesDir: path2.join(root, "node_modules"), packageRoot: root }];
1801
+ const packages = [{ nodeModulesDir: path3.join(root, "node_modules"), packageRoot: root }];
1715
1802
  for (const relativeDir of linkedWorkspaceDirs) {
1716
1803
  packages.push({
1717
- nodeModulesDir: path2.join(root, relativeDir, "node_modules"),
1718
- packageRoot: path2.join(root, relativeDir)
1804
+ nodeModulesDir: path3.join(root, relativeDir, "node_modules"),
1805
+ packageRoot: path3.join(root, relativeDir)
1719
1806
  });
1720
1807
  }
1721
1808
  const representatives = [];
@@ -1727,7 +1814,7 @@ function representativePackages(root, linkedWorkspaceDirs, sampleLimit) {
1727
1814
  return representatives;
1728
1815
  }
1729
1816
  function canonicalNodeModulesQuarantineRoot(root) {
1730
- return path2.join(root, ".agent-worktrees", QUARANTINE_DIRNAME);
1817
+ return path3.join(root, ".agent-worktrees", QUARANTINE_DIRNAME);
1731
1818
  }
1732
1819
  async function inspectCanonicalNodeModulesHealth({
1733
1820
  root,
@@ -1738,8 +1825,8 @@ async function inspectCanonicalNodeModulesHealth({
1738
1825
  sampleLimit = DEFAULT_SAMPLE_LIMIT
1739
1826
  }) {
1740
1827
  const issues = [];
1741
- const nodeModulesDir = path2.join(root, "node_modules");
1742
- const markerPath = path2.join(nodeModulesDir, ".vo-deps-state.json");
1828
+ const nodeModulesDir = path3.join(root, "node_modules");
1829
+ const markerPath = path3.join(nodeModulesDir, ".vo-deps-state.json");
1743
1830
  if (!await pathExists3(nodeModulesDir, fsApi)) {
1744
1831
  issues.push(`missing root node_modules: ${nodeModulesDir}`);
1745
1832
  } else {
@@ -1748,8 +1835,8 @@ async function inspectCanonicalNodeModulesHealth({
1748
1835
  issues.push(`root node_modules must be a real directory: ${nodeModulesDir}`);
1749
1836
  }
1750
1837
  }
1751
- if (!await pathExists3(path2.join(nodeModulesDir, ".pnpm"), fsApi)) {
1752
- issues.push(`missing root .pnpm store view: ${path2.join(nodeModulesDir, ".pnpm")}`);
1838
+ if (!await pathExists3(path3.join(nodeModulesDir, ".pnpm"), fsApi)) {
1839
+ issues.push(`missing root .pnpm store view: ${path3.join(nodeModulesDir, ".pnpm")}`);
1753
1840
  }
1754
1841
  if (requireMarker) {
1755
1842
  try {
@@ -1762,7 +1849,7 @@ async function inspectCanonicalNodeModulesHealth({
1762
1849
  }
1763
1850
  }
1764
1851
  for (const relativeDir of linkedWorkspaceDirs) {
1765
- const workspaceNodeModules = path2.join(root, relativeDir, "node_modules");
1852
+ const workspaceNodeModules = path3.join(root, relativeDir, "node_modules");
1766
1853
  if (!await pathExists3(workspaceNodeModules, fsApi)) {
1767
1854
  issues.push(`missing workspace node_modules: ${workspaceNodeModules}`);
1768
1855
  continue;
@@ -1785,17 +1872,17 @@ async function inspectCanonicalNodeModulesHealth({
1785
1872
  async function quarantineCanonicalNodeModules(root, issues, options = {}) {
1786
1873
  const fsApi = options.fsApi || rootFsApi();
1787
1874
  const logger = options.logger || console.error;
1788
- const nodeModulesDir = path2.join(root, "node_modules");
1875
+ const nodeModulesDir = path3.join(root, "node_modules");
1789
1876
  const quarantineRoot = canonicalNodeModulesQuarantineRoot(root);
1790
1877
  if (!await pathExists3(nodeModulesDir, fsApi)) return null;
1791
1878
  await fsApi.mkdir(quarantineRoot, { recursive: true });
1792
1879
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1793
1880
  let attempt = 0;
1794
1881
  for (; ; ) {
1795
- const quarantinePath = path2.join(quarantineRoot, `node_modules-${stamp}-${process.pid}-${attempt}`);
1882
+ const quarantinePath = path3.join(quarantineRoot, `node_modules-${stamp}-${process.pid}-${attempt}`);
1796
1883
  try {
1797
1884
  await fsApi.rename(nodeModulesDir, quarantinePath);
1798
- const metadataPath = path2.join(quarantinePath, ".vo-runner-quarantine.json");
1885
+ const metadataPath = path3.join(quarantinePath, ".vo-runner-quarantine.json");
1799
1886
  await fsApi.writeFile(metadataPath, `${JSON.stringify({
1800
1887
  issues,
1801
1888
  originalPath: nodeModulesDir,
@@ -1821,22 +1908,22 @@ var init_pnpm_canonical_health = __esm({
1821
1908
 
1822
1909
  // src/runner/pnpm-command.mjs
1823
1910
  import fs2 from "node:fs";
1824
- import path3 from "node:path";
1911
+ import path4 from "node:path";
1825
1912
  function isWindowsDriveOrUnc(value) {
1826
1913
  return WINDOWS_DRIVE_OR_UNC_RE.test(String(value || ""));
1827
1914
  }
1828
1915
  function portableDirname(value) {
1829
- if (isWindowsDriveOrUnc(value)) return path3.win32.dirname(value);
1830
- if (path3.posix.isAbsolute(value)) return path3.posix.dirname(value);
1831
- return path3.dirname(value);
1916
+ if (isWindowsDriveOrUnc(value)) return path4.win32.dirname(value);
1917
+ if (path4.posix.isAbsolute(value)) return path4.posix.dirname(value);
1918
+ return path4.dirname(value);
1832
1919
  }
1833
1920
  function portableJoin(root, ...segments) {
1834
- if (isWindowsDriveOrUnc(root)) return path3.win32.join(root, ...segments);
1835
- if (path3.posix.isAbsolute(root)) return path3.posix.join(root, ...segments);
1836
- return path3.join(root, ...segments);
1921
+ if (isWindowsDriveOrUnc(root)) return path4.win32.join(root, ...segments);
1922
+ if (path4.posix.isAbsolute(root)) return path4.posix.join(root, ...segments);
1923
+ return path4.join(root, ...segments);
1837
1924
  }
1838
1925
  function readPackageManager(root) {
1839
- const packagePath = path3.join(root, "package.json");
1926
+ const packagePath = path4.join(root, "package.json");
1840
1927
  if (!fs2.existsSync(packagePath)) return "";
1841
1928
  try {
1842
1929
  return String(JSON.parse(fs2.readFileSync(packagePath, "utf8"))?.packageManager || "").trim();
@@ -1856,13 +1943,13 @@ function pnpmSelector(root) {
1856
1943
  const packageManager = readPackageManager(root);
1857
1944
  const version = validatedPnpmVersionToken(root);
1858
1945
  if (packageManager && !version) {
1859
- throw new Error(`[vo-mcp runner] invalid pnpm packageManager token in ${path3.join(root, "package.json")}`);
1946
+ throw new Error(`[vo-mcp runner] invalid pnpm packageManager token in ${path4.join(root, "package.json")}`);
1860
1947
  }
1861
1948
  return version ? `pnpm@${version}` : "pnpm";
1862
1949
  }
1863
1950
  function whereResults(result) {
1864
1951
  if (result?.status !== 0) return [];
1865
- return String(result.stdout || "").split(/\r?\n/u).map((line) => line.trim()).filter((line) => path3.win32.isAbsolute(line));
1952
+ return String(result.stdout || "").split(/\r?\n/u).map((line) => line.trim()).filter((line) => path4.win32.isAbsolute(line));
1866
1953
  }
1867
1954
  async function resolveWindowsNativeCommand(command, runner) {
1868
1955
  const result = await runner("where", [command], { timeoutMs: 1e4 });
@@ -1874,7 +1961,7 @@ function trustedCorepackCandidates(options) {
1874
1961
  const roots = [portableDirname(execPath)];
1875
1962
  for (const key of ["ProgramW6432", "ProgramFiles", "ProgramFiles(x86)"]) {
1876
1963
  const programFiles = String(env2[key] || "").trim();
1877
- if (isWindowsDriveOrUnc(programFiles) || path3.posix.isAbsolute(programFiles)) {
1964
+ if (isWindowsDriveOrUnc(programFiles) || path4.posix.isAbsolute(programFiles)) {
1878
1965
  roots.push(portableJoin(programFiles, "nodejs"));
1879
1966
  }
1880
1967
  }
@@ -1953,7 +2040,7 @@ var init_pnpm_command = __esm({
1953
2040
 
1954
2041
  // src/runner/pnpm-materialize.mjs
1955
2042
  import fsp4 from "node:fs/promises";
1956
- import path4 from "node:path";
2043
+ import path5 from "node:path";
1957
2044
  function linkType() {
1958
2045
  return process.platform === "win32" ? "junction" : "dir";
1959
2046
  }
@@ -1965,13 +2052,13 @@ async function realpathOrThrow(target, fsApi) {
1965
2052
  }
1966
2053
  }
1967
2054
  function shouldMapToWorktree(resolvedTarget, canonicalRoot) {
1968
- const relative = path4.relative(canonicalRoot, resolvedTarget).replace(/\\/g, "/");
2055
+ const relative = path5.relative(canonicalRoot, resolvedTarget).replace(/\\/g, "/");
1969
2056
  return Boolean(relative && !relative.startsWith("..") && !relative.split("/").includes("node_modules"));
1970
2057
  }
1971
2058
  async function resolveEntryTarget(sourceEntry, canonicalRoot, worktreeRoot, fsApi) {
1972
2059
  const resolved = await realpathOrThrow(sourceEntry, fsApi);
1973
2060
  if (!shouldMapToWorktree(resolved, canonicalRoot)) return resolved;
1974
- const mapped = path4.join(worktreeRoot, path4.relative(canonicalRoot, resolved));
2061
+ const mapped = path5.join(worktreeRoot, path5.relative(canonicalRoot, resolved));
1975
2062
  if (!await fsApi.pathExists(mapped)) {
1976
2063
  throw new Error(`[vo-mcp runner] task-local workspace target is missing for dependency link: ${mapped}`);
1977
2064
  }
@@ -1982,7 +2069,7 @@ async function ensureLinkedDirectory(source, target, fsApi) {
1982
2069
  if (await fsApi.realpath(target) === await fsApi.realpath(source)) return;
1983
2070
  throw new Error(`[vo-mcp runner] refusing to overwrite existing dependency path: ${target}`);
1984
2071
  }
1985
- await fsApi.mkdir(path4.dirname(target), { recursive: true });
2072
+ await fsApi.mkdir(path5.dirname(target), { recursive: true });
1986
2073
  await fsApi.symlink(source, target, linkType());
1987
2074
  if (await fsApi.realpath(target) !== await fsApi.realpath(source)) {
1988
2075
  throw new Error(`[vo-mcp runner] dependency link validation failed for ${target}`);
@@ -1996,8 +2083,8 @@ async function maybeYield2(state) {
1996
2083
  async function copyDirRecursive(sourceDir, targetDir, fsApi, yieldState) {
1997
2084
  await fsApi.mkdir(targetDir, { recursive: true });
1998
2085
  for (const entry of await fsApi.readdir(sourceDir, { withFileTypes: true })) {
1999
- const source = path4.join(sourceDir, entry.name);
2000
- const target = path4.join(targetDir, entry.name);
2086
+ const source = path5.join(sourceDir, entry.name);
2087
+ const target = path5.join(targetDir, entry.name);
2001
2088
  await maybeYield2(yieldState);
2002
2089
  if (entry.isDirectory()) {
2003
2090
  await copyDirRecursive(source, target, fsApi, yieldState);
@@ -2009,17 +2096,17 @@ async function copyDirRecursive(sourceDir, targetDir, fsApi, yieldState) {
2009
2096
  async function materializeEntry(sourceEntry, targetEntry, canonicalRoot, worktreeRoot, options) {
2010
2097
  await options.beforeEntry?.(sourceEntry, targetEntry);
2011
2098
  const stat3 = await options.fsApi.lstat(sourceEntry);
2012
- if (stat3.isDirectory() && !stat3.isSymbolicLink() && path4.basename(sourceEntry) === ".bin") {
2099
+ if (stat3.isDirectory() && !stat3.isSymbolicLink() && path5.basename(sourceEntry) === ".bin") {
2013
2100
  await copyDirRecursive(sourceEntry, targetEntry, options.fsApi, options.yieldState);
2014
2101
  return;
2015
2102
  }
2016
- if (stat3.isDirectory() && !stat3.isSymbolicLink() && path4.basename(sourceEntry).startsWith("@")) {
2103
+ if (stat3.isDirectory() && !stat3.isSymbolicLink() && path5.basename(sourceEntry).startsWith("@")) {
2017
2104
  await options.fsApi.mkdir(targetEntry, { recursive: true });
2018
2105
  for (const nested of await options.fsApi.readdir(sourceEntry, { withFileTypes: true })) {
2019
2106
  await maybeYield2(options.yieldState);
2020
2107
  await materializeEntry(
2021
- path4.join(sourceEntry, nested.name),
2022
- path4.join(targetEntry, nested.name),
2108
+ path5.join(sourceEntry, nested.name),
2109
+ path5.join(targetEntry, nested.name),
2023
2110
  canonicalRoot,
2024
2111
  worktreeRoot,
2025
2112
  options
@@ -2032,7 +2119,7 @@ async function materializeEntry(sourceEntry, targetEntry, canonicalRoot, worktre
2032
2119
  await ensureLinkedDirectory(resolvedTarget, targetEntry, options.fsApi);
2033
2120
  return;
2034
2121
  }
2035
- await options.fsApi.mkdir(path4.dirname(targetEntry), { recursive: true });
2122
+ await options.fsApi.mkdir(path5.dirname(targetEntry), { recursive: true });
2036
2123
  await options.fsApi.copyFile(sourceEntry, targetEntry);
2037
2124
  }
2038
2125
  function createPnpmFsApi(pathExists6) {
@@ -2056,8 +2143,8 @@ async function materializeNodeModulesForest(sourceNodeModules, targetNodeModules
2056
2143
  for (const entry of await options.fsApi.readdir(sourceNodeModules, { withFileTypes: true })) {
2057
2144
  await maybeYield2(options.yieldState);
2058
2145
  await materializeEntry(
2059
- path4.join(sourceNodeModules, entry.name),
2060
- path4.join(targetNodeModules, entry.name),
2146
+ path5.join(sourceNodeModules, entry.name),
2147
+ path5.join(targetNodeModules, entry.name),
2061
2148
  canonicalRoot,
2062
2149
  worktreeRoot,
2063
2150
  options
@@ -2074,15 +2161,15 @@ var init_pnpm_materialize = __esm({
2074
2161
  import { createHash as createHash2 } from "node:crypto";
2075
2162
  import fs3 from "node:fs";
2076
2163
  import fsp5 from "node:fs/promises";
2077
- import path5 from "node:path";
2164
+ import path6 from "node:path";
2078
2165
  function hashText(text) {
2079
2166
  return createHash2("sha256").update(String(text)).digest("hex");
2080
2167
  }
2081
2168
  function statePath(root) {
2082
- return path5.join(root, ".agent-worktrees", "runner-pnpm-hydration.json");
2169
+ return path6.join(root, ".agent-worktrees", "runner-pnpm-hydration.json");
2083
2170
  }
2084
2171
  function packageJson(root) {
2085
- const packagePath = path5.join(root, "package.json");
2172
+ const packagePath = path6.join(root, "package.json");
2086
2173
  if (!fs3.existsSync(packagePath)) return null;
2087
2174
  try {
2088
2175
  return JSON.parse(fs3.readFileSync(packagePath, "utf8"));
@@ -2099,7 +2186,7 @@ async function pathExists4(target) {
2099
2186
  }
2100
2187
  }
2101
2188
  function lockfileHash(root) {
2102
- const lockPath = path5.join(root, "pnpm-lock.yaml");
2189
+ const lockPath = path6.join(root, "pnpm-lock.yaml");
2103
2190
  if (!fs3.existsSync(lockPath)) return "";
2104
2191
  return hashText(fs3.readFileSync(lockPath, "utf8"));
2105
2192
  }
@@ -2114,7 +2201,7 @@ function readHydrationState(root) {
2114
2201
  }
2115
2202
  function writeHydrationState(root, state) {
2116
2203
  const file = statePath(root);
2117
- fs3.mkdirSync(path5.dirname(file), { recursive: true });
2204
+ fs3.mkdirSync(path6.dirname(file), { recursive: true });
2118
2205
  fs3.writeFileSync(file, `${JSON.stringify({
2119
2206
  ...state,
2120
2207
  stateVersion: 2,
@@ -2123,7 +2210,7 @@ function writeHydrationState(root, state) {
2123
2210
  `, "utf8");
2124
2211
  }
2125
2212
  function voDepsStatePath(root) {
2126
- return path5.join(root, "node_modules", ".vo-deps-state.json");
2213
+ return path6.join(root, "node_modules", ".vo-deps-state.json");
2127
2214
  }
2128
2215
  async function ensureVoDepsState(root, expectedHash, fsApi = fsp5) {
2129
2216
  const marker = voDepsStatePath(root);
@@ -2133,7 +2220,7 @@ async function ensureVoDepsState(root, expectedHash, fsApi = fsp5) {
2133
2220
  } catch {
2134
2221
  }
2135
2222
  const temp = `${marker}.tmp-${process.pid}-${Date.now()}`;
2136
- await fsApi.mkdir(path5.dirname(marker), { recursive: true });
2223
+ await fsApi.mkdir(path6.dirname(marker), { recursive: true });
2137
2224
  await fsApi.writeFile(temp, `${JSON.stringify({ lockfileHash: expectedHash, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
2138
2225
  `, "utf8");
2139
2226
  await fsApi.rename(temp, marker);
@@ -2160,7 +2247,7 @@ function workspacePatternsFromPackageJson(root) {
2160
2247
  return [];
2161
2248
  }
2162
2249
  function workspacePatternsFromPnpmWorkspace(root) {
2163
- const workspacePath = path5.join(root, "pnpm-workspace.yaml");
2250
+ const workspacePath = path6.join(root, "pnpm-workspace.yaml");
2164
2251
  if (!fs3.existsSync(workspacePath)) return [];
2165
2252
  const lines = fs3.readFileSync(workspacePath, "utf8").split(/\r?\n/u);
2166
2253
  const patterns = [];
@@ -2219,8 +2306,8 @@ function collectPackageDirs(root, options = {}) {
2219
2306
  while (stack.length > 0) {
2220
2307
  const current = stack.pop();
2221
2308
  if (!current) continue;
2222
- const relativeDir = path5.relative(root, current.dir).replace(/\\/g, "/");
2223
- if (relativeDir && fs3.existsSync(path5.join(current.dir, "package.json"))) {
2309
+ const relativeDir = path6.relative(root, current.dir).replace(/\\/g, "/");
2310
+ if (relativeDir && fs3.existsSync(path6.join(current.dir, "package.json"))) {
2224
2311
  found.push(relativeDir);
2225
2312
  if (found.length >= maxDirs) break;
2226
2313
  }
@@ -2228,7 +2315,7 @@ function collectPackageDirs(root, options = {}) {
2228
2315
  for (const entry of fs3.readdirSync(current.dir, { withFileTypes: true })) {
2229
2316
  if (!entry.isDirectory()) continue;
2230
2317
  if (IGNORED_SCAN_DIRS.has(entry.name)) continue;
2231
- stack.push({ dir: path5.join(current.dir, entry.name), depth: current.depth + 1 });
2318
+ stack.push({ dir: path6.join(current.dir, entry.name), depth: current.depth + 1 });
2232
2319
  }
2233
2320
  }
2234
2321
  return found.sort();
@@ -2252,8 +2339,8 @@ async function runInstall(root, options = {}) {
2252
2339
  }
2253
2340
  }
2254
2341
  async function hasReadyNodeModules(root) {
2255
- const nodeModules = path5.join(root, "node_modules");
2256
- return await pathExists4(path5.join(nodeModules, ".modules.yaml")) || await pathExists4(path5.join(nodeModules, ".pnpm"));
2342
+ const nodeModules = path6.join(root, "node_modules");
2343
+ return await pathExists4(path6.join(nodeModules, ".modules.yaml")) || await pathExists4(path6.join(nodeModules, ".pnpm"));
2257
2344
  }
2258
2345
  function hydrationFsApi(overrides = {}) {
2259
2346
  return { ...createPnpmFsApi(pathExists4), ...overrides };
@@ -2290,7 +2377,7 @@ async function ensurePnpmHydration(root, options = {}) {
2290
2377
  expectedHash: hash,
2291
2378
  requireMarker: false
2292
2379
  });
2293
- if (!health.healthy && await fsApi.pathExists(path5.join(root, "node_modules"))) {
2380
+ if (!health.healthy && await fsApi.pathExists(path6.join(root, "node_modules"))) {
2294
2381
  quarantine = await quarantineCanonicalNodeModules(root, health.issues, {
2295
2382
  fsApi,
2296
2383
  logger: options.logger
@@ -2302,7 +2389,7 @@ async function ensurePnpmHydration(root, options = {}) {
2302
2389
  }
2303
2390
  const linkedWorkspaceDirs = [];
2304
2391
  for (const relativeDir of workspaceDirs) {
2305
- if (await pathExists4(path5.join(root, relativeDir, "node_modules"))) {
2392
+ if (await pathExists4(path6.join(root, relativeDir, "node_modules"))) {
2306
2393
  linkedWorkspaceDirs.push(relativeDir);
2307
2394
  }
2308
2395
  }
@@ -2338,18 +2425,18 @@ async function linkHydratedNodeModules({ root, worktreeDir, hydration, options =
2338
2425
  yieldEvery: Math.max(1, options.yieldEvery ?? DEFAULT_YIELD_EVERY2)
2339
2426
  }
2340
2427
  };
2341
- recordOwnedNodeModulesRoot(dependencyOwnership, path5.join(worktreeDir, "node_modules"));
2428
+ recordOwnedNodeModulesRoot(dependencyOwnership, path6.join(worktreeDir, "node_modules"));
2342
2429
  await materializeNodeModulesForest(
2343
- path5.join(root, "node_modules"),
2344
- path5.join(worktreeDir, "node_modules"),
2430
+ path6.join(root, "node_modules"),
2431
+ path6.join(worktreeDir, "node_modules"),
2345
2432
  root,
2346
2433
  worktreeDir,
2347
2434
  materializeOptions
2348
2435
  );
2349
2436
  for (const relativeDir of hydration.linkedWorkspaceDirs) {
2350
- const sourceNodeModules = path5.join(root, relativeDir, "node_modules");
2351
- const targetNodeModules = path5.join(worktreeDir, relativeDir, "node_modules");
2352
- if (!await fsApi.pathExists(path5.join(worktreeDir, relativeDir))) continue;
2437
+ const sourceNodeModules = path6.join(root, relativeDir, "node_modules");
2438
+ const targetNodeModules = path6.join(worktreeDir, relativeDir, "node_modules");
2439
+ if (!await fsApi.pathExists(path6.join(worktreeDir, relativeDir))) continue;
2353
2440
  recordOwnedNodeModulesRoot(dependencyOwnership, targetNodeModules);
2354
2441
  await materializeNodeModulesForest(sourceNodeModules, targetNodeModules, root, worktreeDir, materializeOptions);
2355
2442
  }
@@ -2387,28 +2474,28 @@ var init_pnpm_hydration = __esm({
2387
2474
 
2388
2475
  // src/runner/worktree-paths.mjs
2389
2476
  import { createHash as createHash3 } from "node:crypto";
2390
- import path6 from "node:path";
2477
+ import path7 from "node:path";
2391
2478
  function samePath2(left, right) {
2392
- const a = path6.resolve(left);
2393
- const b = path6.resolve(right);
2479
+ const a = path7.resolve(left);
2480
+ const b = path7.resolve(right);
2394
2481
  return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
2395
2482
  }
2396
2483
  function worktreePoolForRoot(root, { clonesRootDir = process.env.VO_CODE_RUNNER_CLONES_ROOT || "" } = {}) {
2397
- const canonicalRoot = path6.resolve(root);
2484
+ const canonicalRoot = path7.resolve(root);
2398
2485
  if (clonesRootDir) {
2399
- const clonePool = path6.resolve(clonesRootDir);
2400
- if (samePath2(path6.dirname(canonicalRoot), clonePool)) {
2401
- return path6.join(clonePool, ".agent-worktrees", path6.basename(canonicalRoot));
2486
+ const clonePool = path7.resolve(clonesRootDir);
2487
+ if (samePath2(path7.dirname(canonicalRoot), clonePool)) {
2488
+ return path7.join(clonePool, ".agent-worktrees", path7.basename(canonicalRoot));
2402
2489
  }
2403
2490
  }
2404
- return path6.join(canonicalRoot, ".agent-worktrees");
2491
+ return path7.join(canonicalRoot, ".agent-worktrees");
2405
2492
  }
2406
2493
  function worktreeDirForName(root, worktreeName, options = {}) {
2407
2494
  const leaf = createHash3("sha256").update(String(worktreeName)).digest("hex").slice(0, 16);
2408
- return path6.join(worktreePoolForRoot(root, options), leaf);
2495
+ return path7.join(worktreePoolForRoot(root, options), leaf);
2409
2496
  }
2410
2497
  function recoveryLedgerPathForRoot(root, options = {}) {
2411
- return path6.join(worktreePoolForRoot(root, options), "recovery-ledger.jsonl");
2498
+ return path7.join(worktreePoolForRoot(root, options), "recovery-ledger.jsonl");
2412
2499
  }
2413
2500
  var init_worktree_paths = __esm({
2414
2501
  "src/runner/worktree-paths.mjs"() {
@@ -2419,7 +2506,7 @@ var init_worktree_paths = __esm({
2419
2506
  // src/runner/worktree-cleanup.mjs
2420
2507
  import fs4 from "node:fs";
2421
2508
  import fsp6 from "node:fs/promises";
2422
- import path7 from "node:path";
2509
+ import path8 from "node:path";
2423
2510
  function stateFromEntry(entry) {
2424
2511
  return {
2425
2512
  root: entry.root,
@@ -2460,14 +2547,14 @@ function pruneSuccessfulStates(nowMs = Date.now()) {
2460
2547
  function assertTrackedCleanupPath(entry) {
2461
2548
  const poolRoot = worktreePoolForRoot(entry.root);
2462
2549
  const expected = worktreeDirForName(entry.root, entry.worktreeName);
2463
- const resolvedPool = path7.resolve(poolRoot);
2464
- const resolvedTarget = path7.resolve(entry.worktreeDir);
2465
- if (!resolvedTarget.startsWith(`${resolvedPool}${path7.sep}`)) {
2550
+ const resolvedPool = path8.resolve(poolRoot);
2551
+ const resolvedTarget = path8.resolve(entry.worktreeDir);
2552
+ if (!resolvedTarget.startsWith(`${resolvedPool}${path8.sep}`)) {
2466
2553
  const error = new Error(`cleanup refused outside managed pool: ${entry.worktreeDir}`);
2467
2554
  error.cleanupFatal = true;
2468
2555
  throw error;
2469
2556
  }
2470
- if (resolvedTarget !== path7.resolve(expected)) {
2557
+ if (resolvedTarget !== path8.resolve(expected)) {
2471
2558
  const error = new Error(`cleanup refused for unexpected tracked path: ${entry.worktreeDir}`);
2472
2559
  error.cleanupFatal = true;
2473
2560
  throw error;
@@ -2481,8 +2568,8 @@ async function worktreeStillRegistered2(root, worktreeDir, gitRunner) {
2481
2568
  if (result.status !== 0) {
2482
2569
  throw new Error(`git worktree list --porcelain failed during cleanup verification: ${summarizeProcessFailure(result)}`);
2483
2570
  }
2484
- const registered = String(result.stdout || "").split(/\r?\n/u).filter((line) => line.startsWith("worktree ")).map((line) => path7.resolve(line.slice("worktree ".length).trim()));
2485
- return registered.includes(path7.resolve(worktreeDir));
2571
+ const registered = String(result.stdout || "").split(/\r?\n/u).filter((line) => line.startsWith("worktree ")).map((line) => path8.resolve(line.slice("worktree ".length).trim()));
2572
+ return registered.includes(path8.resolve(worktreeDir));
2486
2573
  }
2487
2574
  function cleanupBackoff(attempt) {
2488
2575
  return 250 * attempt;
@@ -2592,7 +2679,7 @@ function scheduleTrackedCleanup(entry, options = {}) {
2592
2679
  }
2593
2680
  function pendingCleanupDirs() {
2594
2681
  return new Set(
2595
- [...CLEANUP_STATES.values()].filter((state) => state.status === "pending").map((state) => path7.resolve(state.worktreeDir))
2682
+ [...CLEANUP_STATES.values()].filter((state) => state.status === "pending").map((state) => path8.resolve(state.worktreeDir))
2596
2683
  );
2597
2684
  }
2598
2685
  var CLEANUP_STATES, CLEANUP_PROMISES, SUCCESS_HISTORY_LIMIT, SUCCESS_HISTORY_TTL_MS, DEFAULT_CLEANUP_ATTEMPTS;
@@ -2613,13 +2700,13 @@ var init_worktree_cleanup = __esm({
2613
2700
  // src/runner/task-root-prepare.mjs
2614
2701
  import fs5 from "node:fs";
2615
2702
  import fsp7 from "node:fs/promises";
2616
- import path8 from "node:path";
2703
+ import path9 from "node:path";
2617
2704
  function prepLockDir(root) {
2618
- return path8.join(root, ".agent-worktrees", "runner-root-prep.lock");
2705
+ return path9.join(root, ".agent-worktrees", "runner-root-prep.lock");
2619
2706
  }
2620
2707
  function readLockMeta(lockDir) {
2621
2708
  try {
2622
- return JSON.parse(fs5.readFileSync(path8.join(lockDir, "owner.json"), "utf8"));
2709
+ return JSON.parse(fs5.readFileSync(path9.join(lockDir, "owner.json"), "utf8"));
2623
2710
  } catch {
2624
2711
  return null;
2625
2712
  }
@@ -2639,9 +2726,9 @@ async function acquirePrepLock(root, options = {}) {
2639
2726
  const staleMs = options.lockStaleMs ?? PREP_LOCK_STALE_MS;
2640
2727
  const sleep3 = options.sleep || sleepMs;
2641
2728
  const lockDir = prepLockDir(root);
2642
- const ownerPath = path8.join(lockDir, "owner.json");
2729
+ const ownerPath = path9.join(lockDir, "owner.json");
2643
2730
  const deadline = nowMs() + waitMs;
2644
- fs5.mkdirSync(path8.dirname(lockDir), { recursive: true });
2731
+ fs5.mkdirSync(path9.dirname(lockDir), { recursive: true });
2645
2732
  for (; ; ) {
2646
2733
  try {
2647
2734
  fs5.mkdirSync(lockDir);
@@ -2691,16 +2778,16 @@ async function gitText(root, args, options = {}) {
2691
2778
  }
2692
2779
  function canonicalRecoveryDir(root, options = {}) {
2693
2780
  const now = options.now || (() => /* @__PURE__ */ new Date());
2694
- return path8.join(
2695
- options.managedPool || path8.join(root, ".agent-worktrees"),
2781
+ return path9.join(
2782
+ options.managedPool || path9.join(root, ".agent-worktrees"),
2696
2783
  ".canonical-recovery",
2697
2784
  `preexisting-${now().toISOString().replace(/[:.]/gu, "-")}`
2698
2785
  );
2699
2786
  }
2700
2787
  function canonicalPath(root, relative) {
2701
- const resolvedRoot = path8.resolve(root);
2702
- const target = path8.resolve(root, relative);
2703
- const prefix = `${resolvedRoot}${path8.sep}`;
2788
+ const resolvedRoot = path9.resolve(root);
2789
+ const target = path9.resolve(root, relative);
2790
+ const prefix = `${resolvedRoot}${path9.sep}`;
2704
2791
  if (!target.startsWith(prefix)) {
2705
2792
  throw new Error(`canonical recovery path escaped the runner clone: ${relative}`);
2706
2793
  }
@@ -2728,7 +2815,7 @@ async function recoverManagedCanonicalResidue(root, options = {}) {
2728
2815
  if (patchResult.status !== 0) {
2729
2816
  throw new Error(`could not preserve canonical tracked changes: ${summarizeProcessFailure(patchResult)}`);
2730
2817
  }
2731
- await fsp7.writeFile(path8.join(quarantineDir, "tracked.patch"), String(patchResult.stdout || ""), "utf8");
2818
+ await fsp7.writeFile(path9.join(quarantineDir, "tracked.patch"), String(patchResult.stdout || ""), "utf8");
2732
2819
  const symlinks = [];
2733
2820
  for (const relative of paths.untracked) {
2734
2821
  const source = canonicalPath(root, relative);
@@ -2740,13 +2827,13 @@ async function recoverManagedCanonicalResidue(root, options = {}) {
2740
2827
  if (!stat3.isFile()) {
2741
2828
  throw new Error(`canonical recovery refuses unsupported untracked entry: ${relative}`);
2742
2829
  }
2743
- const target = canonicalPath(path8.join(quarantineDir, "untracked"), relative);
2744
- await fsp7.mkdir(path8.dirname(target), { recursive: true });
2830
+ const target = canonicalPath(path9.join(quarantineDir, "untracked"), relative);
2831
+ await fsp7.mkdir(path9.dirname(target), { recursive: true });
2745
2832
  await fsp7.copyFile(source, target);
2746
2833
  }
2747
- await fsp7.writeFile(path8.join(quarantineDir, "manifest.json"), `${JSON.stringify({
2834
+ await fsp7.writeFile(path9.join(quarantineDir, "manifest.json"), `${JSON.stringify({
2748
2835
  recoveredAt: (/* @__PURE__ */ new Date()).toISOString(),
2749
- canonicalRoot: path8.resolve(root),
2836
+ canonicalRoot: path9.resolve(root),
2750
2837
  canonicalHead: headSha,
2751
2838
  tracked: paths.tracked,
2752
2839
  untracked: paths.untracked,
@@ -2809,6 +2896,18 @@ async function alignCanonicalClone(root, options = {}) {
2809
2896
  }
2810
2897
  return { updated: true, headSha, originSha };
2811
2898
  }
2899
+ async function fetchExactTaskBase(root, baseSha, options = {}) {
2900
+ if (!/^[0-9a-f]{40}$/u.test(baseSha)) {
2901
+ throw new Error("comparative base SHA must be exact lowercase hex");
2902
+ }
2903
+ const fetched = await git(root, ["fetch", "origin", baseSha], options);
2904
+ if (fetched.status !== 0) {
2905
+ throw new Error(`exact comparative source is unavailable: ${summarizeProcessFailure(fetched)}`);
2906
+ }
2907
+ const resolved = await gitText(root, ["rev-parse", `${baseSha}^{commit}`], options);
2908
+ if (resolved !== baseSha) throw new Error("exact comparative source did not resolve to the requested commit");
2909
+ return resolved;
2910
+ }
2812
2911
  function shouldIgnoreManagedEntry(entryName) {
2813
2912
  return IGNORED_MANAGED_ENTRIES.has(entryName) || entryName.endsWith(".lock") || entryName.startsWith("runner-");
2814
2913
  }
@@ -2818,11 +2917,11 @@ async function registeredWorktreeDirs(root, options = {}) {
2818
2917
  throw new Error(`git worktree list --porcelain failed while checking managed residue: ${summarizeProcessFailure(listed)}`);
2819
2918
  }
2820
2919
  return new Set(
2821
- String(listed.stdout || "").split(/\r?\n/u).filter((line) => line.startsWith("worktree ")).map((line) => path8.resolve(line.slice("worktree ".length).trim()))
2920
+ String(listed.stdout || "").split(/\r?\n/u).filter((line) => line.startsWith("worktree ")).map((line) => path9.resolve(line.slice("worktree ".length).trim()))
2822
2921
  );
2823
2922
  }
2824
2923
  async function reportLegacyResiduals(root, options = {}) {
2825
- const managedRoot = path8.join(root, ".agent-worktrees");
2924
+ const managedRoot = path9.join(root, ".agent-worktrees");
2826
2925
  if (!fs5.existsSync(managedRoot)) return [];
2827
2926
  const registered = await registeredWorktreeDirs(root, options);
2828
2927
  const pending = pendingCleanupDirs();
@@ -2830,7 +2929,7 @@ async function reportLegacyResiduals(root, options = {}) {
2830
2929
  for (const entry of fs5.readdirSync(managedRoot, { withFileTypes: true })) {
2831
2930
  if (!entry.isDirectory()) continue;
2832
2931
  if (shouldIgnoreManagedEntry(entry.name)) continue;
2833
- const absolute = path8.resolve(path8.join(managedRoot, entry.name));
2932
+ const absolute = path9.resolve(path9.join(managedRoot, entry.name));
2834
2933
  if (registered.has(absolute)) continue;
2835
2934
  if (pending.has(absolute)) continue;
2836
2935
  found.push(absolute);
@@ -2849,6 +2948,9 @@ async function prepareTaskRoot(root, options = {}) {
2849
2948
  try {
2850
2949
  await reportLegacyResiduals(root, options);
2851
2950
  const alignment = await alignCanonicalClone(root, options);
2951
+ if (options.baseSha) {
2952
+ await fetchExactTaskBase(root, options.baseSha, options);
2953
+ }
2852
2954
  const hydration = await ensurePnpmHydration(root, {
2853
2955
  logger: options.logger,
2854
2956
  runner: options.runner || runProcess
@@ -2899,7 +3001,7 @@ var init_worktree_github_auth = __esm({
2899
3001
 
2900
3002
  // src/runner/worktree-recovery-start.mjs
2901
3003
  import fsp8 from "node:fs/promises";
2902
- import path9 from "node:path";
3004
+ import path10 from "node:path";
2903
3005
  async function recordWorktreeStarted(worktreeTarget, meta = {}) {
2904
3006
  if (!worktreeTarget?.worktreeName || !meta.taskId) return null;
2905
3007
  const entry = {
@@ -2914,7 +3016,7 @@ async function recordWorktreeStarted(worktreeTarget, meta = {}) {
2914
3016
  reason: "worktree allocated before execution"
2915
3017
  };
2916
3018
  const ledger = recoveryLedgerPathForRoot(worktreeTarget.root || process.cwd());
2917
- await fsp8.mkdir(path9.dirname(ledger), { recursive: true });
3019
+ await fsp8.mkdir(path10.dirname(ledger), { recursive: true });
2918
3020
  await fsp8.appendFile(ledger, `${JSON.stringify(entry)}
2919
3021
  `, "utf8");
2920
3022
  return { entry, ledger };
@@ -2929,7 +3031,7 @@ var init_worktree_recovery_start = __esm({
2929
3031
  // src/runner/worktree-helper.mjs
2930
3032
  import fs6 from "node:fs";
2931
3033
  import fsp9 from "node:fs/promises";
2932
- import path10 from "node:path";
3034
+ import path11 from "node:path";
2933
3035
  function repoRoot() {
2934
3036
  return process.env.VO_CODE_RUNNER_REPO || process.cwd();
2935
3037
  }
@@ -2945,7 +3047,7 @@ function cloneDirForSlug(repoSlug, clonesRootDir) {
2945
3047
  const [owner, name] = String(repoSlug).split("/");
2946
3048
  if (owner === "." || owner === ".." || name === "." || name === "..") return null;
2947
3049
  if (owner.startsWith("-") || name.startsWith("-")) return null;
2948
- return path10.join(clonesRootDir, `${sanitize(owner, "owner")}__${sanitize(name, "repo")}`);
3050
+ return path11.join(clonesRootDir, `${sanitize(owner, "owner")}__${sanitize(name, "repo")}`);
2949
3051
  }
2950
3052
  function cloneLockDir(dir) {
2951
3053
  return `${dir}.clone-lock`;
@@ -2960,7 +3062,7 @@ async function pathExists5(target) {
2960
3062
  }
2961
3063
  async function readLockMeta2(lockDir) {
2962
3064
  try {
2963
- return JSON.parse(await fsp9.readFile(path10.join(lockDir, "owner.json"), "utf8"));
3065
+ return JSON.parse(await fsp9.readFile(path11.join(lockDir, "owner.json"), "utf8"));
2964
3066
  } catch {
2965
3067
  return null;
2966
3068
  }
@@ -2981,11 +3083,11 @@ async function acquireCloneLock(dir, options = {}) {
2981
3083
  const sleep3 = options.sleep || sleepMs;
2982
3084
  const lockDir = cloneLockDir(dir);
2983
3085
  const deadline = nowMs() + waitMs;
2984
- await fsp9.mkdir(path10.dirname(lockDir), { recursive: true });
3086
+ await fsp9.mkdir(path11.dirname(lockDir), { recursive: true });
2985
3087
  for (; ; ) {
2986
3088
  try {
2987
3089
  await fsp9.mkdir(lockDir);
2988
- await fsp9.writeFile(path10.join(lockDir, "owner.json"), `${JSON.stringify({
3090
+ await fsp9.writeFile(path11.join(lockDir, "owner.json"), `${JSON.stringify({
2989
3091
  pid: process.pid,
2990
3092
  createdAt: new Date(nowMs()).toISOString(),
2991
3093
  dir
@@ -3014,7 +3116,7 @@ async function acquireCloneLock(dir, options = {}) {
3014
3116
  }
3015
3117
  }
3016
3118
  async function isUsableGitClone(dir, runner = runProcess) {
3017
- if (!await pathExists5(path10.join(dir, ".git"))) return false;
3119
+ if (!await pathExists5(path11.join(dir, ".git"))) return false;
3018
3120
  const result = await runner("git", ["-C", dir, "rev-parse", "HEAD"], { timeoutMs: 1e4 });
3019
3121
  return result.status === 0 && Boolean(String(result.stdout || "").trim());
3020
3122
  }
@@ -3035,7 +3137,7 @@ async function ensureUsableClone(repoSlug, dir, options = {}) {
3035
3137
  const maxAttempts = options.maxAttempts || 5;
3036
3138
  const raceWaitMs = options.raceWaitMs ?? 1e4;
3037
3139
  let lastError = null;
3038
- await fsp9.mkdir(path10.dirname(dir), { recursive: true });
3140
+ await fsp9.mkdir(path11.dirname(dir), { recursive: true });
3039
3141
  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
3040
3142
  if (await pathExists5(dir)) {
3041
3143
  if (await waitForUsableClone(dir, runner, sleep3, raceWaitMs)) return dir;
@@ -3047,7 +3149,7 @@ async function ensureUsableClone(repoSlug, dir, options = {}) {
3047
3149
  ["clone", "--no-tags", `https://github.com/${owner}/${name}.git`, tmpDir],
3048
3150
  { timeoutMs: 6e5, env: githubGitAuthEnv(options.githubToken) }
3049
3151
  );
3050
- if (clone.status !== 0 || !await pathExists5(path10.join(tmpDir, ".git"))) {
3152
+ if (clone.status !== 0 || !await pathExists5(path11.join(tmpDir, ".git"))) {
3051
3153
  await fsp9.rm(tmpDir, { recursive: true, force: true });
3052
3154
  lastError = new Error(`[vo-mcp runner] clone failed for ${repoSlug}: ${describeGitFailure(clone)}`);
3053
3155
  continue;
@@ -3074,7 +3176,7 @@ async function ensureUsableClone(repoSlug, dir, options = {}) {
3074
3176
  }
3075
3177
  async function resolveTaskRoot(repoSlug, options = {}) {
3076
3178
  const root = clonesRoot();
3077
- if (root && !path10.isAbsolute(root)) {
3179
+ if (root && !path11.isAbsolute(root)) {
3078
3180
  throw new Error(`[vo-mcp runner] VO_CODE_RUNNER_CLONES_ROOT must be an absolute path (got '${root}')`);
3079
3181
  }
3080
3182
  const dir = cloneDirForSlug(repoSlug, root);
@@ -3123,10 +3225,16 @@ async function createFixWorktree(kind, error = {}, options = {}) {
3123
3225
  const dependencyOwnership = createDependencyOwnershipTracker(worktreeDir);
3124
3226
  const prep = await prepare(root, {
3125
3227
  recoverDirtyCanonical: multiRepo,
3126
- managedPool: path10.dirname(worktreeDir)
3228
+ managedPool: path11.dirname(worktreeDir),
3229
+ baseSha: options.baseSha || null
3127
3230
  });
3128
3231
  await processRunner("git", ["config", "core.longpaths", "true"], { cwd: root, timeoutMs: 3e4 });
3129
- const add = await addWorktree({ root, branchName, worktreeDir });
3232
+ const add = await addWorktree({
3233
+ root,
3234
+ branchName,
3235
+ worktreeDir,
3236
+ baseRef: options.baseSha || "origin/main"
3237
+ });
3130
3238
  if (!add.ok) {
3131
3239
  const detail = describeGitFailure(add.result);
3132
3240
  if (multiRepo) {
@@ -3189,7 +3297,7 @@ function preserveFailedWorktree(worktreeName, meta = {}) {
3189
3297
  };
3190
3298
  try {
3191
3299
  const ledger = recoveryLedgerPathForRoot(root);
3192
- fs6.mkdirSync(path10.dirname(ledger), { recursive: true });
3300
+ fs6.mkdirSync(path11.dirname(ledger), { recursive: true });
3193
3301
  fs6.appendFileSync(ledger, `${JSON.stringify(entry)}
3194
3302
  `, "utf8");
3195
3303
  console.error(`[vo-mcp runner] Preserved worktree ${worktreeName} (reason: ${entry.reason})`);
@@ -3235,9 +3343,9 @@ function partitionRecoveryLedger(lines, { nowMs, ttlMs = PRESERVED_WORKTREE_TTL_
3235
3343
  return decisions;
3236
3344
  }
3237
3345
  function isManagedPreservedDir(dir, root = repoRoot()) {
3238
- const normalized = path10.resolve(String(dir || ""));
3239
- const pool = path10.resolve(worktreePoolForRoot(root));
3240
- return normalized.startsWith(pool + path10.sep);
3346
+ const normalized = path11.resolve(String(dir || ""));
3347
+ const pool = path11.resolve(worktreePoolForRoot(root));
3348
+ return normalized.startsWith(pool + path11.sep);
3241
3349
  }
3242
3350
  function mergeAppendedSinceRead(originalRaw, currentRaw, keptLines) {
3243
3351
  const originalSet = new Set(String(originalRaw || "").split(/\r?\n/u).map((l) => l.trim()).filter(Boolean));
@@ -3762,13 +3870,13 @@ async function getTaskKnowledgeContextRequest(req, taskId, { query } = {}, {
3762
3870
  } = {}) {
3763
3871
  const body = {};
3764
3872
  if (typeof query === "string" && query.trim()) body.query = query;
3765
- const path22 = `/api/v1/code-task/${encodeURIComponent(taskId)}/knowledge-context`;
3873
+ const path23 = `/api/v1/code-task/${encodeURIComponent(taskId)}/knowledge-context`;
3766
3874
  const timeoutMs = Math.max(Number(taskRequestTimeoutMs) || 0, MIN_KNOWLEDGE_CONTEXT_TIMEOUT_MS);
3767
3875
  for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt += 1) {
3768
3876
  let res;
3769
3877
  let cause;
3770
3878
  try {
3771
- res = await req("POST", path22, body, { timeoutMs });
3879
+ res = await req("POST", path23, body, { timeoutMs });
3772
3880
  } catch (err) {
3773
3881
  cause = err;
3774
3882
  }
@@ -3835,10 +3943,10 @@ async function getPreparedJobRequest(req, taskId, options = {}, invalidateToken
3835
3943
  if (typeof taskId !== "string" || taskId.length === 0) {
3836
3944
  return { ok: false, reason: "missing_task_id", status: 0, ...envMeta };
3837
3945
  }
3838
- const path22 = `/api/v1/code-task/${encodeURIComponent(taskId)}/prepared-job?${query}`;
3946
+ const path23 = `/api/v1/code-task/${encodeURIComponent(taskId)}/prepared-job?${query}`;
3839
3947
  let res;
3840
3948
  try {
3841
- res = await req("GET", path22, void 0, { timeoutMs });
3949
+ res = await req("GET", path23, void 0, { timeoutMs });
3842
3950
  } catch (err) {
3843
3951
  return { ok: false, reason: `transport: ${err?.message || String(err)}`, status: 0, ...envMeta };
3844
3952
  }
@@ -3937,11 +4045,11 @@ function createControlPlaneClient({
3937
4045
  const resolvedBaseUrl = baseUrl ?? env2.VO_CONTROL_PLANE_URL ?? "";
3938
4046
  if (!resolvedBaseUrl) throw new Error("VO_CONTROL_PLANE_URL is required for the code-runner daemon");
3939
4047
  const root = resolvedBaseUrl.replace(/\/+$/, "");
3940
- async function req(method, path22, body, { timeoutMs } = {}) {
4048
+ async function req(method, path23, body, { timeoutMs } = {}) {
3941
4049
  const bearer = await resolveBearer(env2);
3942
4050
  const controller = timeoutMs ? new AbortController() : null;
3943
4051
  let timeoutId;
3944
- const request = Promise.resolve(fetchImpl(`${root}${path22}`, {
4052
+ const request = Promise.resolve(fetchImpl(`${root}${path23}`, {
3945
4053
  method,
3946
4054
  headers: {
3947
4055
  "content-type": "application/json",
@@ -3954,7 +4062,7 @@ function createControlPlaneClient({
3954
4062
  const timeout = new Promise((_, reject) => {
3955
4063
  timeoutId = setTimeout(() => {
3956
4064
  controller.abort();
3957
- reject(new Error(`control-plane ${path22} timed out after ${timeoutMs}ms`));
4065
+ reject(new Error(`control-plane ${path23} timed out after ${timeoutMs}ms`));
3958
4066
  }, timeoutMs);
3959
4067
  });
3960
4068
  try {
@@ -3963,7 +4071,7 @@ function createControlPlaneClient({
3963
4071
  clearTimeout(timeoutId);
3964
4072
  }
3965
4073
  }
3966
- const taskReq = (method, path22, body, options = {}) => req(method, path22, body, { timeoutMs: taskRequestTimeoutMs, ...options });
4074
+ const taskReq = (method, path23, body, options = {}) => req(method, path23, body, { timeoutMs: taskRequestTimeoutMs, ...options });
3967
4075
  const claimGate = makeClaimGateNotice({ log: (m) => console.warn(`[code-runner ${(/* @__PURE__ */ new Date()).toISOString()}] ${m}`) });
3968
4076
  return {
3969
4077
  getClaimGate: () => claimGate.current(),
@@ -4107,8 +4215,8 @@ function createControlPlaneClient({
4107
4215
  return listAllPrOpenedTasks(taskReq);
4108
4216
  },
4109
4217
  async downloadTaskAttachment(taskId, attachmentId) {
4110
- const path22 = `/api/v1/code-task/${encodeURIComponent(taskId)}/attachment/${encodeURIComponent(attachmentId)}`;
4111
- const res = await taskReq("GET", path22);
4218
+ const path23 = `/api/v1/code-task/${encodeURIComponent(taskId)}/attachment/${encodeURIComponent(attachmentId)}`;
4219
+ const res = await taskReq("GET", path23);
4112
4220
  if (res.status === 401) cachedFirebaseToken = null;
4113
4221
  if (!res.ok) throw new Error(`attachment download failed: HTTP ${res.status}`);
4114
4222
  return Buffer.from(await res.arrayBuffer());
@@ -4266,7 +4374,7 @@ var init_control_plane_client = __esm({
4266
4374
 
4267
4375
  // ../../scripts/virtual-office/code-runner/windows-claude-launch.mjs
4268
4376
  import { existsSync as existsSync8, realpathSync } from "node:fs";
4269
- import { win32 as path11 } from "node:path";
4377
+ import { win32 as path12 } from "node:path";
4270
4378
  import { spawnSync } from "node:child_process";
4271
4379
  function pathValue(env2) {
4272
4380
  for (const key of ["Path", "PATH", "path"]) {
@@ -4287,38 +4395,38 @@ function envValue(env2, name) {
4287
4395
  function userClaudeCandidates(bin, env2) {
4288
4396
  if (!/^claude(?:\.(?:exe|cmd|ps1))?$/iu.test(bin)) return [];
4289
4397
  const userProfile = envValue(env2, "USERPROFILE");
4290
- const appData = envValue(env2, "APPDATA") || (userProfile ? path11.join(userProfile, "AppData", "Roaming") : "");
4291
- const localAppData = envValue(env2, "LOCALAPPDATA") || (userProfile ? path11.join(userProfile, "AppData", "Local") : "");
4398
+ const appData = envValue(env2, "APPDATA") || (userProfile ? path12.join(userProfile, "AppData", "Roaming") : "");
4399
+ const localAppData = envValue(env2, "LOCALAPPDATA") || (userProfile ? path12.join(userProfile, "AppData", "Local") : "");
4292
4400
  const candidates = [];
4293
4401
  if (appData) {
4294
- const npmBin = path11.join(appData, "npm");
4402
+ const npmBin = path12.join(appData, "npm");
4295
4403
  candidates.push(
4296
- path11.join(npmBin, "claude.exe"),
4297
- path11.join(npmBin, "claude.cmd"),
4298
- path11.join(npmBin, "claude.ps1"),
4299
- path11.join(npmBin, "claude"),
4300
- path11.join(npmBin, ...NATIVE_CLAUDE_PARTS)
4404
+ path12.join(npmBin, "claude.exe"),
4405
+ path12.join(npmBin, "claude.cmd"),
4406
+ path12.join(npmBin, "claude.ps1"),
4407
+ path12.join(npmBin, "claude"),
4408
+ path12.join(npmBin, ...NATIVE_CLAUDE_PARTS)
4301
4409
  );
4302
4410
  }
4303
- if (userProfile) candidates.push(path11.join(userProfile, ".local", "bin", "claude.exe"));
4411
+ if (userProfile) candidates.push(path12.join(userProfile, ".local", "bin", "claude.exe"));
4304
4412
  if (localAppData) {
4305
4413
  candidates.push(
4306
- path11.join(localAppData, "Microsoft", "WinGet", "Links", "claude.exe"),
4307
- path11.join(localAppData, "Microsoft", "WindowsApps", "claude.exe")
4414
+ path12.join(localAppData, "Microsoft", "WinGet", "Links", "claude.exe"),
4415
+ path12.join(localAppData, "Microsoft", "WindowsApps", "claude.exe")
4308
4416
  );
4309
4417
  }
4310
4418
  return candidates;
4311
4419
  }
4312
4420
  function pathCandidates(bin, env2) {
4313
- if (path11.isAbsolute(bin) || /[\\/]/u.test(bin)) {
4314
- return [path11.resolve(bin)];
4315
- }
4316
- const extension = path11.extname(bin);
4317
- const fromPath = pathValue(env2).split(";").map(cleanPathSegment).filter(Boolean).flatMap((directory) => extension ? [path11.join(directory, bin)] : [
4318
- path11.join(directory, `${bin}.exe`),
4319
- path11.join(directory, `${bin}.cmd`),
4320
- path11.join(directory, `${bin}.ps1`),
4321
- path11.join(directory, bin)
4421
+ if (path12.isAbsolute(bin) || /[\\/]/u.test(bin)) {
4422
+ return [path12.resolve(bin)];
4423
+ }
4424
+ const extension = path12.extname(bin);
4425
+ const fromPath = pathValue(env2).split(";").map(cleanPathSegment).filter(Boolean).flatMap((directory) => extension ? [path12.join(directory, bin)] : [
4426
+ path12.join(directory, `${bin}.exe`),
4427
+ path12.join(directory, `${bin}.cmd`),
4428
+ path12.join(directory, `${bin}.ps1`),
4429
+ path12.join(directory, bin)
4322
4430
  ]);
4323
4431
  const seen = /* @__PURE__ */ new Set();
4324
4432
  return [...fromPath, ...userClaudeCandidates(bin, env2)].filter((candidate) => {
@@ -4349,8 +4457,8 @@ function resolveWindowsClaudeExecutable({
4349
4457
  for (const candidate of pathCandidates(requested, env2)) {
4350
4458
  const found = canonicalExistingPath(candidate, exists, canonicalize);
4351
4459
  if (!found) continue;
4352
- if (path11.extname(found).toLowerCase() === ".exe") return found;
4353
- const native = path11.join(path11.dirname(found), ...NATIVE_CLAUDE_PARTS);
4460
+ if (path12.extname(found).toLowerCase() === ".exe") return found;
4461
+ const native = path12.join(path12.dirname(found), ...NATIVE_CLAUDE_PARTS);
4354
4462
  const resolvedNative = canonicalExistingPath(native, exists, canonicalize);
4355
4463
  if (resolvedNative) return resolvedNative;
4356
4464
  }
@@ -4611,6 +4719,7 @@ function finalizeAgentTaskResult({
4611
4719
  result,
4612
4720
  bin,
4613
4721
  stderrTail,
4722
+ classifyStderr = null,
4614
4723
  timedOut,
4615
4724
  killed,
4616
4725
  cancelReason,
@@ -4641,8 +4750,16 @@ function finalizeAgentTaskResult({
4641
4750
  if (code !== 0 || signal) summary = fallback || `${bin} exited ${signal || code}`;
4642
4751
  else summary = fallback || `${bin} exited without terminal result`;
4643
4752
  }
4753
+ let failure = result.failure ?? null;
4754
+ if (!failure && !sawTerminalResult && (code !== 0 || signal) && typeof classifyStderr === "function") {
4755
+ try {
4756
+ failure = classifyStderr(stderrTail);
4757
+ } catch {
4758
+ }
4759
+ }
4644
4760
  return {
4645
4761
  ...result,
4762
+ ...failure ? { failure } : {},
4646
4763
  ok: sawTerminalResult && result.ok && (code === 0 || forcedAfterResult),
4647
4764
  summary: augmentSummary(summary)
4648
4765
  };
@@ -4847,12 +4964,12 @@ var init_terminal_process_cleanup = __esm({
4847
4964
  import { spawnSync as spawnSync5 } from "node:child_process";
4848
4965
  import { existsSync as existsSync9, mkdirSync as mkdirSync7, readdirSync as readdirSync2, readFileSync as readFileSync7, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "node:fs";
4849
4966
  import os from "node:os";
4850
- import path12 from "node:path";
4967
+ import path13 from "node:path";
4851
4968
  function registryRoot(tmp = os.tmpdir()) {
4852
- return path12.join(tmp, REGISTRY_ROOT_NAME);
4969
+ return path13.join(tmp, REGISTRY_ROOT_NAME);
4853
4970
  }
4854
4971
  function instanceDir(root, instanceId) {
4855
- return path12.join(root, String(instanceId).replace(/[^A-Za-z0-9_-]/g, ""));
4972
+ return path13.join(root, String(instanceId).replace(/[^A-Za-z0-9_-]/g, ""));
4856
4973
  }
4857
4974
  function registerDaemonInstance({
4858
4975
  root = registryRoot(),
@@ -4863,7 +4980,7 @@ function registerDaemonInstance({
4863
4980
  if (!instanceId) return null;
4864
4981
  const dir = instanceDir(root, instanceId);
4865
4982
  mkdirSync7(dir, { recursive: true });
4866
- const file = path12.join(dir, DAEMON_RECORD);
4983
+ const file = path13.join(dir, DAEMON_RECORD);
4867
4984
  writeFileSync5(file, JSON.stringify({ daemonPid, daemonStartedAtMs, instanceId }), {
4868
4985
  encoding: "utf8",
4869
4986
  mode: 384
@@ -4882,7 +4999,7 @@ function recordAgentPid({
4882
4999
  const dir = instanceDir(root, instanceId);
4883
5000
  mkdirSync7(dir, { recursive: true });
4884
5001
  writeFileSync5(
4885
- path12.join(dir, `${pid}.json`),
5002
+ path13.join(dir, `${pid}.json`),
4886
5003
  JSON.stringify({ pid, agentId, startedAtMs, instanceId }),
4887
5004
  { encoding: "utf8", mode: 384 }
4888
5005
  );
@@ -4898,7 +5015,7 @@ function unrecordAgentPid({
4898
5015
  } = {}) {
4899
5016
  if (!instanceId || !Number.isInteger(pid)) return false;
4900
5017
  try {
4901
- rmSync2(path12.join(instanceDir(root, instanceId), `${pid}.json`), { force: true });
5018
+ rmSync2(path13.join(instanceDir(root, instanceId), `${pid}.json`), { force: true });
4902
5019
  return true;
4903
5020
  } catch {
4904
5021
  return false;
@@ -4926,7 +5043,7 @@ function readRegistry({ root = registryRoot(), currentInstanceId } = {}) {
4926
5043
  for (const dirent of dirents) {
4927
5044
  if (!dirent.isDirectory()) continue;
4928
5045
  if (currentInstanceId && dirent.name === instanceDirName(currentInstanceId)) continue;
4929
- const dir = path12.join(root, dirent.name);
5046
+ const dir = path13.join(root, dirent.name);
4930
5047
  let daemon = null;
4931
5048
  const agents = [];
4932
5049
  let files;
@@ -4938,7 +5055,7 @@ function readRegistry({ root = registryRoot(), currentInstanceId } = {}) {
4938
5055
  for (const name of files) {
4939
5056
  let parsed;
4940
5057
  try {
4941
- parsed = JSON.parse(readFileSync7(path12.join(dir, name), "utf8"));
5058
+ parsed = JSON.parse(readFileSync7(path13.join(dir, name), "utf8"));
4942
5059
  } catch {
4943
5060
  continue;
4944
5061
  }
@@ -4986,7 +5103,7 @@ function windowsSystemRoot(env2 = process.env) {
4986
5103
  return env2.SystemRoot || env2.WINDIR || "C:\\Windows";
4987
5104
  }
4988
5105
  function windowsPowershellExe(env2 = process.env) {
4989
- return path12.join(windowsSystemRoot(env2), "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
5106
+ return path13.join(windowsSystemRoot(env2), "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
4990
5107
  }
4991
5108
  function listProcessCreationTimes({ platform: platform4 = process.platform, spawn: spawn5 = spawnSync5, env: env2 = process.env, warn = console.warn } = {}) {
4992
5109
  const map = /* @__PURE__ */ new Map();
@@ -5031,7 +5148,7 @@ function parsePosixPsLine(line) {
5031
5148
  function killProcessTree(pid, { platform: platform4 = process.platform, spawn: spawn5 = spawnSync5, env: env2 = process.env } = {}) {
5032
5149
  if (!Number.isInteger(pid) || pid <= 0) return false;
5033
5150
  if (platform4 === "win32") {
5034
- const taskkill = path12.join(windowsSystemRoot(env2), "System32", "taskkill.exe");
5151
+ const taskkill = path13.join(windowsSystemRoot(env2), "System32", "taskkill.exe");
5035
5152
  const r = spawn5(taskkill, ["/PID", String(pid), "/T", "/F"], { windowsHide: true, stdio: "ignore", timeout: 15e3 });
5036
5153
  return !r.error && r.status === 0;
5037
5154
  }
@@ -5567,7 +5684,8 @@ function runAgentTask({
5567
5684
  // parser would work, the runner would report null, and the feature
5568
5685
  // would measure nothing while every test passed.
5569
5686
  tokenUsage: evt.tokenUsage ?? result.tokenUsage ?? null,
5570
- modelUsage: evt.modelUsage ?? result.modelUsage ?? null
5687
+ modelUsage: evt.modelUsage ?? result.modelUsage ?? null,
5688
+ failure: evt.failure ?? result.failure ?? null
5571
5689
  };
5572
5690
  terminalCleanupTimer ??= armTerminalCleanup({
5573
5691
  child,
@@ -5596,6 +5714,7 @@ function runAgentTask({
5596
5714
  result,
5597
5715
  bin,
5598
5716
  stderrTail,
5717
+ classifyStderr: runner.classifyStderr,
5599
5718
  timedOut,
5600
5719
  killed,
5601
5720
  cancelReason,
@@ -5720,26 +5839,15 @@ var init_claude_runner = __esm({
5720
5839
  if (process.platform !== "win32") return { bin, args, spawnOptions: spawnOptions ?? this.getSpawnOptions() };
5721
5840
  return buildWindowsClaudeLaunch({ bin, args, env: env2 });
5722
5841
  }
5723
- /**
5724
- * Fill ANTHROPIC_API_KEY from the OS keychain when not already set (M4 BYO),
5725
- * so a friend who ran `vo-mcp set-key` authenticates without an env var.
5726
- * Explicit env wins; no key stored → unchanged (Claude Code login as before).
5727
- */
5728
5842
  applyAuthEnv(env2 = process.env) {
5729
5843
  return withAnthropicKey(env2);
5730
5844
  }
5731
5845
  costBasis(env2 = process.env) {
5732
5846
  return claudeCostBasis(env2);
5733
5847
  }
5734
- /** Describe which Anthropic auth source the spawn will use (for runner logs). */
5735
5848
  describeAuth(env2 = process.env) {
5736
5849
  return describeAnthropicAuthSource(env2);
5737
5850
  }
5738
- /**
5739
- * Best-effort auth check: is `claude` on PATH and can we verify login?
5740
- * Never throws. If we can't cheaply detect auth, we return installed:true
5741
- * and let the real spawn fail with a clearer error from the CLI itself.
5742
- */
5743
5851
  async checkAuth() {
5744
5852
  return checkClaudeAuth();
5745
5853
  }
@@ -5865,6 +5973,66 @@ var init_flat_token_usage = __esm({
5865
5973
  }
5866
5974
  });
5867
5975
 
5976
+ // ../../scripts/virtual-office/code-runner/error-message.mjs
5977
+ function boundedErrorMessage(error, maxLength = 240) {
5978
+ try {
5979
+ return String(error?.message ?? error ?? "unknown error").slice(0, maxLength);
5980
+ } catch {
5981
+ return "unknown error";
5982
+ }
5983
+ }
5984
+ function codexFailure(code, fields = {}) {
5985
+ const definition = FAILURE_DEFINITIONS[code];
5986
+ return {
5987
+ schema: FAILURE_SCHEMA,
5988
+ agent: "codex",
5989
+ source: definition.source,
5990
+ code,
5991
+ ...fields,
5992
+ operator_next_action: definition.operator_next_action
5993
+ };
5994
+ }
5995
+ function classifyCodexTerminalFailure(evt = {}) {
5996
+ const error = evt && typeof evt.error === "object" && evt.error !== null ? evt.error : null;
5997
+ if (!/^The ['"][^'"]+['"] model is not supported when using Codex with a ChatGPT account\.$/u.test(
5998
+ typeof error?.message === "string" ? error.message : ""
5999
+ )) return null;
6000
+ const status = Number(evt.status ?? error.status);
6001
+ return codexFailure("model_unsupported_for_account", Number.isInteger(status) ? { provider_status: status } : {});
6002
+ }
6003
+ function classifyCodexStderr(stderr = "") {
6004
+ if (!/(?:^|\n)\s*windows sandbox(?: failed)?: runner error:.*\bCreateProcessAsUserW failed:\s*1312\b/imu.test(String(stderr))) return null;
6005
+ return codexFailure("codex_runtime_launch_logon_session", { win32_error: 1312 });
6006
+ }
6007
+ function serializeRunnerFailure(record, maxLength = 2e3) {
6008
+ const definition = record && FAILURE_DEFINITIONS[record.code];
6009
+ if (!definition || record.schema !== FAILURE_SCHEMA || record.source !== definition.source) return null;
6010
+ const value = codexFailure(record.code, {
6011
+ ...record.code === "model_unsupported_for_account" && Number.isInteger(record.provider_status) ? { provider_status: record.provider_status } : {},
6012
+ ...definition.win32_error ? { win32_error: definition.win32_error } : {}
6013
+ });
6014
+ const serialized2 = JSON.stringify(value);
6015
+ return serialized2.length <= Math.max(0, maxLength) ? serialized2 : null;
6016
+ }
6017
+ var FAILURE_SCHEMA, FAILURE_DEFINITIONS;
6018
+ var init_error_message = __esm({
6019
+ "../../scripts/virtual-office/code-runner/error-message.mjs"() {
6020
+ "use strict";
6021
+ FAILURE_SCHEMA = "vo.runner_failure.v1";
6022
+ FAILURE_DEFINITIONS = {
6023
+ model_unsupported_for_account: {
6024
+ source: "terminal_event",
6025
+ operator_next_action: "Create a new owner-reviewed task with an account-supported Codex model; keep the preserved draft PR for review rather than resuming this task."
6026
+ },
6027
+ codex_runtime_launch_logon_session: {
6028
+ source: "stderr",
6029
+ win32_error: 1312,
6030
+ operator_next_action: "Repair the Codex Windows logon/session configuration on the serving host, then rerun the runner."
6031
+ }
6032
+ };
6033
+ }
6034
+ });
6035
+
5868
6036
  // ../../scripts/virtual-office/code-runner/codex-runner.mjs
5869
6037
  import { spawnSync as spawnSync6 } from "node:child_process";
5870
6038
  import { existsSync as existsSync10 } from "node:fs";
@@ -5965,7 +6133,8 @@ function parseCodexEvent(line) {
5965
6133
  }
5966
6134
  if (type === "turn.failed" || type === "error") {
5967
6135
  const msg = evt.error && (evt.error.message || evt.error) || evt.message || "codex run failed";
5968
- return { kind: "result", isError: true, costUsd: null, summary: String(msg), numTurns: null };
6136
+ const failure = classifyCodexTerminalFailure(evt);
6137
+ return { kind: "result", isError: true, costUsd: null, summary: String(msg), numTurns: null, ...failure ? { failure } : {} };
5969
6138
  }
5970
6139
  return null;
5971
6140
  }
@@ -5976,6 +6145,7 @@ var init_codex_runner = __esm({
5976
6145
  init_agent_key_store();
5977
6146
  init_agent_auth_tier();
5978
6147
  init_flat_token_usage();
6148
+ init_error_message();
5979
6149
  CODEX_PREFER_LOGIN_ENV = "VO_RUNNER_CODEX_PREFER_LOGIN";
5980
6150
  LEGACY_PREFER_LOGIN_ENV = "VO_RUNNER_PREFER_LOGIN";
5981
6151
  CodexRunner = class {
@@ -5993,6 +6163,9 @@ var init_codex_runner = __esm({
5993
6163
  parseEvent(line) {
5994
6164
  return parseCodexEvent(line);
5995
6165
  }
6166
+ classifyStderr(stderr) {
6167
+ return classifyCodexStderr(stderr);
6168
+ }
5996
6169
  /**
5997
6170
  * SECURITY: never `shell: true` — same RCE class as cursor-runner. The old
5998
6171
  * `shell: win32 && !/\.exe$/` fell back to shell mode whenever
@@ -6014,12 +6187,6 @@ var init_codex_runner = __esm({
6014
6187
  windowsVerbatimArguments: false
6015
6188
  };
6016
6189
  }
6017
- /**
6018
- * Fill the OpenAI credential env var(s) (OPENAI_API_KEY / CODEX_API_KEY) from
6019
- * the OS keychain when not already set, so a BYO friend who ran
6020
- * `vo-mcp set-key --provider codex` authenticates without an env var. Explicit
6021
- * env wins; no key stored → unchanged (a prior `codex login` still works).
6022
- */
6023
6190
  applyAuthEnv(env2 = process.env) {
6024
6191
  if (isTruthyFlag2(env2[CODEX_PREFER_LOGIN_ENV]) || isTruthyFlag2(env2[LEGACY_PREFER_LOGIN_ENV])) {
6025
6192
  const out = { ...env2 };
@@ -6032,20 +6199,9 @@ var init_codex_runner = __esm({
6032
6199
  costBasis(env2 = process.env) {
6033
6200
  return String(env2.OPENAI_API_KEY || env2.CODEX_API_KEY || "").trim() ? "vendor_billed" : "subscription_api_equivalent";
6034
6201
  }
6035
- /**
6036
- * Dispatch-time billing tier for the NEXT codex spawn, from the SAME facts
6037
- * checkAuth() already gathered — no extra subprocess. applyAuthEnv() is a
6038
- * keychain read in this process (@napi-rs/keyring), not a spawn.
6039
- *
6040
- * Codex is the agent where this signal was already computed and then thrown
6041
- * away: checkAuth() distinguished "API key available (no persisted ChatGPT
6042
- * login)" from a real login, but only inside a `message` string that the
6043
- * heartbeat schema strips before storage. This gives that fact a typed home.
6044
- */
6045
6202
  authTier(env2 = this.env) {
6046
6203
  return safeAuthTier(() => authTierFromCostBasis(this.costBasis(this.applyAuthEnv(env2))));
6047
6204
  }
6048
- /** Best-effort binary + persisted-login probe. Never throws or spends tokens. */
6049
6205
  async checkAuth() {
6050
6206
  try {
6051
6207
  const bin = this.binary;
@@ -6937,9 +7093,9 @@ var init_rate_limit_detector_core = __esm({
6937
7093
 
6938
7094
  // ../../scripts/virtual-office/code-runner/rate-limit-resume-state.mjs
6939
7095
  import fsp10 from "node:fs/promises";
6940
- import path13 from "node:path";
7096
+ import path14 from "node:path";
6941
7097
  async function atomicWrite(file, content) {
6942
- await fsp10.mkdir(path13.dirname(file), { recursive: true });
7098
+ await fsp10.mkdir(path14.dirname(file), { recursive: true });
6943
7099
  const temp = `${file}.tmp-${process.pid}-${Date.now()}`;
6944
7100
  const handle = await fsp10.open(temp, "wx");
6945
7101
  try {
@@ -7000,7 +7156,7 @@ function writeResumeAttempts(file, store) {
7000
7156
  }
7001
7157
  async function acquireLock(lockFile, { now = Date.now, sleep: sleep3 = delay } = {}) {
7002
7158
  const deadline = now() + LOCK_WAIT_MS;
7003
- await fsp10.mkdir(path13.dirname(lockFile), { recursive: true });
7159
+ await fsp10.mkdir(path14.dirname(lockFile), { recursive: true });
7004
7160
  for (; ; ) {
7005
7161
  let handle;
7006
7162
  try {
@@ -7145,6 +7301,17 @@ async function classifyFailureForResume({
7145
7301
  record = recordRateLimited,
7146
7302
  deferRecord = false
7147
7303
  } = {}) {
7304
+ const structuredFailure = serializeRunnerFailure(run.failure);
7305
+ if (structuredFailure) {
7306
+ return {
7307
+ rateLimited: false,
7308
+ progress: {
7309
+ status: "failed",
7310
+ message: `runner blocker: ${run.failure.code}; operator review required`,
7311
+ result: structuredFailure
7312
+ }
7313
+ };
7314
+ }
7148
7315
  if (enabled) {
7149
7316
  const rl = detect(run.summary, { now });
7150
7317
  if (rl.rateLimited) {
@@ -7219,6 +7386,7 @@ var init_rate_limit_resume = __esm({
7219
7386
  "use strict";
7220
7387
  init_rate_limit_detector_core();
7221
7388
  init_rate_limit_resume_state();
7389
+ init_error_message();
7222
7390
  }
7223
7391
  });
7224
7392
 
@@ -7458,14 +7626,14 @@ function parsePorcelainZ(out) {
7458
7626
  for (let i = 0; i < tokens.length; i += 1) {
7459
7627
  const token2 = tokens[i];
7460
7628
  if (!token2) continue;
7461
- const path22 = token2.slice(3);
7462
- if (path22) files.push(path22);
7629
+ const path23 = token2.slice(3);
7630
+ if (path23) files.push(path23);
7463
7631
  if (token2[0] === "R" || token2[0] === "C") i += 1;
7464
7632
  }
7465
7633
  return files;
7466
7634
  }
7467
- function isAgentScratch(path22) {
7468
- const normalized = String(path22 || "");
7635
+ function isAgentScratch(path23) {
7636
+ const normalized = String(path23 || "");
7469
7637
  return SCRATCH_PATTERNS.some((pattern) => pattern.test(normalized));
7470
7638
  }
7471
7639
  var SCRATCH_PATTERNS;
@@ -7722,7 +7890,7 @@ var init_executor = __esm({
7722
7890
 
7723
7891
  // ../../scripts/virtual-office/code-runner/test-gen-gate.mjs
7724
7892
  import fs7 from "node:fs";
7725
- import path14 from "node:path";
7893
+ import path15 from "node:path";
7726
7894
  async function postFailed(client, id, message, result) {
7727
7895
  try {
7728
7896
  await client.postProgress(id, {
@@ -7758,7 +7926,7 @@ async function gateTestGenTaskOrFail({ client, id, task, files, worktreeDir, env
7758
7926
  }
7759
7927
  let testSource = "";
7760
7928
  try {
7761
- testSource = fs7.readFileSync(path14.join(worktreeDir, testFile), "utf8");
7929
+ testSource = fs7.readFileSync(path15.join(worktreeDir, testFile), "utf8");
7762
7930
  } catch (err) {
7763
7931
  await postFailed(client, id, `could not read generated test ${testFile}: ${err.message}`, "gate_test_unreadable");
7764
7932
  return true;
@@ -7808,7 +7976,7 @@ var init_test_gen_gate = __esm({
7808
7976
  // ../../scripts/virtual-office/code-runner/completion-gate.mjs
7809
7977
  import { execFile } from "node:child_process";
7810
7978
  import fs8 from "node:fs";
7811
- import path15 from "node:path";
7979
+ import path16 from "node:path";
7812
7980
  function resolveCompletionGate(task) {
7813
7981
  const raw = task?.completion_gate;
7814
7982
  if (raw === void 0 || raw === null) return null;
@@ -7846,14 +8014,14 @@ function workspaceFingerprint(worktreeDir, execFileImpl = execFile) {
7846
8014
  }
7847
8015
  function readState(worktreeDir) {
7848
8016
  try {
7849
- return JSON.parse(fs8.readFileSync(path15.join(worktreeDir, COMPLETION_GATE_STATE_FILE), "utf8"));
8017
+ return JSON.parse(fs8.readFileSync(path16.join(worktreeDir, COMPLETION_GATE_STATE_FILE), "utf8"));
7850
8018
  } catch {
7851
8019
  return null;
7852
8020
  }
7853
8021
  }
7854
8022
  function writeState(worktreeDir, state) {
7855
8023
  try {
7856
- fs8.writeFileSync(path15.join(worktreeDir, COMPLETION_GATE_STATE_FILE), `${JSON.stringify(state)}
8024
+ fs8.writeFileSync(path16.join(worktreeDir, COMPLETION_GATE_STATE_FILE), `${JSON.stringify(state)}
7857
8025
  `, "utf8");
7858
8026
  } catch {
7859
8027
  }
@@ -8565,16 +8733,28 @@ var init_repair_publication_strategy = __esm({
8565
8733
 
8566
8734
  // ../../scripts/virtual-office/code-runner/partial-pr-continuation.mjs
8567
8735
  function partialPrContinuationResult(run = {}, maxLength = 2e3, reason = null) {
8736
+ const limit = Math.max(0, Number(maxLength) || 0);
8568
8737
  const summary = String(run.summary || "agent stopped before completing the task").trim();
8569
8738
  const reasonMarker = reason === "rate_limited" ? `
8570
8739
  ${RATE_LIMITED_CONTINUATION_MARKER}` : "";
8571
- return `${PARTIAL_PR_CONTINUATION_MARKER}${reasonMarker}
8572
- ${summary}`.slice(0, Math.max(0, maxLength));
8740
+ const blockedMarker = "ALGOSUITE_TASK_OUTCOME: BLOCKED";
8741
+ const blockedPrefix = `${blockedMarker}${reasonMarker}
8742
+ `;
8743
+ const serializedFailure = serializeRunnerFailure(run.failure, Math.max(0, limit - blockedPrefix.length - 1));
8744
+ const outcomeMarker = serializedFailure ? blockedMarker : PARTIAL_PR_CONTINUATION_MARKER;
8745
+ const prefix = `${outcomeMarker}${reasonMarker}
8746
+ `;
8747
+ const failure = serializedFailure && outcomeMarker === blockedMarker ? serializedFailure : null;
8748
+ const suffix = failure ? `
8749
+ ${failure}` : "";
8750
+ const summaryRoom = Math.max(0, limit - prefix.length - suffix.length);
8751
+ return `${prefix}${summary.slice(0, summaryRoom)}${suffix}`.slice(0, limit);
8573
8752
  }
8574
8753
  var PARTIAL_PR_CONTINUATION_MARKER, RATE_LIMITED_CONTINUATION_MARKER;
8575
8754
  var init_partial_pr_continuation = __esm({
8576
8755
  "../../scripts/virtual-office/code-runner/partial-pr-continuation.mjs"() {
8577
8756
  "use strict";
8757
+ init_error_message();
8578
8758
  PARTIAL_PR_CONTINUATION_MARKER = "ALGOSUITE_TASK_OUTCOME: NEEDS_CONTINUATION";
8579
8759
  RATE_LIMITED_CONTINUATION_MARKER = "ALGOSUITE_CONTINUATION_REASON: RATE_LIMITED";
8580
8760
  }
@@ -9441,7 +9621,7 @@ var init_task_prompt = __esm({
9441
9621
  import { createHash as createHash4, randomUUID as randomUUID3 } from "node:crypto";
9442
9622
  import { chmod, mkdir, mkdtemp, readFile, readdir, realpath, rm, stat, writeFile } from "node:fs/promises";
9443
9623
  import os2 from "node:os";
9444
- import path16 from "node:path";
9624
+ import path17 from "node:path";
9445
9625
  function safeTaskToken(taskId) {
9446
9626
  return String(taskId || "task").replace(/[^0-9A-Za-z_-]/gu, "_").slice(0, 48) || "task";
9447
9627
  }
@@ -9454,9 +9634,9 @@ function hasGeneratedPrefix(name) {
9454
9634
  return name.startsWith(DIRECTORY_PREFIX) || name.startsWith(LEGACY_DIRECTORY_PREFIX);
9455
9635
  }
9456
9636
  function assertGeneratedDirectory(directory, containmentRoot) {
9457
- const resolvedDirectory = path16.resolve(directory);
9458
- const resolvedRoot = path16.resolve(containmentRoot);
9459
- if (path16.dirname(resolvedDirectory) !== resolvedRoot || !hasGeneratedPrefix(path16.basename(resolvedDirectory))) {
9637
+ const resolvedDirectory = path17.resolve(directory);
9638
+ const resolvedRoot = path17.resolve(containmentRoot);
9639
+ if (path17.dirname(resolvedDirectory) !== resolvedRoot || !hasGeneratedPrefix(path17.basename(resolvedDirectory))) {
9460
9640
  throw new Error("refusing to clean an unverified task-attachment directory");
9461
9641
  }
9462
9642
  return resolvedDirectory;
@@ -9465,7 +9645,7 @@ async function resolveContainmentRoot(worktreeDir) {
9465
9645
  if (typeof worktreeDir !== "string" || !worktreeDir.trim()) {
9466
9646
  throw new Error("refusing to materialize task attachments outside an agent-readable worktree: no worktreeDir given");
9467
9647
  }
9468
- const root = path16.resolve(worktreeDir);
9648
+ const root = path17.resolve(worktreeDir);
9469
9649
  const stats = await stat(root).catch(() => null);
9470
9650
  if (!stats?.isDirectory()) {
9471
9651
  throw new Error(`refusing to materialize task attachments: agent worktree root is not a directory (${root})`);
@@ -9474,15 +9654,15 @@ async function resolveContainmentRoot(worktreeDir) {
9474
9654
  }
9475
9655
  async function createAttachmentDirectory(taskId, containmentRoot) {
9476
9656
  const root = await resolveContainmentRoot(containmentRoot);
9477
- const directory = await mkdtemp(path16.join(root, `${DIRECTORY_PREFIX}${safeTaskToken(taskId)}-`));
9657
+ const directory = await mkdtemp(path17.join(root, `${DIRECTORY_PREFIX}${safeTaskToken(taskId)}-`));
9478
9658
  const [realRoot, realDirectory] = await Promise.all([realpath(root), realpath(directory)]);
9479
- if (path16.dirname(realDirectory) !== realRoot) {
9659
+ if (path17.dirname(realDirectory) !== realRoot) {
9480
9660
  await rm(directory, { recursive: true, force: true }).catch(() => void 0);
9481
9661
  throw new Error("task-attachment directory escaped the agent worktree root");
9482
9662
  }
9483
- await writeFile(path16.join(directory, GITIGNORE_FILE), GITIGNORE_BODY, { encoding: "utf8", mode: 384 });
9484
- const marker = JSON.stringify({ owner: MARKER_OWNER, token: randomUUID3(), directory: path16.basename(directory), created_at: (/* @__PURE__ */ new Date()).toISOString() });
9485
- await writeFile(path16.join(directory, MARKER_FILE), marker, { encoding: "utf8", mode: 384 });
9663
+ await writeFile(path17.join(directory, GITIGNORE_FILE), GITIGNORE_BODY, { encoding: "utf8", mode: 384 });
9664
+ const marker = JSON.stringify({ owner: MARKER_OWNER, token: randomUUID3(), directory: path17.basename(directory), created_at: (/* @__PURE__ */ new Date()).toISOString() });
9665
+ await writeFile(path17.join(directory, MARKER_FILE), marker, { encoding: "utf8", mode: 384 });
9486
9666
  return { directory, marker, root, cleaned: false };
9487
9667
  }
9488
9668
  async function cleanupGeneratedDirectory(state) {
@@ -9496,7 +9676,7 @@ async function cleanupGeneratedDirectory(state) {
9496
9676
  state.cleaned = true;
9497
9677
  return;
9498
9678
  }
9499
- const marker = await readFile(path16.join(directory, MARKER_FILE), "utf8").catch(() => "");
9679
+ const marker = await readFile(path17.join(directory, MARKER_FILE), "utf8").catch(() => "");
9500
9680
  if (marker !== state.marker) throw new Error("refusing to clean a task-attachment directory without its exact marker");
9501
9681
  await rm(directory, { recursive: true, force: true });
9502
9682
  state.cleaned = true;
@@ -9515,7 +9695,7 @@ async function sweepStaleTaskAttachmentDirectories({
9515
9695
  now = Date.now(),
9516
9696
  maxAgeMs = DEFAULT_STALE_AGE_MS
9517
9697
  } = {}) {
9518
- const root = path16.resolve(tempRoot);
9698
+ const root = path17.resolve(tempRoot);
9519
9699
  if (!Number.isFinite(maxAgeMs) || maxAgeMs <= 0) throw new Error("stale attachment age must be positive");
9520
9700
  const entries = await readdir(root, { withFileTypes: true }).catch((error) => {
9521
9701
  if (error?.code === "ENOENT") return [];
@@ -9524,8 +9704,8 @@ async function sweepStaleTaskAttachmentDirectories({
9524
9704
  let removed = 0;
9525
9705
  for (const entry of entries) {
9526
9706
  if (!entry.isDirectory() || !hasGeneratedPrefix(entry.name)) continue;
9527
- const directory = assertGeneratedDirectory(path16.join(root, entry.name), root);
9528
- const markerRaw = await readFile(path16.join(directory, MARKER_FILE), "utf8").catch(() => "");
9707
+ const directory = assertGeneratedDirectory(path17.join(root, entry.name), root);
9708
+ const markerRaw = await readFile(path17.join(directory, MARKER_FILE), "utf8").catch(() => "");
9529
9709
  const marker = parseOwnedMarker(markerRaw, entry.name);
9530
9710
  if (!marker) continue;
9531
9711
  const directoryStat = await stat(directory);
@@ -9582,8 +9762,8 @@ async function materializeTaskAttachments(client, task, { worktreeDir } = {}) {
9582
9762
  const sha2562 = createHash4("sha256").update(content).digest("hex");
9583
9763
  if (sha2562 !== ref.sha256) throw new Error(`attachment ${ref.attachment_id} sha256 mismatch`);
9584
9764
  const name = sanitizeTaskAttachmentName(ref.name, index);
9585
- const filePath = path16.resolve(state.directory, name);
9586
- if (path16.dirname(filePath) !== state.directory) throw new Error(`attachment ${ref.attachment_id} resolved outside its task directory`);
9765
+ const filePath = path17.resolve(state.directory, name);
9766
+ if (path17.dirname(filePath) !== state.directory) throw new Error(`attachment ${ref.attachment_id} resolved outside its task directory`);
9587
9767
  await writeFile(filePath, content, { flag: "wx", mode: 384 });
9588
9768
  await chmod(filePath, 384);
9589
9769
  files.push({ attachmentId: ref.attachment_id, name, mime: ref.mime, sizeBytes: ref.size_bytes, sha256: sha2562, path: filePath });
@@ -9668,9 +9848,9 @@ async function readSpool(spoolDir = SPOOL_DIR) {
9668
9848
  }
9669
9849
  return out;
9670
9850
  }
9671
- async function readCloudMap(path22) {
9851
+ async function readCloudMap(path23) {
9672
9852
  try {
9673
- return JSON.parse(await readFile2(path22, "utf8"));
9853
+ return JSON.parse(await readFile2(path23, "utf8"));
9674
9854
  } catch {
9675
9855
  return {};
9676
9856
  }
@@ -9971,9 +10151,9 @@ function backoffMs(streak, baseMs) {
9971
10151
  if (streak <= 0) return 0;
9972
10152
  return Math.min(baseMs * 2 ** Math.min(streak - 1, 20), MAX_BACKOFF_MS);
9973
10153
  }
9974
- async function loadState(path22) {
10154
+ async function loadState(path23) {
9975
10155
  try {
9976
- const parsed = JSON.parse(await readFile3(path22, "utf8"));
10156
+ const parsed = JSON.parse(await readFile3(path23, "utf8"));
9977
10157
  if (parsed && typeof parsed === "object" && Number.isInteger(parsed.byte_offset) && parsed.byte_offset >= 0) {
9978
10158
  return { ...parsed, byte_offset: parsed.byte_offset };
9979
10159
  }
@@ -9981,15 +10161,15 @@ async function loadState(path22) {
9981
10161
  }
9982
10162
  return { byte_offset: 0, last_event_id: null, forwarded_total: 0, rejected_total: 0, rejected_event_ids: [] };
9983
10163
  }
9984
- async function saveState(path22, state) {
9985
- await mkdir2(dirname9(path22), { recursive: true });
9986
- await writeFile3(path22, JSON.stringify(state, null, 2), "utf8");
10164
+ async function saveState(path23, state) {
10165
+ await mkdir2(dirname9(path23), { recursive: true });
10166
+ await writeFile3(path23, JSON.stringify(state, null, 2), "utf8");
9987
10167
  }
9988
- async function readNewBytes(path22, offset, max) {
9989
- const st = await stat2(path22);
10168
+ async function readNewBytes(path23, offset, max) {
10169
+ const st = await stat2(path23);
9990
10170
  if (st.size <= offset) return { buf: Buffer.alloc(0), size: st.size };
9991
10171
  const length = Math.min(st.size - offset, max);
9992
- const fh = await open(path22, "r");
10172
+ const fh = await open(path23, "r");
9993
10173
  try {
9994
10174
  const buf = Buffer.alloc(length);
9995
10175
  const { bytesRead } = await fh.read(buf, 0, length, offset);
@@ -11082,10 +11262,10 @@ function formatShadowLogLine(record) {
11082
11262
  const loud = record.unexplained_fields?.length ? "!! " : "";
11083
11263
  return `${loud}[prepared-job-shadow] ${parts.join(" ")}`;
11084
11264
  }
11085
- function appendShadowRecord(record, { path: path22 = PREPARED_JOB_SHADOW_SINK, append = appendFileSync, mkdir: mkdir5 = mkdirSync8 } = {}) {
11265
+ function appendShadowRecord(record, { path: path23 = PREPARED_JOB_SHADOW_SINK, append = appendFileSync, mkdir: mkdir5 = mkdirSync8 } = {}) {
11086
11266
  try {
11087
- mkdir5(dirname10(path22), { recursive: true });
11088
- append(path22, `${JSON.stringify(record)}
11267
+ mkdir5(dirname10(path23), { recursive: true });
11268
+ append(path23, `${JSON.stringify(record)}
11089
11269
  `, "utf8");
11090
11270
  return true;
11091
11271
  } catch {
@@ -11405,7 +11585,7 @@ var init_shared = __esm({
11405
11585
  // ../../scripts/virtual-office/code-runner/account-usage/claude.mjs
11406
11586
  import fs10 from "node:fs";
11407
11587
  import os3 from "node:os";
11408
- import path17 from "node:path";
11588
+ import path18 from "node:path";
11409
11589
  function fileCaptureTime(filePath, explicit, statFn) {
11410
11590
  if (typeof explicit === "string" && explicit) return explicit;
11411
11591
  try {
@@ -11419,7 +11599,7 @@ function usageBaseUrl(env2 = process.env) {
11419
11599
  return String(raw).replace(/\/+$/, "");
11420
11600
  }
11421
11601
  function readOAuthToken({ homeDir = os3.homedir(), read = readJson2, now = Date.now() } = {}) {
11422
- const creds = read(path17.join(homeDir, ".claude", ".credentials.json"));
11602
+ const creds = read(path18.join(homeDir, ".claude", ".credentials.json"));
11423
11603
  const oauth = creds && typeof creds === "object" ? creds.claudeAiOauth : null;
11424
11604
  if (!oauth || typeof oauth !== "object") return null;
11425
11605
  const token2 = typeof oauth.accessToken === "string" ? oauth.accessToken.trim() : "";
@@ -11429,7 +11609,7 @@ function readOAuthToken({ homeDir = os3.homedir(), read = readJson2, now = Date.
11429
11609
  return token2;
11430
11610
  }
11431
11611
  function readAccountId({ homeDir = os3.homedir(), read = readJson2 } = {}) {
11432
- const cfg = read(path17.join(homeDir, ".claude.json"));
11612
+ const cfg = read(path18.join(homeDir, ".claude.json"));
11433
11613
  const account = cfg && typeof cfg === "object" ? cfg.oauthAccount : null;
11434
11614
  return account && typeof account.accountUuid === "string" ? account.accountUuid : null;
11435
11615
  }
@@ -11539,7 +11719,7 @@ function readClaudeFileUsage({
11539
11719
  if (age === null || age > MAX_FILE_AGE_MS) return null;
11540
11720
  return row;
11541
11721
  };
11542
- const statusPath = path17.join(homeDir, ".claude", "claude-usage.json");
11722
+ const statusPath = path18.join(homeDir, ".claude", "claude-usage.json");
11543
11723
  const status = read(statusPath);
11544
11724
  if (status && (status.seven_day || status.five_hour)) {
11545
11725
  const row = fresh(makeUsageRow({
@@ -11554,7 +11734,7 @@ function readClaudeFileUsage({
11554
11734
  }));
11555
11735
  if (row) return row;
11556
11736
  }
11557
- const weeklyPath = path17.join(homeDir, ".claude", "claude-weekly-usage.json");
11737
+ const weeklyPath = path18.join(homeDir, ".claude", "claude-weekly-usage.json");
11558
11738
  const weekly = read(weeklyPath);
11559
11739
  if (weekly) {
11560
11740
  const row = fresh(makeUsageRow({
@@ -11932,20 +12112,6 @@ var init_pr_watcher_failure_confirmation = __esm({
11932
12112
  }
11933
12113
  });
11934
12114
 
11935
- // ../../scripts/virtual-office/code-runner/error-message.mjs
11936
- function boundedErrorMessage(error, maxLength = 240) {
11937
- try {
11938
- return String(error?.message ?? error ?? "unknown error").slice(0, maxLength);
11939
- } catch {
11940
- return "unknown error";
11941
- }
11942
- }
11943
- var init_error_message = __esm({
11944
- "../../scripts/virtual-office/code-runner/error-message.mjs"() {
11945
- "use strict";
11946
- }
11947
- });
11948
-
11949
12115
  // ../../scripts/virtual-office/code-runner/watcher-coordination.mjs
11950
12116
  import { createHash as createHash7 } from "node:crypto";
11951
12117
  function ciFixOccurrenceKey({ repo, prNumber, headSha, repairAttempt }) {
@@ -12441,7 +12607,49 @@ var init_superseded_pr_source = __esm({
12441
12607
  });
12442
12608
 
12443
12609
  // ../../scripts/virtual-office/code-runner/ci-fix-prompt.mjs
12444
- function buildCiFixPrompt({ prNumber, repo, branch, headSha, failedChecks, prPatch, failedLogs }) {
12610
+ function fairSplit(aLen, bLen, budget) {
12611
+ if (aLen + bLen <= budget) return { a: aLen, b: bLen };
12612
+ const half = Math.floor(budget / 2);
12613
+ if (aLen <= half) return { a: aLen, b: budget - aLen };
12614
+ if (bLen <= half) return { a: budget - bLen, b: bLen };
12615
+ return { a: half, b: budget - half };
12616
+ }
12617
+ function boundHead(content, allocated, label) {
12618
+ if (content.length <= allocated) return content;
12619
+ const marker = `
12620
+ [${label} truncated to the first ${allocated} characters available in this dispatch]`;
12621
+ if (marker.length >= allocated) return marker.slice(0, Math.max(0, allocated));
12622
+ return `${content.slice(0, allocated - marker.length)}${marker}`;
12623
+ }
12624
+ function boundTail(content, allocated, label) {
12625
+ if (content.length <= allocated) return content;
12626
+ const marker = `[${label} truncated to the final ${allocated} characters available in this dispatch]
12627
+ `;
12628
+ if (marker.length >= allocated) return marker.slice(0, Math.max(0, allocated));
12629
+ return `${marker}${content.slice(-(allocated - marker.length))}`;
12630
+ }
12631
+ function boundedEvidenceShares({
12632
+ prPatch,
12633
+ failedLogs,
12634
+ fixedOverheadChars,
12635
+ prNumber,
12636
+ maxTotalChars = CI_FIX_PROMPT_MAX_CHARS
12637
+ }) {
12638
+ const patchWanted = String(prPatch || "").trim() || NO_PATCH_EVIDENCE;
12639
+ const logsWanted = String(failedLogs || "").trim() || NO_LOG_EVIDENCE;
12640
+ const available = maxTotalChars - fixedOverheadChars;
12641
+ if (available < NO_PATCH_EVIDENCE.length + NO_LOG_EVIDENCE.length) {
12642
+ throw new Error(
12643
+ `CI-fix prompt for PR #${prNumber ?? "?"} cannot fit its mandatory instructions and identity (${fixedOverheadChars} chars) plus even an empty-evidence notice inside the ${maxTotalChars}-character dispatch limit \u2014 refusing to send a malformed or instructions-truncated request`
12644
+ );
12645
+ }
12646
+ const { a: patchShare, b: logsShare } = fairSplit(patchWanted.length, logsWanted.length, available);
12647
+ return {
12648
+ patchBody: boundHead(patchWanted, patchShare, "PR patch"),
12649
+ logsBody: boundTail(logsWanted, logsShare, "failed-job evidence")
12650
+ };
12651
+ }
12652
+ function renderHeader({ prNumber, repo, branch, headSha, failedChecks }) {
12445
12653
  return [
12446
12654
  `${CI_FIX_MARKER} An AlgoHQ-dispatched pull request has FAILING CI and needs a fix.`,
12447
12655
  "",
@@ -12460,24 +12668,45 @@ function buildCiFixPrompt({ prNumber, repo, branch, headSha, failedChecks, prPat
12460
12668
  "VO-ALLOW-PR-OVERLAP marker. Your work ships: finish the fix completely, leave UNCOMMITTED",
12461
12669
  "edits, and the runner publishes for you. A denied git/gh is EXPECTED; do NOT retry it.",
12462
12670
  "",
12463
- HEADLESS_EXECUTION_CONTRACT,
12671
+ HEADLESS_EXECUTION_CONTRACT
12672
+ ].join("\n");
12673
+ }
12674
+ function assemblePrompt(header, patchBody, logsBody) {
12675
+ return [
12676
+ header,
12464
12677
  "",
12465
12678
  "## Bounded source PR patch excerpt",
12466
12679
  "```diff",
12467
- prPatch || "[No excerpt available; the complete exact source is still materialized in the worktree.]",
12680
+ patchBody,
12468
12681
  "```",
12469
12682
  "",
12470
12683
  "## Failed-job evidence",
12471
12684
  "```text",
12472
- failedLogs || "[No failed-job log was available; use the named checks and patch.]",
12685
+ logsBody,
12473
12686
  "```"
12474
12687
  ].join("\n");
12475
12688
  }
12689
+ function buildCiFixPrompt({ prNumber, repo, branch, headSha, failedChecks, prPatch, failedLogs }) {
12690
+ const header = renderHeader({ prNumber, repo, branch, headSha, failedChecks });
12691
+ const fixedOverheadChars = assemblePrompt(header, "", "").length;
12692
+ const { patchBody, logsBody } = boundedEvidenceShares({
12693
+ prPatch,
12694
+ failedLogs,
12695
+ fixedOverheadChars,
12696
+ prNumber,
12697
+ maxTotalChars: CI_FIX_PROMPT_MAX_CHARS
12698
+ });
12699
+ return assemblePrompt(header, patchBody, logsBody);
12700
+ }
12701
+ var CI_FIX_PROMPT_MAX_CHARS, NO_PATCH_EVIDENCE, NO_LOG_EVIDENCE;
12476
12702
  var init_ci_fix_prompt = __esm({
12477
12703
  "../../scripts/virtual-office/code-runner/ci-fix-prompt.mjs"() {
12478
12704
  "use strict";
12479
12705
  init_superseded_pr_source();
12480
12706
  init_headless_execution_contract();
12707
+ CI_FIX_PROMPT_MAX_CHARS = 9900;
12708
+ NO_PATCH_EVIDENCE = "[No excerpt available; the complete exact source is still materialized in the worktree.]";
12709
+ NO_LOG_EVIDENCE = "[No failed-job log was available; use the named checks and patch.]";
12481
12710
  }
12482
12711
  });
12483
12712
 
@@ -12500,7 +12729,7 @@ function noteCiViaRest(log2) {
12500
12729
  log2("watch: CI status read via REST check-runs/status (gh's GraphQL rollup needs actions:read for checkSuite.workflowRun, which the read scope does not carry)");
12501
12730
  }
12502
12731
  async function readCommitCiViaRest(repo, sha, { run, env: env2 }) {
12503
- const api = async (path22) => JSON.parse(await run("gh", ["api", path22], { timeout: 3e4, env: env2 }) || "{}");
12732
+ const api = async (path23) => JSON.parse(await run("gh", ["api", path23], { timeout: 3e4, env: env2 }) || "{}");
12504
12733
  const rollup = [];
12505
12734
  let total = null;
12506
12735
  for (let page = 1; page <= REST_MAX_PAGES && (total === null || rollup.length < total); page += 1) {
@@ -13164,9 +13393,9 @@ function buildControlHandler({ getStatus, requestStop, allowedOrigin }) {
13164
13393
  res.end();
13165
13394
  return;
13166
13395
  }
13167
- const path22 = String(req.url || "").split("?")[0];
13396
+ const path23 = String(req.url || "").split("?")[0];
13168
13397
  res.setHeader("content-type", "application/json");
13169
- if (req.method === "GET" && path22 === "/status") {
13398
+ if (req.method === "GET" && path23 === "/status") {
13170
13399
  let status;
13171
13400
  try {
13172
13401
  status = getStatus();
@@ -13177,7 +13406,7 @@ function buildControlHandler({ getStatus, requestStop, allowedOrigin }) {
13177
13406
  res.end(JSON.stringify({ ok: true, ...status }));
13178
13407
  return;
13179
13408
  }
13180
- if (req.method === "POST" && path22 === "/stop") {
13409
+ if (req.method === "POST" && path23 === "/stop") {
13181
13410
  if (!isControlOriginAllowed(req.headers.origin, allowedOrigin) || !req.headers["x-vo-control"]) {
13182
13411
  res.statusCode = 403;
13183
13412
  res.end(JSON.stringify({ ok: false, error: "forbidden" }));
@@ -13371,22 +13600,22 @@ var init_effort_mode_config = __esm({
13371
13600
  import { randomUUID as randomUUID6 } from "node:crypto";
13372
13601
  import fs11 from "node:fs";
13373
13602
  import os4 from "node:os";
13374
- import path18 from "node:path";
13603
+ import path19 from "node:path";
13375
13604
  import { fileURLToPath as fileURLToPath6 } from "node:url";
13376
13605
  function userCacheRoot() {
13377
13606
  try {
13378
13607
  const home = os4.homedir();
13379
- if (home) return path18.join(home, ".claude");
13608
+ if (home) return path19.join(home, ".claude");
13380
13609
  } catch {
13381
13610
  }
13382
- return path18.join(os4.tmpdir(), `vo-model-registry-${randomUUID6()}`);
13611
+ return path19.join(os4.tmpdir(), `vo-model-registry-${randomUUID6()}`);
13383
13612
  }
13384
13613
  function resolveCacheBaseDir(env2 = process.env, moduleDir = __dirname) {
13385
13614
  if (env2.VO_MODEL_REGISTRY_CACHE_DIR) return env2.VO_MODEL_REGISTRY_CACHE_DIR;
13386
13615
  if (env2.VO_RUNNER_RUNTIME_ROOT) return env2.VO_RUNNER_RUNTIME_ROOT;
13387
- const segments = moduleDir.split(path18.sep);
13616
+ const segments = moduleDir.split(path19.sep);
13388
13617
  const isRepoCheckout2 = segments.at(-1) === "virtual-office" && segments.at(-2) === "scripts";
13389
- return isRepoCheckout2 ? path18.resolve(moduleDir, "..", "..") : userCacheRoot();
13618
+ return isRepoCheckout2 ? path19.resolve(moduleDir, "..", "..") : userCacheRoot();
13390
13619
  }
13391
13620
  function uniqueModels(models = []) {
13392
13621
  return [...new Set(models.map((model) => String(model || "").trim()).filter(Boolean))];
@@ -13509,7 +13738,7 @@ function readCache(cacheFile = DEFAULT_CACHE_FILE, nowMs = Date.now(), ttlMs = D
13509
13738
  }
13510
13739
  }
13511
13740
  function writeCache(cacheFile = DEFAULT_CACHE_FILE, payload) {
13512
- fs11.mkdirSync(path18.dirname(cacheFile), { recursive: true });
13741
+ fs11.mkdirSync(path19.dirname(cacheFile), { recursive: true });
13513
13742
  fs11.writeFileSync(cacheFile, JSON.stringify(payload, null, 2));
13514
13743
  }
13515
13744
  async function fetchRegistryCatalog({
@@ -13567,13 +13796,13 @@ var __dirname, DEFAULT_CACHE_DIR, DEFAULT_CACHE_FILE, DEFAULT_TTL_MS3, ANTHROPIC
13567
13796
  var init_model_registry = __esm({
13568
13797
  "../../scripts/virtual-office/model-registry.mjs"() {
13569
13798
  "use strict";
13570
- __dirname = path18.dirname(fileURLToPath6(import.meta.url));
13571
- DEFAULT_CACHE_DIR = path18.join(
13799
+ __dirname = path19.dirname(fileURLToPath6(import.meta.url));
13800
+ DEFAULT_CACHE_DIR = path19.join(
13572
13801
  resolveCacheBaseDir(),
13573
13802
  ".virtual-office-cache",
13574
13803
  "model-registry"
13575
13804
  );
13576
- DEFAULT_CACHE_FILE = path18.join(DEFAULT_CACHE_DIR, "catalog.json");
13805
+ DEFAULT_CACHE_FILE = path19.join(DEFAULT_CACHE_DIR, "catalog.json");
13577
13806
  DEFAULT_TTL_MS3 = 60 * 60 * 1e3;
13578
13807
  ANTHROPIC_API_VERSION = "2023-06-01";
13579
13808
  FAMILY_DEFINITIONS = {
@@ -14212,9 +14441,9 @@ function operatorTierToRung(tier, difficulty, thresholds) {
14212
14441
  if (tier === "best" && difficulty >= thresholds.rungBounds.R5) return "R5";
14213
14442
  return base;
14214
14443
  }
14215
- function readCodexModelsCache({ path: path22 = DEFAULT_CODEX_MODELS_CACHE, read = readFileSync9 } = {}) {
14444
+ function readCodexModelsCache({ path: path23 = DEFAULT_CODEX_MODELS_CACHE, read = readFileSync9 } = {}) {
14216
14445
  try {
14217
- const parsed = JSON.parse(read(path22, "utf8"));
14446
+ const parsed = JSON.parse(read(path23, "utf8"));
14218
14447
  return Array.isArray(parsed?.models) ? parsed : null;
14219
14448
  } catch {
14220
14449
  return null;
@@ -14471,15 +14700,15 @@ function formatDecisionReason(decision, maxLen = 480) {
14471
14700
  const s = `[${decision.routerVersion}] ${decision.taskClass} d=${decision.difficulty} c=${decision.confidence} \u2192 ${decision.rung}/${decision.tier}${decision.effort ? ` effort=${decision.effort}` : ""} turns=${decision.maxTurns} $${decision.maxBudgetUsd}${decision.flags.length ? ` [${decision.flags.join(",")}]` : ""} :: ${decision.reasons.join("; ")}`;
14472
14701
  return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
14473
14702
  }
14474
- function appendDecisionFallback(decision, { path: path22 = DECISION_FALLBACK_PATH, append = appendFileSync2, mkdir: mkdir5 = mkdirSync9, task, thresholds, roleCostInputs } = {}) {
14703
+ function appendDecisionFallback(decision, { path: path23 = DECISION_FALLBACK_PATH, append = appendFileSync2, mkdir: mkdir5 = mkdirSync9, task, thresholds, roleCostInputs } = {}) {
14475
14704
  try {
14476
- mkdir5(dirname12(path22), { recursive: true });
14477
- append(path22, `${JSON.stringify(decision)}
14705
+ mkdir5(dirname12(path23), { recursive: true });
14706
+ append(path23, `${JSON.stringify(decision)}
14478
14707
  `, "utf8");
14479
14708
  if (isRouterDecision(decision)) {
14480
14709
  try {
14481
14710
  const records = buildShadowRecords({ decision, task, thresholds: thresholds || loadThresholds(), roleCostInputs });
14482
- for (const record of records) append(path22, `${JSON.stringify(record)}
14711
+ for (const record of records) append(path23, `${JSON.stringify(record)}
14483
14712
  `, "utf8");
14484
14713
  } catch {
14485
14714
  }
@@ -15533,7 +15762,7 @@ var init_inference_task_runner = __esm({
15533
15762
  // ../../scripts/virtual-office/code-runner/isolation-audit.mjs
15534
15763
  import fs12 from "node:fs";
15535
15764
  import fsp11 from "node:fs/promises";
15536
- import path19 from "node:path";
15765
+ import path20 from "node:path";
15537
15766
  async function defaultRun3(command, args, cwd, options = {}) {
15538
15767
  return runProcess2(command, args, { cwd, timeout: 6e4, ...options });
15539
15768
  }
@@ -15546,7 +15775,7 @@ async function canonicalRootForWorktree(worktreeDir, run) {
15546
15775
  "--path-format=absolute",
15547
15776
  "--git-common-dir"
15548
15777
  ])).trim();
15549
- const root = path19.dirname(commonDir);
15778
+ const root = path20.dirname(commonDir);
15550
15779
  return samePath3(root, worktreeDir) ? null : root;
15551
15780
  }
15552
15781
  async function snapshot(root, run) {
@@ -15588,21 +15817,21 @@ async function changedPaths(root, run) {
15588
15817
  }
15589
15818
  async function quarantineCanonicalWrites({ baseline, worktreeDir, taskId, run, now }) {
15590
15819
  const paths = await changedPaths(baseline.root, run);
15591
- const quarantineDir = path19.join(
15592
- path19.dirname(worktreeDir),
15820
+ const quarantineDir = path20.join(
15821
+ path20.dirname(worktreeDir),
15593
15822
  ".canonical-recovery",
15594
15823
  `${String(taskId || "unknown").replace(/[^a-z0-9-]/gi, "-")}-${now().toISOString().replace(/[:.]/g, "-")}`
15595
15824
  );
15596
15825
  await fsp11.mkdir(quarantineDir, { recursive: true });
15597
15826
  const patch = await git2(run, baseline.root, ["diff", "--binary", "HEAD"], { raw: true });
15598
- await fsp11.writeFile(path19.join(quarantineDir, "tracked.patch"), patch, "utf8");
15827
+ await fsp11.writeFile(path20.join(quarantineDir, "tracked.patch"), patch, "utf8");
15599
15828
  for (const relative of paths.untracked) {
15600
- const source = path19.join(baseline.root, relative);
15601
- const target = path19.join(quarantineDir, "untracked", relative);
15602
- await fsp11.mkdir(path19.dirname(target), { recursive: true });
15829
+ const source = path20.join(baseline.root, relative);
15830
+ const target = path20.join(quarantineDir, "untracked", relative);
15831
+ await fsp11.mkdir(path20.dirname(target), { recursive: true });
15603
15832
  await fsp11.copyFile(source, target);
15604
15833
  }
15605
- await fsp11.writeFile(path19.join(quarantineDir, "manifest.json"), `${JSON.stringify({
15834
+ await fsp11.writeFile(path20.join(quarantineDir, "manifest.json"), `${JSON.stringify({
15606
15835
  taskId,
15607
15836
  canonicalRoot: baseline.root,
15608
15837
  canonicalHead: baseline.head,
@@ -15624,8 +15853,8 @@ async function restoreExactCanonicalPaths(baseline, evidence, run) {
15624
15853
  ]);
15625
15854
  }
15626
15855
  for (const relative of evidence.untracked) {
15627
- const target = path19.resolve(baseline.root, relative);
15628
- const prefix = `${path19.resolve(baseline.root)}${path19.sep}`;
15856
+ const target = path20.resolve(baseline.root, relative);
15857
+ const prefix = `${path20.resolve(baseline.root)}${path20.sep}`;
15629
15858
  if (!target.startsWith(prefix) || !fs12.existsSync(target)) continue;
15630
15859
  await fsp11.rm(target, { force: true });
15631
15860
  }
@@ -15662,7 +15891,7 @@ var init_isolation_audit = __esm({
15662
15891
  init_process_runner2();
15663
15892
  splitZ2 = (value) => String(value || "").split("\0").map((item) => item.trim()).filter(Boolean);
15664
15893
  samePath3 = (left, right) => {
15665
- const [a, b] = [left, right].map((value) => path19.resolve(value));
15894
+ const [a, b] = [left, right].map((value) => path20.resolve(value));
15666
15895
  return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
15667
15896
  };
15668
15897
  }
@@ -15984,7 +16213,11 @@ async function finalizePublishedPr({
15984
16213
  const overlapBlocked = pr.overlapDraft === true;
15985
16214
  const blockedRefs = Array.isArray(pr.overlapBlockedBy) && pr.overlapBlockedBy.length > 0 ? pr.overlapBlockedBy.map((n) => `#${n}`).join(", ") : "unresolved (see PR body for the gate report)";
15986
16215
  const overlapNote = overlapBlocked ? ` as DRAFT \u2014 overlap-blocked by ${blockedRefs} (finished work preserved; mark ready after the overlap is resolved)` : "";
15987
- const fixDispatchGuard = overlapBlocked ? { allowFixDispatch: false } : {};
16216
+ const serializedRunnerFailure = serializeRunnerFailure(run?.failure);
16217
+ const runnerFailure = Boolean(serializedRunnerFailure);
16218
+ const runnerFailureRecord = serializedRunnerFailure ? JSON.parse(serializedRunnerFailure) : null;
16219
+ const runnerFailureMessage = runnerFailureRecord ? `blocked by ${runnerFailureRecord.code}; operator review required: ${runnerFailureRecord.operator_next_action}` : "";
16220
+ const fixDispatchGuard = overlapBlocked || runnerFailure ? { allowFixDispatch: false } : {};
15988
16221
  let resumeQueued = false;
15989
16222
  if (cfg.watchEnabled) {
15990
16223
  try {
@@ -15995,7 +16228,7 @@ async function finalizePublishedPr({
15995
16228
  taskId: id,
15996
16229
  operatorId: task.operator_id,
15997
16230
  tenantId: task.tenant_id,
15998
- needsContinuation: partial && !rateLimitResume && (task.continuation_attempt ?? 0) < (task.continuation_max_attempts ?? 3),
16231
+ needsContinuation: partial && !rateLimitResume && !runnerFailure && (task.continuation_attempt ?? 0) < (task.continuation_max_attempts ?? 3),
15999
16232
  continuationExhausted: partial && (task.continuation_attempt ?? 0) >= (task.continuation_max_attempts ?? 3),
16000
16233
  repairChain: task.repair_chain ?? {
16001
16234
  root_pr_number: pr.prNumber,
@@ -16052,15 +16285,15 @@ async function finalizePublishedPr({
16052
16285
  safeProgress: safeProgress2,
16053
16286
  log: log2,
16054
16287
  patch: {
16055
- status: "pr_opened",
16056
- message: `opened ${pr.prUrl}${overlapNote}${pr.newCommit === false ? " (no new commits \u2014 the branch already held every change)" : ""}`,
16288
+ status: runnerFailure ? "failed" : "pr_opened",
16289
+ message: runnerFailure ? `${runnerFailureMessage}; preserved PR ${pr.prUrl} for owner review` : `opened ${pr.prUrl}${overlapNote}${pr.newCommit === false ? " (no new commits \u2014 the branch already held every change)" : ""}`,
16057
16290
  pr_url: pr.prUrl,
16058
16291
  pr_number: pr.prNumber,
16059
16292
  pr_branch: pr.branch,
16060
16293
  result: (() => {
16061
16294
  const prefix = overlapBlocked ? `[VO-PUBLISH-OVERLAP-BLOCKED: ${blockedRefs}] ` : "";
16062
16295
  const room = 2e3 - prefix.length;
16063
- return `${prefix}${partial ? partialPrContinuationResult(run, room, rateLimitResume ? "rate_limited" : null) : String(run.summary).slice(0, room)}`;
16296
+ return `${prefix}${partial || runnerFailure ? partialPrContinuationResult(run, room, rateLimitResume ? "rate_limited" : null) : String(run.summary).slice(0, room)}`;
16064
16297
  })(),
16065
16298
  ...runOutcomePatch(run),
16066
16299
  ...terminalLedgerPatch(run)
@@ -16104,13 +16337,14 @@ var init_publication_outcome = __esm({
16104
16337
  init_outcome_commit();
16105
16338
  init_terminal_delivery();
16106
16339
  init_rate_limit_resume();
16340
+ init_error_message();
16107
16341
  defaultRunCommand4 = (cmd, args, cwd, opts = {}) => runProcess2(cmd, args, { cwd, ...opts });
16108
16342
  }
16109
16343
  });
16110
16344
 
16111
16345
  // ../../scripts/virtual-office/code-runner/committed-scratch-cleanup.mjs
16112
16346
  import fsp12 from "node:fs/promises";
16113
- import path20 from "node:path";
16347
+ import path21 from "node:path";
16114
16348
  function defaultRun4(command, args, cwd, options = {}) {
16115
16349
  return runProcess2(command, args, { cwd, ...options });
16116
16350
  }
@@ -16118,13 +16352,13 @@ async function resolveSafeScratchTarget(worktreeDir, file) {
16118
16352
  if (!isAgentScratch(file)) {
16119
16353
  throw new Error(`refusing to remove non-scratch publication path: ${file}`);
16120
16354
  }
16121
- const root = path20.resolve(worktreeDir);
16122
- const target = path20.resolve(root, file);
16123
- const relative = path20.relative(root, target);
16124
- if (!relative || relative.startsWith(`..${path20.sep}`) || path20.isAbsolute(relative)) {
16355
+ const root = path21.resolve(worktreeDir);
16356
+ const target = path21.resolve(root, file);
16357
+ const relative = path21.relative(root, target);
16358
+ if (!relative || relative.startsWith(`..${path21.sep}`) || path21.isAbsolute(relative)) {
16125
16359
  throw new Error(`refusing to remove publication scratch outside worktree: ${file}`);
16126
16360
  }
16127
- for (let cursor = target; cursor !== root; cursor = path20.dirname(cursor)) {
16361
+ for (let cursor = target; cursor !== root; cursor = path21.dirname(cursor)) {
16128
16362
  try {
16129
16363
  if ((await fsp12.lstat(cursor)).isSymbolicLink()) {
16130
16364
  throw new Error(`refusing to follow symlink while removing publication scratch: ${file}`);
@@ -16246,7 +16480,7 @@ var init_publication_scope = __esm({
16246
16480
  // ../../scripts/virtual-office/code-runner/recovery-ledger.mjs
16247
16481
  import fs13 from "node:fs";
16248
16482
  import fsp13 from "node:fs/promises";
16249
- import path21 from "node:path";
16483
+ import path22 from "node:path";
16250
16484
  function recoveryTaskId(prompt) {
16251
16485
  const match = String(prompt || "").match(/VO_RECOVERY_FROM_CODE_TASK:\s*([0-9a-f-]{36})/i);
16252
16486
  return match ? match[1].toLowerCase() : null;
@@ -16260,10 +16494,10 @@ function cloneLeaf(repo) {
16260
16494
  function recoveryLedgerCandidates(repo, clonesRoot2) {
16261
16495
  const leaf = cloneLeaf(repo);
16262
16496
  if (!leaf || !clonesRoot2) return [];
16263
- const canonical = path21.join(clonesRoot2, leaf);
16497
+ const canonical = path22.join(clonesRoot2, leaf);
16264
16498
  return [
16265
- path21.join(clonesRoot2, ".agent-worktrees", leaf, "recovery-ledger.jsonl"),
16266
- path21.join(canonical, ".agent-worktrees", "recovery-ledger.jsonl")
16499
+ path22.join(clonesRoot2, ".agent-worktrees", leaf, "recovery-ledger.jsonl"),
16500
+ path22.join(canonical, ".agent-worktrees", "recovery-ledger.jsonl")
16267
16501
  ];
16268
16502
  }
16269
16503
  async function readLedger(file, readFile6) {
@@ -16441,7 +16675,16 @@ function isMaxTurnExhaustion(run = {}, maxTurns) {
16441
16675
  return Number.isInteger(maxTurns) && maxTurns > 0 && Number.isInteger(run.numTurns) && run.numTurns > maxTurns;
16442
16676
  }
16443
16677
  function decideNoChangesTerminalStatus({ partial, run = {}, maxTurns } = {}) {
16678
+ const structuredFailure = serializeRunnerFailure(run.failure);
16444
16679
  if (!partial) {
16680
+ if (structuredFailure) {
16681
+ const failure = JSON.parse(structuredFailure);
16682
+ return {
16683
+ status: "failed",
16684
+ message: `agent reported a runner blocker with no file changes \u2014 ${failure.code}; ${failure.operator_next_action}`,
16685
+ result: partialPrContinuationResult(run, RESULT_LIMIT)
16686
+ };
16687
+ }
16445
16688
  if (reportsBlocker(run.summary)) {
16446
16689
  return {
16447
16690
  status: "failed",
@@ -16465,8 +16708,8 @@ function decideNoChangesTerminalStatus({ partial, run = {}, maxTurns } = {}) {
16465
16708
  const cause = String(run.summary || "").trim();
16466
16709
  return {
16467
16710
  status: "failed",
16468
- message: cause ? `agent made no file changes \u2014 ${cause.slice(0, 200)}` : "agent made no file changes",
16469
- result: (cause || "no_changes").slice(0, RESULT_LIMIT)
16711
+ message: structuredFailure ? `agent made no file changes \u2014 ${JSON.parse(structuredFailure).code}; ${JSON.parse(structuredFailure).operator_next_action}` : cause ? `agent made no file changes \u2014 ${cause.slice(0, 200)}` : "agent made no file changes",
16712
+ result: structuredFailure ? partialPrContinuationResult(run, RESULT_LIMIT) : (cause || "no_changes").slice(0, RESULT_LIMIT)
16470
16713
  };
16471
16714
  }
16472
16715
  async function closeSupersededSourceOnNoChanges({
@@ -16599,6 +16842,8 @@ var init_no_changes_terminal_status = __esm({
16599
16842
  init_terminal_ledger_patch();
16600
16843
  init_outcome_commit();
16601
16844
  init_terminal_delivery();
16845
+ init_error_message();
16846
+ init_partial_pr_continuation();
16602
16847
  RESULT_LIMIT = 2e3;
16603
16848
  }
16604
16849
  });
@@ -16916,6 +17161,50 @@ var init_daemon_config = __esm({
16916
17161
  });
16917
17162
 
16918
17163
  // ../../scripts/virtual-office/code-runner/task-worktree-preparation.mjs
17164
+ async function git3(worktreeDir, args, runCommand = runProcess2) {
17165
+ const result = await runCommand("git", args, { cwd: worktreeDir, timeoutMs: 6e4 });
17166
+ if (result.status !== 0) throw new Error(`source binding git ${args[0]} failed before model execution`);
17167
+ return String(result.stdout || "").trim();
17168
+ }
17169
+ function hasAffirmativePreSpawnNoSpendEvidence(parentTask) {
17170
+ return Boolean(parentTask && parentTask.status === "failed" && parentTask.completed_at && parentTask.execution_started_at === null && parentTask.usage_reported_at && parentTask.cost_usd === 0 && parentTask.cost_basis === "no_agent_spawned" && typeof parentTask.claimed_by === "string" && parentTask.claimed_by.length > 0 && RUNNER_INSTANCE_ID.test(parentTask.runner_instance_id ?? "") && parentTask.usage_runner_instance_id === parentTask.runner_instance_id && !parentTask.token_usage && !parentTask.model_usage && !parentTask.detached_run_economics?.length && !parentTask.resumed_from && !parentTask.pr_branch && !parentTask.pr_url && !parentTask.pr_number);
17171
+ }
17172
+ async function bindTaskExecutionSource({
17173
+ client,
17174
+ task,
17175
+ parentTask,
17176
+ worktreeDir,
17177
+ continuationRestore,
17178
+ runCommand = runProcess2
17179
+ }) {
17180
+ const baseSha = task.comparative_base_sha;
17181
+ if (!baseSha) return null;
17182
+ if (!EXACT_SHA.test(baseSha)) throw new Error("comparative base SHA is invalid");
17183
+ const actualSha = await git3(worktreeDir, ["rev-parse", "HEAD"], runCommand);
17184
+ if (!EXACT_SHA.test(actualSha)) throw new Error("worktree HEAD is not an exact commit SHA");
17185
+ if (!task.resumed_from) {
17186
+ if (actualSha !== baseSha) throw new Error("initial comparative worktree HEAD does not match requested base");
17187
+ } else if (continuationRestore) {
17188
+ if (task.pr_head_sha_at_enqueue && actualSha !== task.pr_head_sha_at_enqueue) {
17189
+ throw new Error("continuation worktree HEAD does not match its expected continuation head");
17190
+ }
17191
+ await git3(worktreeDir, ["merge-base", "--is-ancestor", baseSha, actualSha], runCommand);
17192
+ } else {
17193
+ if (!hasAffirmativePreSpawnNoSpendEvidence(parentTask)) {
17194
+ throw new Error("continuation branch is missing and prior execution state is unknown; recovery review required");
17195
+ }
17196
+ if (actualSha !== baseSha) throw new Error("safe pre-spawn continuation did not rematerialize the original base");
17197
+ }
17198
+ const acknowledgement = await client.postProgress(task.code_task_id, {
17199
+ execution_start_sha: actualSha,
17200
+ stage: "preparing_worktree",
17201
+ message: `Exact execution source ${actualSha} verified before model start`
17202
+ });
17203
+ if (acknowledgement?.terminal || acknowledgement?.task?.execution_start_sha !== actualSha) {
17204
+ throw new Error("control plane did not durably acknowledge execution_start_sha");
17205
+ }
17206
+ return { baseSha, executionStartSha: actualSha };
17207
+ }
16919
17208
  async function prepareTaskWorktree({ client, task, cfg, safeProgress: safeProgress2, log: log2 }) {
16920
17209
  const id = task.code_task_id;
16921
17210
  await safeProgress2(client, id, runnerStagePatch(
@@ -16932,7 +17221,7 @@ async function prepareTaskWorktree({ client, task, cfg, safeProgress: safeProgre
16932
17221
  const wt = await createFixWorktree(
16933
17222
  "code-task",
16934
17223
  { source: id.slice(0, 8), repo: task.repo },
16935
- { githubToken }
17224
+ { githubToken, baseSha: task.comparative_base_sha ?? null }
16936
17225
  );
16937
17226
  if (!wt.worktreeName || !wt.worktreeDir) {
16938
17227
  throw new Error("worktree isolation failure \u2014 refusing to run in the main tree");
@@ -16962,8 +17251,16 @@ async function prepareTaskWorktree({ client, task, cfg, safeProgress: safeProgre
16962
17251
  });
16963
17252
  log2(repairPlan.strategy === "in-place" ? `task ${id}: repairing ${task.repo}#${task.repair_pr_number}@${repairPlan.headSha} IN PLACE on ${repairPlan.remoteBranch}` : `task ${id}: materialized full repair source ${task.repo}#${task.repair_pr_number}@${repairPlan.headSha} for supersede (${repairPlan.reason})${repairPlan.conflicted ? " with conflicts for the agent to resolve" : ""}`);
16964
17253
  }
16965
- return { wt, githubToken, agentGithubReadToken, continuationRestore, repairPlan };
17254
+ const sourceBinding = await bindTaskExecutionSource({
17255
+ client,
17256
+ task,
17257
+ parentTask,
17258
+ worktreeDir: wt.worktreeDir,
17259
+ continuationRestore
17260
+ });
17261
+ return { wt, githubToken, agentGithubReadToken, continuationRestore, repairPlan, sourceBinding };
16966
17262
  }
17263
+ var EXACT_SHA, RUNNER_INSTANCE_ID;
16967
17264
  var init_task_worktree_preparation = __esm({
16968
17265
  "../../scripts/virtual-office/code-runner/task-worktree-preparation.mjs"() {
16969
17266
  "use strict";
@@ -16971,6 +17268,9 @@ var init_task_worktree_preparation = __esm({
16971
17268
  init_resume_branch();
16972
17269
  init_repair_publication_strategy();
16973
17270
  init_task_helpers();
17271
+ init_process_runner2();
17272
+ EXACT_SHA = /^[0-9a-f]{40}$/u;
17273
+ RUNNER_INSTANCE_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
16974
17274
  }
16975
17275
  });
16976
17276