@algosuite/vo-mcp 0.2.0-beta.29 → 0.2.0-beta.30

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.
@@ -77,11 +77,11 @@ __export(credential_store_exports, {
77
77
  readStoredCredentialKeychainOnly: () => readStoredCredentialKeychainOnly,
78
78
  writeStoredCredential: () => writeStoredCredential
79
79
  });
80
- import { homedir } from "node:os";
80
+ import { homedir as homedir2 } from "node:os";
81
81
  import { join, dirname } from "node:path";
82
82
  import {
83
- existsSync,
84
- mkdirSync,
83
+ existsSync as existsSync2,
84
+ mkdirSync as mkdirSync2,
85
85
  readFileSync,
86
86
  writeFileSync,
87
87
  chmodSync,
@@ -90,7 +90,7 @@ import {
90
90
  function credentialPath(env2 = process.env) {
91
91
  const override = env2["VO_MCP_CREDENTIALS_PATH"]?.trim();
92
92
  if (override) return override;
93
- return join(homedir(), ".config", "vo-mcp", "credentials.json");
93
+ return join(homedir2(), ".config", "vo-mcp", "credentials.json");
94
94
  }
95
95
  function keychainEnabled(env2, keychain) {
96
96
  const disabled = (env2["VO_MCP_DISABLE_KEYCHAIN"] ?? "").trim().toLowerCase();
@@ -119,7 +119,7 @@ function deserialize(raw) {
119
119
  function readFromFile(env2) {
120
120
  try {
121
121
  const p = credentialPath(env2);
122
- if (!existsSync(p)) return null;
122
+ if (!existsSync2(p)) return null;
123
123
  return deserialize(readFileSync(p, "utf8"));
124
124
  } catch {
125
125
  return null;
@@ -146,7 +146,7 @@ function deleteFile(env2) {
146
146
  }
147
147
  function writeToFile(payload, env2) {
148
148
  const p = credentialPath(env2);
149
- mkdirSync(dirname(p), { recursive: true });
149
+ mkdirSync2(dirname(p), { recursive: true });
150
150
  writeFileSync(p, `${JSON.stringify(payload, null, 2)}
151
151
  `, { mode: 384 });
152
152
  try {
@@ -505,7 +505,8 @@ async function runProcess(command, args = [], options = {}) {
505
505
  });
506
506
  }
507
507
  async function commandExists(command, options = {}) {
508
- const checker = process.platform === "win32" ? "where" : "which";
508
+ const platform = options.platform || process.platform;
509
+ const checker = platform === "win32" ? "where" : "which";
509
510
  const result = await (options.runner || runProcess)(checker, [command], {
510
511
  timeoutMs: 1e4
511
512
  });
@@ -841,9 +842,141 @@ var init_pnpm_canonical_health = __esm({
841
842
  }
842
843
  });
843
844
 
845
+ // src/runner/pnpm-command.mjs
846
+ import fs2 from "node:fs";
847
+ import path3 from "node:path";
848
+ function isWindowsDriveOrUnc(value) {
849
+ return WINDOWS_DRIVE_OR_UNC_RE.test(String(value || ""));
850
+ }
851
+ function portableDirname(value) {
852
+ if (isWindowsDriveOrUnc(value)) return path3.win32.dirname(value);
853
+ if (path3.posix.isAbsolute(value)) return path3.posix.dirname(value);
854
+ return path3.dirname(value);
855
+ }
856
+ function portableJoin(root, ...segments) {
857
+ if (isWindowsDriveOrUnc(root)) return path3.win32.join(root, ...segments);
858
+ if (path3.posix.isAbsolute(root)) return path3.posix.join(root, ...segments);
859
+ return path3.join(root, ...segments);
860
+ }
861
+ function readPackageManager(root) {
862
+ const packagePath = path3.join(root, "package.json");
863
+ if (!fs2.existsSync(packagePath)) return "";
864
+ try {
865
+ return String(JSON.parse(fs2.readFileSync(packagePath, "utf8"))?.packageManager || "").trim();
866
+ } catch {
867
+ return "";
868
+ }
869
+ }
870
+ function validatedPnpmVersionToken(root) {
871
+ const raw = readPackageManager(root);
872
+ const match = /^pnpm@(.+)$/iu.exec(raw);
873
+ if (!match) return "";
874
+ const version = match[1].trim();
875
+ if (!version || /\s/u.test(version) || !SAFE_PNPM_VERSION_RE.test(version)) return "";
876
+ return version;
877
+ }
878
+ function pnpmSelector(root) {
879
+ const packageManager = readPackageManager(root);
880
+ const version = validatedPnpmVersionToken(root);
881
+ if (packageManager && !version) {
882
+ throw new Error(`[vo-mcp runner] invalid pnpm packageManager token in ${path3.join(root, "package.json")}`);
883
+ }
884
+ return version ? `pnpm@${version}` : "pnpm";
885
+ }
886
+ function whereResults(result) {
887
+ if (result?.status !== 0) return [];
888
+ return String(result.stdout || "").split(/\r?\n/u).map((line) => line.trim()).filter((line) => path3.win32.isAbsolute(line));
889
+ }
890
+ async function resolveWindowsNativeCommand(command, runner) {
891
+ const result = await runner("where", [command], { timeoutMs: 1e4 });
892
+ return whereResults(result).find((candidate) => WINDOWS_NATIVE_EXECUTABLE_RE.test(candidate)) || "";
893
+ }
894
+ function trustedCorepackCandidates(options) {
895
+ const env2 = options.env || process.env;
896
+ const execPath = options.execPath || process.execPath;
897
+ const roots = [portableDirname(execPath)];
898
+ for (const key of ["ProgramW6432", "ProgramFiles", "ProgramFiles(x86)"]) {
899
+ const programFiles = String(env2[key] || "").trim();
900
+ if (isWindowsDriveOrUnc(programFiles) || path3.posix.isAbsolute(programFiles)) {
901
+ roots.push(portableJoin(programFiles, "nodejs"));
902
+ }
903
+ }
904
+ return [...new Set(roots)].map((root) => portableJoin(root, "node_modules", "corepack", "dist", "corepack.js"));
905
+ }
906
+ function resolveTrustedCorepackJs(options) {
907
+ const existsSync7 = options.existsSync || fs2.existsSync;
908
+ return trustedCorepackCandidates(options).find((candidate) => existsSync7(candidate)) || "";
909
+ }
910
+ async function resolvePnpmInstallCommand(root, options = {}) {
911
+ const runner = options.runner || runProcess;
912
+ const platform = options.platform || process.platform;
913
+ const selector = pnpmSelector(root);
914
+ if (platform !== "win32") {
915
+ if (await commandExists("pnpm", { runner, platform })) {
916
+ return {
917
+ command: "pnpm",
918
+ args: [...DEFAULT_PNPM_INSTALL_ARGS],
919
+ displayCommand: ["pnpm", ...DEFAULT_PNPM_INSTALL_ARGS]
920
+ };
921
+ }
922
+ if (await commandExists("corepack", { runner, platform })) {
923
+ return {
924
+ command: "corepack",
925
+ args: [selector, ...DEFAULT_PNPM_INSTALL_ARGS],
926
+ displayCommand: ["corepack", selector, ...DEFAULT_PNPM_INSTALL_ARGS]
927
+ };
928
+ }
929
+ throw new Error("[vo-mcp runner] pnpm hydration requires `pnpm` or `corepack` on PATH.");
930
+ }
931
+ const pnpmExecutable = await resolveWindowsNativeCommand("pnpm", runner);
932
+ if (pnpmExecutable) {
933
+ return {
934
+ command: pnpmExecutable,
935
+ args: [...DEFAULT_PNPM_INSTALL_ARGS],
936
+ displayCommand: ["pnpm", ...DEFAULT_PNPM_INSTALL_ARGS]
937
+ };
938
+ }
939
+ const corepackExecutable = await resolveWindowsNativeCommand("corepack", runner);
940
+ if (corepackExecutable) {
941
+ return {
942
+ command: corepackExecutable,
943
+ args: [selector, ...DEFAULT_PNPM_INSTALL_ARGS],
944
+ displayCommand: ["corepack", selector, ...DEFAULT_PNPM_INSTALL_ARGS]
945
+ };
946
+ }
947
+ const corepackJs = resolveTrustedCorepackJs(options);
948
+ if (corepackJs) {
949
+ return {
950
+ command: options.execPath || process.execPath,
951
+ args: [corepackJs, selector, ...DEFAULT_PNPM_INSTALL_ARGS],
952
+ displayCommand: ["corepack", selector, ...DEFAULT_PNPM_INSTALL_ARGS]
953
+ };
954
+ }
955
+ throw new Error(
956
+ "[vo-mcp runner] pnpm hydration on Windows requires a native pnpm/corepack executable or a trusted Corepack installation."
957
+ );
958
+ }
959
+ var DEFAULT_PNPM_INSTALL_ARGS, SAFE_PNPM_VERSION_RE, WINDOWS_NATIVE_EXECUTABLE_RE, WINDOWS_DRIVE_OR_UNC_RE;
960
+ var init_pnpm_command = __esm({
961
+ "src/runner/pnpm-command.mjs"() {
962
+ "use strict";
963
+ init_process_runner();
964
+ DEFAULT_PNPM_INSTALL_ARGS = Object.freeze([
965
+ "install",
966
+ "--frozen-lockfile",
967
+ "--prefer-offline",
968
+ "--ignore-scripts",
969
+ "--config.confirmModulesPurge=false"
970
+ ]);
971
+ SAFE_PNPM_VERSION_RE = /^[0-9A-Za-z._+-]+$/u;
972
+ WINDOWS_NATIVE_EXECUTABLE_RE = /\.(?:com|exe)$/iu;
973
+ WINDOWS_DRIVE_OR_UNC_RE = /^(?:[A-Za-z]:[\\/]|\\\\)/u;
974
+ }
975
+ });
976
+
844
977
  // src/runner/pnpm-materialize.mjs
845
978
  import fsp4 from "node:fs/promises";
846
- import path3 from "node:path";
979
+ import path4 from "node:path";
847
980
  function linkType() {
848
981
  return process.platform === "win32" ? "junction" : "dir";
849
982
  }
@@ -855,13 +988,13 @@ async function realpathOrThrow(target, fsApi) {
855
988
  }
856
989
  }
857
990
  function shouldMapToWorktree(resolvedTarget, canonicalRoot) {
858
- const relative = path3.relative(canonicalRoot, resolvedTarget).replace(/\\/g, "/");
991
+ const relative = path4.relative(canonicalRoot, resolvedTarget).replace(/\\/g, "/");
859
992
  return Boolean(relative && !relative.startsWith("..") && !relative.split("/").includes("node_modules"));
860
993
  }
861
994
  async function resolveEntryTarget(sourceEntry, canonicalRoot, worktreeRoot, fsApi) {
862
995
  const resolved = await realpathOrThrow(sourceEntry, fsApi);
863
996
  if (!shouldMapToWorktree(resolved, canonicalRoot)) return resolved;
864
- const mapped = path3.join(worktreeRoot, path3.relative(canonicalRoot, resolved));
997
+ const mapped = path4.join(worktreeRoot, path4.relative(canonicalRoot, resolved));
865
998
  if (!await fsApi.pathExists(mapped)) {
866
999
  throw new Error(`[vo-mcp runner] task-local workspace target is missing for dependency link: ${mapped}`);
867
1000
  }
@@ -872,7 +1005,7 @@ async function ensureLinkedDirectory(source, target, fsApi) {
872
1005
  if (await fsApi.realpath(target) === await fsApi.realpath(source)) return;
873
1006
  throw new Error(`[vo-mcp runner] refusing to overwrite existing dependency path: ${target}`);
874
1007
  }
875
- await fsApi.mkdir(path3.dirname(target), { recursive: true });
1008
+ await fsApi.mkdir(path4.dirname(target), { recursive: true });
876
1009
  await fsApi.symlink(source, target, linkType());
877
1010
  if (await fsApi.realpath(target) !== await fsApi.realpath(source)) {
878
1011
  throw new Error(`[vo-mcp runner] dependency link validation failed for ${target}`);
@@ -886,8 +1019,8 @@ async function maybeYield2(state) {
886
1019
  async function copyDirRecursive(sourceDir, targetDir, fsApi, yieldState) {
887
1020
  await fsApi.mkdir(targetDir, { recursive: true });
888
1021
  for (const entry of await fsApi.readdir(sourceDir, { withFileTypes: true })) {
889
- const source = path3.join(sourceDir, entry.name);
890
- const target = path3.join(targetDir, entry.name);
1022
+ const source = path4.join(sourceDir, entry.name);
1023
+ const target = path4.join(targetDir, entry.name);
891
1024
  await maybeYield2(yieldState);
892
1025
  if (entry.isDirectory()) {
893
1026
  await copyDirRecursive(source, target, fsApi, yieldState);
@@ -899,17 +1032,17 @@ async function copyDirRecursive(sourceDir, targetDir, fsApi, yieldState) {
899
1032
  async function materializeEntry(sourceEntry, targetEntry, canonicalRoot, worktreeRoot, options) {
900
1033
  await options.beforeEntry?.(sourceEntry, targetEntry);
901
1034
  const stat2 = await options.fsApi.lstat(sourceEntry);
902
- if (stat2.isDirectory() && !stat2.isSymbolicLink() && path3.basename(sourceEntry) === ".bin") {
1035
+ if (stat2.isDirectory() && !stat2.isSymbolicLink() && path4.basename(sourceEntry) === ".bin") {
903
1036
  await copyDirRecursive(sourceEntry, targetEntry, options.fsApi, options.yieldState);
904
1037
  return;
905
1038
  }
906
- if (stat2.isDirectory() && !stat2.isSymbolicLink() && path3.basename(sourceEntry).startsWith("@")) {
1039
+ if (stat2.isDirectory() && !stat2.isSymbolicLink() && path4.basename(sourceEntry).startsWith("@")) {
907
1040
  await options.fsApi.mkdir(targetEntry, { recursive: true });
908
1041
  for (const nested of await options.fsApi.readdir(sourceEntry, { withFileTypes: true })) {
909
1042
  await maybeYield2(options.yieldState);
910
1043
  await materializeEntry(
911
- path3.join(sourceEntry, nested.name),
912
- path3.join(targetEntry, nested.name),
1044
+ path4.join(sourceEntry, nested.name),
1045
+ path4.join(targetEntry, nested.name),
913
1046
  canonicalRoot,
914
1047
  worktreeRoot,
915
1048
  options
@@ -922,7 +1055,7 @@ async function materializeEntry(sourceEntry, targetEntry, canonicalRoot, worktre
922
1055
  await ensureLinkedDirectory(resolvedTarget, targetEntry, options.fsApi);
923
1056
  return;
924
1057
  }
925
- await options.fsApi.mkdir(path3.dirname(targetEntry), { recursive: true });
1058
+ await options.fsApi.mkdir(path4.dirname(targetEntry), { recursive: true });
926
1059
  await options.fsApi.copyFile(sourceEntry, targetEntry);
927
1060
  }
928
1061
  function createPnpmFsApi(pathExists6) {
@@ -946,8 +1079,8 @@ async function materializeNodeModulesForest(sourceNodeModules, targetNodeModules
946
1079
  for (const entry of await options.fsApi.readdir(sourceNodeModules, { withFileTypes: true })) {
947
1080
  await maybeYield2(options.yieldState);
948
1081
  await materializeEntry(
949
- path3.join(sourceNodeModules, entry.name),
950
- path3.join(targetNodeModules, entry.name),
1082
+ path4.join(sourceNodeModules, entry.name),
1083
+ path4.join(targetNodeModules, entry.name),
951
1084
  canonicalRoot,
952
1085
  worktreeRoot,
953
1086
  options
@@ -962,20 +1095,20 @@ var init_pnpm_materialize = __esm({
962
1095
 
963
1096
  // src/runner/pnpm-hydration.mjs
964
1097
  import { createHash } from "node:crypto";
965
- import fs2 from "node:fs";
1098
+ import fs3 from "node:fs";
966
1099
  import fsp5 from "node:fs/promises";
967
- import path4 from "node:path";
1100
+ import path5 from "node:path";
968
1101
  function hashText(text) {
969
1102
  return createHash("sha256").update(String(text)).digest("hex");
970
1103
  }
971
1104
  function statePath(root) {
972
- return path4.join(root, ".agent-worktrees", "runner-pnpm-hydration.json");
1105
+ return path5.join(root, ".agent-worktrees", "runner-pnpm-hydration.json");
973
1106
  }
974
1107
  function packageJson(root) {
975
- const packagePath = path4.join(root, "package.json");
976
- if (!fs2.existsSync(packagePath)) return null;
1108
+ const packagePath = path5.join(root, "package.json");
1109
+ if (!fs3.existsSync(packagePath)) return null;
977
1110
  try {
978
- return JSON.parse(fs2.readFileSync(packagePath, "utf8"));
1111
+ return JSON.parse(fs3.readFileSync(packagePath, "utf8"));
979
1112
  } catch {
980
1113
  return null;
981
1114
  }
@@ -989,23 +1122,23 @@ async function pathExists4(target) {
989
1122
  }
990
1123
  }
991
1124
  function lockfileHash(root) {
992
- const lockPath = path4.join(root, "pnpm-lock.yaml");
993
- if (!fs2.existsSync(lockPath)) return "";
994
- return hashText(fs2.readFileSync(lockPath, "utf8"));
1125
+ const lockPath = path5.join(root, "pnpm-lock.yaml");
1126
+ if (!fs3.existsSync(lockPath)) return "";
1127
+ return hashText(fs3.readFileSync(lockPath, "utf8"));
995
1128
  }
996
1129
  function readHydrationState(root) {
997
1130
  const file = statePath(root);
998
- if (!fs2.existsSync(file)) return null;
1131
+ if (!fs3.existsSync(file)) return null;
999
1132
  try {
1000
- return JSON.parse(fs2.readFileSync(file, "utf8"));
1133
+ return JSON.parse(fs3.readFileSync(file, "utf8"));
1001
1134
  } catch {
1002
1135
  return null;
1003
1136
  }
1004
1137
  }
1005
1138
  function writeHydrationState(root, state) {
1006
1139
  const file = statePath(root);
1007
- fs2.mkdirSync(path4.dirname(file), { recursive: true });
1008
- fs2.writeFileSync(file, `${JSON.stringify({
1140
+ fs3.mkdirSync(path5.dirname(file), { recursive: true });
1141
+ fs3.writeFileSync(file, `${JSON.stringify({
1009
1142
  ...state,
1010
1143
  stateVersion: 2,
1011
1144
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -1013,7 +1146,7 @@ function writeHydrationState(root, state) {
1013
1146
  `, "utf8");
1014
1147
  }
1015
1148
  function voDepsStatePath(root) {
1016
- return path4.join(root, "node_modules", ".vo-deps-state.json");
1149
+ return path5.join(root, "node_modules", ".vo-deps-state.json");
1017
1150
  }
1018
1151
  async function ensureVoDepsState(root, expectedHash, fsApi = fsp5) {
1019
1152
  const marker = voDepsStatePath(root);
@@ -1023,7 +1156,7 @@ async function ensureVoDepsState(root, expectedHash, fsApi = fsp5) {
1023
1156
  } catch {
1024
1157
  }
1025
1158
  const temp = `${marker}.tmp-${process.pid}-${Date.now()}`;
1026
- await fsApi.mkdir(path4.dirname(marker), { recursive: true });
1159
+ await fsApi.mkdir(path5.dirname(marker), { recursive: true });
1027
1160
  await fsApi.writeFile(temp, `${JSON.stringify({ lockfileHash: expectedHash, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
1028
1161
  `, "utf8");
1029
1162
  await fsApi.rename(temp, marker);
@@ -1043,14 +1176,6 @@ async function assertVoDepsState(root, expectedHash, fsApi = fsp5) {
1043
1176
  }
1044
1177
  throw new Error(`[vo-mcp runner] linked vo:deps state marker mismatch for ${root}`);
1045
1178
  }
1046
- function validatedPnpmVersionToken(root) {
1047
- const raw = String(packageJson(root)?.packageManager || "").trim();
1048
- const match = /^pnpm@(.+)$/iu.exec(raw);
1049
- if (!match) return "";
1050
- const version = match[1].trim();
1051
- if (!version || /\s/u.test(version) || !SAFE_PNPM_VERSION_RE.test(version)) return "";
1052
- return version;
1053
- }
1054
1179
  function workspacePatternsFromPackageJson(root) {
1055
1180
  const workspaces = packageJson(root)?.workspaces;
1056
1181
  if (Array.isArray(workspaces)) return workspaces.map(String);
@@ -1058,9 +1183,9 @@ function workspacePatternsFromPackageJson(root) {
1058
1183
  return [];
1059
1184
  }
1060
1185
  function workspacePatternsFromPnpmWorkspace(root) {
1061
- const workspacePath = path4.join(root, "pnpm-workspace.yaml");
1062
- if (!fs2.existsSync(workspacePath)) return [];
1063
- const lines = fs2.readFileSync(workspacePath, "utf8").split(/\r?\n/u);
1186
+ const workspacePath = path5.join(root, "pnpm-workspace.yaml");
1187
+ if (!fs3.existsSync(workspacePath)) return [];
1188
+ const lines = fs3.readFileSync(workspacePath, "utf8").split(/\r?\n/u);
1064
1189
  const patterns = [];
1065
1190
  let inPackages = false;
1066
1191
  for (const rawLine of lines) {
@@ -1117,16 +1242,16 @@ function collectPackageDirs(root, options = {}) {
1117
1242
  while (stack.length > 0) {
1118
1243
  const current = stack.pop();
1119
1244
  if (!current) continue;
1120
- const relativeDir = path4.relative(root, current.dir).replace(/\\/g, "/");
1121
- if (relativeDir && fs2.existsSync(path4.join(current.dir, "package.json"))) {
1245
+ const relativeDir = path5.relative(root, current.dir).replace(/\\/g, "/");
1246
+ if (relativeDir && fs3.existsSync(path5.join(current.dir, "package.json"))) {
1122
1247
  found.push(relativeDir);
1123
1248
  if (found.length >= maxDirs) break;
1124
1249
  }
1125
1250
  if (current.depth >= maxDepth) continue;
1126
- for (const entry of fs2.readdirSync(current.dir, { withFileTypes: true })) {
1251
+ for (const entry of fs3.readdirSync(current.dir, { withFileTypes: true })) {
1127
1252
  if (!entry.isDirectory()) continue;
1128
1253
  if (IGNORED_SCAN_DIRS.has(entry.name)) continue;
1129
- stack.push({ dir: path4.join(current.dir, entry.name), depth: current.depth + 1 });
1254
+ stack.push({ dir: path5.join(current.dir, entry.name), depth: current.depth + 1 });
1130
1255
  }
1131
1256
  }
1132
1257
  return found.sort();
@@ -1136,35 +1261,22 @@ function discoverWorkspacePackageDirs(root, options = {}) {
1136
1261
  if (patterns.length === 0) return [];
1137
1262
  return collectPackageDirs(root, options).filter((relativeDir) => patterns.some((pattern) => matchesWorkspacePattern(relativeDir, pattern)));
1138
1263
  }
1139
- async function resolveInstallCommand(root, runner) {
1140
- if (await commandExists("pnpm", { runner })) {
1141
- return { command: "pnpm", args: [...DEFAULT_INSTALL_ARGS] };
1142
- }
1143
- if (await commandExists("corepack", { runner })) {
1144
- const version = validatedPnpmVersionToken(root);
1145
- if (String(packageJson(root)?.packageManager || "").trim() && !version) {
1146
- throw new Error(`[vo-mcp runner] invalid pnpm packageManager token in ${path4.join(root, "package.json")}`);
1147
- }
1148
- return { command: "corepack", args: [version ? `pnpm@${version}` : "pnpm", ...DEFAULT_INSTALL_ARGS] };
1149
- }
1150
- throw new Error("[vo-mcp runner] pnpm hydration requires `pnpm` or `corepack` on PATH.");
1151
- }
1152
1264
  async function runInstall(root, options = {}) {
1153
1265
  const runner = options.runner || runProcess;
1154
- const tuple = await resolveInstallCommand(root, runner);
1266
+ const tuple = await resolvePnpmInstallCommand(root, { ...options, runner });
1155
1267
  const result = await runner(tuple.command, tuple.args, {
1156
1268
  cwd: root,
1157
1269
  timeoutMs: options.timeoutMs ?? DEFAULT_INSTALL_TIMEOUT_MS
1158
1270
  });
1159
1271
  if (result.status !== 0) {
1160
1272
  throw new Error(
1161
- `[vo-mcp runner] pnpm hydration failed for ${root}: ${summarizeProcessFailure(result)} (command: ${[tuple.command, ...tuple.args].join(" ")})`
1273
+ `[vo-mcp runner] pnpm hydration failed for ${root}: ${summarizeProcessFailure(result)} (command: ${tuple.displayCommand.join(" ")})`
1162
1274
  );
1163
1275
  }
1164
1276
  }
1165
1277
  async function hasReadyNodeModules(root) {
1166
- const nodeModules = path4.join(root, "node_modules");
1167
- return await pathExists4(path4.join(nodeModules, ".modules.yaml")) || await pathExists4(path4.join(nodeModules, ".pnpm"));
1278
+ const nodeModules = path5.join(root, "node_modules");
1279
+ return await pathExists4(path5.join(nodeModules, ".modules.yaml")) || await pathExists4(path5.join(nodeModules, ".pnpm"));
1168
1280
  }
1169
1281
  function hydrationFsApi(overrides = {}) {
1170
1282
  return { ...createPnpmFsApi(pathExists4), ...overrides };
@@ -1201,7 +1313,7 @@ async function ensurePnpmHydration(root, options = {}) {
1201
1313
  expectedHash: hash,
1202
1314
  requireMarker: false
1203
1315
  });
1204
- if (!health.healthy && await fsApi.pathExists(path4.join(root, "node_modules"))) {
1316
+ if (!health.healthy && await fsApi.pathExists(path5.join(root, "node_modules"))) {
1205
1317
  quarantine = await quarantineCanonicalNodeModules(root, health.issues, {
1206
1318
  fsApi,
1207
1319
  logger: options.logger
@@ -1213,7 +1325,7 @@ async function ensurePnpmHydration(root, options = {}) {
1213
1325
  }
1214
1326
  const linkedWorkspaceDirs = [];
1215
1327
  for (const relativeDir of workspaceDirs) {
1216
- if (await pathExists4(path4.join(root, relativeDir, "node_modules"))) {
1328
+ if (await pathExists4(path5.join(root, relativeDir, "node_modules"))) {
1217
1329
  linkedWorkspaceDirs.push(relativeDir);
1218
1330
  }
1219
1331
  }
@@ -1249,18 +1361,18 @@ async function linkHydratedNodeModules({ root, worktreeDir, hydration, options =
1249
1361
  yieldEvery: Math.max(1, options.yieldEvery ?? DEFAULT_YIELD_EVERY2)
1250
1362
  }
1251
1363
  };
1252
- recordOwnedNodeModulesRoot(dependencyOwnership, path4.join(worktreeDir, "node_modules"));
1364
+ recordOwnedNodeModulesRoot(dependencyOwnership, path5.join(worktreeDir, "node_modules"));
1253
1365
  await materializeNodeModulesForest(
1254
- path4.join(root, "node_modules"),
1255
- path4.join(worktreeDir, "node_modules"),
1366
+ path5.join(root, "node_modules"),
1367
+ path5.join(worktreeDir, "node_modules"),
1256
1368
  root,
1257
1369
  worktreeDir,
1258
1370
  materializeOptions
1259
1371
  );
1260
1372
  for (const relativeDir of hydration.linkedWorkspaceDirs) {
1261
- const sourceNodeModules = path4.join(root, relativeDir, "node_modules");
1262
- const targetNodeModules = path4.join(worktreeDir, relativeDir, "node_modules");
1263
- if (!await fsApi.pathExists(path4.join(worktreeDir, relativeDir))) continue;
1373
+ const sourceNodeModules = path5.join(root, relativeDir, "node_modules");
1374
+ const targetNodeModules = path5.join(worktreeDir, relativeDir, "node_modules");
1375
+ if (!await fsApi.pathExists(path5.join(worktreeDir, relativeDir))) continue;
1264
1376
  recordOwnedNodeModulesRoot(dependencyOwnership, targetNodeModules);
1265
1377
  await materializeNodeModulesForest(sourceNodeModules, targetNodeModules, root, worktreeDir, materializeOptions);
1266
1378
  }
@@ -1271,22 +1383,16 @@ async function linkHydratedNodeModules({ root, worktreeDir, hydration, options =
1271
1383
  workspaceCount: hydration.linkedWorkspaceDirs.length
1272
1384
  };
1273
1385
  }
1274
- var DEFAULT_INSTALL_TIMEOUT_MS, DEFAULT_INSTALL_ARGS, DEFAULT_YIELD_EVERY2, IGNORED_SCAN_DIRS, SAFE_PNPM_VERSION_RE;
1386
+ var DEFAULT_INSTALL_TIMEOUT_MS, DEFAULT_YIELD_EVERY2, IGNORED_SCAN_DIRS;
1275
1387
  var init_pnpm_hydration = __esm({
1276
1388
  "src/runner/pnpm-hydration.mjs"() {
1277
1389
  "use strict";
1278
1390
  init_pnpm_canonical_health();
1391
+ init_pnpm_command();
1279
1392
  init_pnpm_materialize();
1280
1393
  init_process_runner();
1281
1394
  init_pnpm_link_detach();
1282
1395
  DEFAULT_INSTALL_TIMEOUT_MS = 20 * 60 * 1e3;
1283
- DEFAULT_INSTALL_ARGS = Object.freeze([
1284
- "install",
1285
- "--frozen-lockfile",
1286
- "--prefer-offline",
1287
- "--ignore-scripts",
1288
- "--config.confirmModulesPurge=false"
1289
- ]);
1290
1396
  DEFAULT_YIELD_EVERY2 = 25;
1291
1397
  IGNORED_SCAN_DIRS = /* @__PURE__ */ new Set([
1292
1398
  ".agent-worktrees",
@@ -1299,34 +1405,33 @@ var init_pnpm_hydration = __esm({
1299
1405
  "dist",
1300
1406
  "node_modules"
1301
1407
  ]);
1302
- SAFE_PNPM_VERSION_RE = /^[0-9A-Za-z._+-]+$/u;
1303
1408
  }
1304
1409
  });
1305
1410
 
1306
1411
  // src/runner/worktree-paths.mjs
1307
1412
  import { createHash as createHash2 } from "node:crypto";
1308
- import path5 from "node:path";
1413
+ import path6 from "node:path";
1309
1414
  function samePath(left, right) {
1310
- const a = path5.resolve(left);
1311
- const b = path5.resolve(right);
1415
+ const a = path6.resolve(left);
1416
+ const b = path6.resolve(right);
1312
1417
  return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
1313
1418
  }
1314
1419
  function worktreePoolForRoot(root, { clonesRootDir = process.env.VO_CODE_RUNNER_CLONES_ROOT || "" } = {}) {
1315
- const canonicalRoot = path5.resolve(root);
1420
+ const canonicalRoot = path6.resolve(root);
1316
1421
  if (clonesRootDir) {
1317
- const clonePool = path5.resolve(clonesRootDir);
1318
- if (samePath(path5.dirname(canonicalRoot), clonePool)) {
1319
- return path5.join(clonePool, ".agent-worktrees", path5.basename(canonicalRoot));
1422
+ const clonePool = path6.resolve(clonesRootDir);
1423
+ if (samePath(path6.dirname(canonicalRoot), clonePool)) {
1424
+ return path6.join(clonePool, ".agent-worktrees", path6.basename(canonicalRoot));
1320
1425
  }
1321
1426
  }
1322
- return path5.join(canonicalRoot, ".agent-worktrees");
1427
+ return path6.join(canonicalRoot, ".agent-worktrees");
1323
1428
  }
1324
1429
  function worktreeDirForName(root, worktreeName, options = {}) {
1325
1430
  const leaf = createHash2("sha256").update(String(worktreeName)).digest("hex").slice(0, 16);
1326
- return path5.join(worktreePoolForRoot(root, options), leaf);
1431
+ return path6.join(worktreePoolForRoot(root, options), leaf);
1327
1432
  }
1328
1433
  function recoveryLedgerPathForRoot(root, options = {}) {
1329
- return path5.join(worktreePoolForRoot(root, options), "recovery-ledger.jsonl");
1434
+ return path6.join(worktreePoolForRoot(root, options), "recovery-ledger.jsonl");
1330
1435
  }
1331
1436
  var init_worktree_paths = __esm({
1332
1437
  "src/runner/worktree-paths.mjs"() {
@@ -1335,9 +1440,9 @@ var init_worktree_paths = __esm({
1335
1440
  });
1336
1441
 
1337
1442
  // src/runner/worktree-cleanup.mjs
1338
- import fs3 from "node:fs";
1443
+ import fs4 from "node:fs";
1339
1444
  import fsp6 from "node:fs/promises";
1340
- import path6 from "node:path";
1445
+ import path7 from "node:path";
1341
1446
  function stateFromEntry(entry) {
1342
1447
  return {
1343
1448
  root: entry.root,
@@ -1378,14 +1483,14 @@ function pruneSuccessfulStates(nowMs = Date.now()) {
1378
1483
  function assertTrackedCleanupPath(entry) {
1379
1484
  const poolRoot = worktreePoolForRoot(entry.root);
1380
1485
  const expected = worktreeDirForName(entry.root, entry.worktreeName);
1381
- const resolvedPool = path6.resolve(poolRoot);
1382
- const resolvedTarget = path6.resolve(entry.worktreeDir);
1383
- if (!resolvedTarget.startsWith(`${resolvedPool}${path6.sep}`)) {
1486
+ const resolvedPool = path7.resolve(poolRoot);
1487
+ const resolvedTarget = path7.resolve(entry.worktreeDir);
1488
+ if (!resolvedTarget.startsWith(`${resolvedPool}${path7.sep}`)) {
1384
1489
  const error = new Error(`cleanup refused outside managed pool: ${entry.worktreeDir}`);
1385
1490
  error.cleanupFatal = true;
1386
1491
  throw error;
1387
1492
  }
1388
- if (resolvedTarget !== path6.resolve(expected)) {
1493
+ if (resolvedTarget !== path7.resolve(expected)) {
1389
1494
  const error = new Error(`cleanup refused for unexpected tracked path: ${entry.worktreeDir}`);
1390
1495
  error.cleanupFatal = true;
1391
1496
  throw error;
@@ -1399,8 +1504,8 @@ async function worktreeStillRegistered2(root, worktreeDir, gitRunner) {
1399
1504
  if (result.status !== 0) {
1400
1505
  throw new Error(`git worktree list --porcelain failed during cleanup verification: ${summarizeProcessFailure(result)}`);
1401
1506
  }
1402
- const registered = String(result.stdout || "").split(/\r?\n/u).filter((line) => line.startsWith("worktree ")).map((line) => path6.resolve(line.slice("worktree ".length).trim()));
1403
- return registered.includes(path6.resolve(worktreeDir));
1507
+ const registered = String(result.stdout || "").split(/\r?\n/u).filter((line) => line.startsWith("worktree ")).map((line) => path7.resolve(line.slice("worktree ".length).trim()));
1508
+ return registered.includes(path7.resolve(worktreeDir));
1404
1509
  }
1405
1510
  function cleanupBackoff(attempt) {
1406
1511
  return 250 * attempt;
@@ -1419,7 +1524,7 @@ async function runCleanupCycle(entry, options) {
1419
1524
  await (options.assertDetachedDependencyLinks || assertDetachedDependencyLinks)(entry.dependencyOwnership);
1420
1525
  }
1421
1526
  const gitRunner = options.gitRunner || runProcess;
1422
- const pathExists6 = options.pathExists || ((target) => fs3.existsSync(target));
1527
+ const pathExists6 = options.pathExists || ((target) => fs4.existsSync(target));
1423
1528
  const removeResult = await gitRunner("git", ["worktree", "remove", "--force", entry.worktreeDir], {
1424
1529
  cwd: entry.root,
1425
1530
  timeoutMs: 12e4
@@ -1510,7 +1615,7 @@ function scheduleTrackedCleanup(entry, options = {}) {
1510
1615
  }
1511
1616
  function pendingCleanupDirs() {
1512
1617
  return new Set(
1513
- [...CLEANUP_STATES.values()].filter((state) => state.status === "pending").map((state) => path6.resolve(state.worktreeDir))
1618
+ [...CLEANUP_STATES.values()].filter((state) => state.status === "pending").map((state) => path7.resolve(state.worktreeDir))
1514
1619
  );
1515
1620
  }
1516
1621
  var CLEANUP_STATES, CLEANUP_PROMISES, SUCCESS_HISTORY_LIMIT, SUCCESS_HISTORY_TTL_MS, DEFAULT_CLEANUP_ATTEMPTS;
@@ -1529,15 +1634,15 @@ var init_worktree_cleanup = __esm({
1529
1634
  });
1530
1635
 
1531
1636
  // src/runner/task-root-prepare.mjs
1532
- import fs4 from "node:fs";
1637
+ import fs5 from "node:fs";
1533
1638
  import fsp7 from "node:fs/promises";
1534
- import path7 from "node:path";
1639
+ import path8 from "node:path";
1535
1640
  function prepLockDir(root) {
1536
- return path7.join(root, ".agent-worktrees", "runner-root-prep.lock");
1641
+ return path8.join(root, ".agent-worktrees", "runner-root-prep.lock");
1537
1642
  }
1538
1643
  function readLockMeta(lockDir) {
1539
1644
  try {
1540
- return JSON.parse(fs4.readFileSync(path7.join(lockDir, "owner.json"), "utf8"));
1645
+ return JSON.parse(fs5.readFileSync(path8.join(lockDir, "owner.json"), "utf8"));
1541
1646
  } catch {
1542
1647
  return null;
1543
1648
  }
@@ -1557,13 +1662,13 @@ async function acquirePrepLock(root, options = {}) {
1557
1662
  const staleMs = options.lockStaleMs ?? PREP_LOCK_STALE_MS;
1558
1663
  const sleep3 = options.sleep || sleepMs;
1559
1664
  const lockDir = prepLockDir(root);
1560
- const ownerPath = path7.join(lockDir, "owner.json");
1665
+ const ownerPath = path8.join(lockDir, "owner.json");
1561
1666
  const deadline = nowMs() + waitMs;
1562
- fs4.mkdirSync(path7.dirname(lockDir), { recursive: true });
1667
+ fs5.mkdirSync(path8.dirname(lockDir), { recursive: true });
1563
1668
  for (; ; ) {
1564
1669
  try {
1565
- fs4.mkdirSync(lockDir);
1566
- fs4.writeFileSync(ownerPath, `${JSON.stringify({
1670
+ fs5.mkdirSync(lockDir);
1671
+ fs5.writeFileSync(ownerPath, `${JSON.stringify({
1567
1672
  pid: process.pid,
1568
1673
  createdAt: new Date(nowMs()).toISOString(),
1569
1674
  root
@@ -1609,16 +1714,16 @@ async function gitText(root, args, options = {}) {
1609
1714
  }
1610
1715
  function canonicalRecoveryDir(root, options = {}) {
1611
1716
  const now = options.now || (() => /* @__PURE__ */ new Date());
1612
- return path7.join(
1613
- options.managedPool || path7.join(root, ".agent-worktrees"),
1717
+ return path8.join(
1718
+ options.managedPool || path8.join(root, ".agent-worktrees"),
1614
1719
  ".canonical-recovery",
1615
1720
  `preexisting-${now().toISOString().replace(/[:.]/gu, "-")}`
1616
1721
  );
1617
1722
  }
1618
1723
  function canonicalPath(root, relative) {
1619
- const resolvedRoot = path7.resolve(root);
1620
- const target = path7.resolve(root, relative);
1621
- const prefix = `${resolvedRoot}${path7.sep}`;
1724
+ const resolvedRoot = path8.resolve(root);
1725
+ const target = path8.resolve(root, relative);
1726
+ const prefix = `${resolvedRoot}${path8.sep}`;
1622
1727
  if (!target.startsWith(prefix)) {
1623
1728
  throw new Error(`canonical recovery path escaped the runner clone: ${relative}`);
1624
1729
  }
@@ -1646,7 +1751,7 @@ async function recoverManagedCanonicalResidue(root, options = {}) {
1646
1751
  if (patchResult.status !== 0) {
1647
1752
  throw new Error(`could not preserve canonical tracked changes: ${summarizeProcessFailure(patchResult)}`);
1648
1753
  }
1649
- await fsp7.writeFile(path7.join(quarantineDir, "tracked.patch"), String(patchResult.stdout || ""), "utf8");
1754
+ await fsp7.writeFile(path8.join(quarantineDir, "tracked.patch"), String(patchResult.stdout || ""), "utf8");
1650
1755
  const symlinks = [];
1651
1756
  for (const relative of paths.untracked) {
1652
1757
  const source = canonicalPath(root, relative);
@@ -1658,13 +1763,13 @@ async function recoverManagedCanonicalResidue(root, options = {}) {
1658
1763
  if (!stat2.isFile()) {
1659
1764
  throw new Error(`canonical recovery refuses unsupported untracked entry: ${relative}`);
1660
1765
  }
1661
- const target = canonicalPath(path7.join(quarantineDir, "untracked"), relative);
1662
- await fsp7.mkdir(path7.dirname(target), { recursive: true });
1766
+ const target = canonicalPath(path8.join(quarantineDir, "untracked"), relative);
1767
+ await fsp7.mkdir(path8.dirname(target), { recursive: true });
1663
1768
  await fsp7.copyFile(source, target);
1664
1769
  }
1665
- await fsp7.writeFile(path7.join(quarantineDir, "manifest.json"), `${JSON.stringify({
1770
+ await fsp7.writeFile(path8.join(quarantineDir, "manifest.json"), `${JSON.stringify({
1666
1771
  recoveredAt: (/* @__PURE__ */ new Date()).toISOString(),
1667
- canonicalRoot: path7.resolve(root),
1772
+ canonicalRoot: path8.resolve(root),
1668
1773
  canonicalHead: headSha,
1669
1774
  tracked: paths.tracked,
1670
1775
  untracked: paths.untracked,
@@ -1736,19 +1841,19 @@ async function registeredWorktreeDirs(root, options = {}) {
1736
1841
  throw new Error(`git worktree list --porcelain failed while checking managed residue: ${summarizeProcessFailure(listed)}`);
1737
1842
  }
1738
1843
  return new Set(
1739
- String(listed.stdout || "").split(/\r?\n/u).filter((line) => line.startsWith("worktree ")).map((line) => path7.resolve(line.slice("worktree ".length).trim()))
1844
+ String(listed.stdout || "").split(/\r?\n/u).filter((line) => line.startsWith("worktree ")).map((line) => path8.resolve(line.slice("worktree ".length).trim()))
1740
1845
  );
1741
1846
  }
1742
1847
  async function reportLegacyResiduals(root, options = {}) {
1743
- const managedRoot = path7.join(root, ".agent-worktrees");
1744
- if (!fs4.existsSync(managedRoot)) return [];
1848
+ const managedRoot = path8.join(root, ".agent-worktrees");
1849
+ if (!fs5.existsSync(managedRoot)) return [];
1745
1850
  const registered = await registeredWorktreeDirs(root, options);
1746
1851
  const pending = pendingCleanupDirs();
1747
1852
  const found = [];
1748
- for (const entry of fs4.readdirSync(managedRoot, { withFileTypes: true })) {
1853
+ for (const entry of fs5.readdirSync(managedRoot, { withFileTypes: true })) {
1749
1854
  if (!entry.isDirectory()) continue;
1750
1855
  if (shouldIgnoreManagedEntry(entry.name)) continue;
1751
- const absolute = path7.resolve(path7.join(managedRoot, entry.name));
1856
+ const absolute = path8.resolve(path8.join(managedRoot, entry.name));
1752
1857
  if (registered.has(absolute)) continue;
1753
1858
  if (pending.has(absolute)) continue;
1754
1859
  found.push(absolute);
@@ -1781,6 +1886,7 @@ var init_task_root_prepare = __esm({
1781
1886
  "src/runner/task-root-prepare.mjs"() {
1782
1887
  "use strict";
1783
1888
  init_pnpm_hydration();
1889
+ init_pnpm_command();
1784
1890
  init_process_runner();
1785
1891
  init_worktree_cleanup();
1786
1892
  PREP_LOCK_WAIT_MS = 20 * 60 * 1e3;
@@ -1816,7 +1922,7 @@ var init_worktree_github_auth = __esm({
1816
1922
 
1817
1923
  // src/runner/worktree-recovery-start.mjs
1818
1924
  import fsp8 from "node:fs/promises";
1819
- import path8 from "node:path";
1925
+ import path9 from "node:path";
1820
1926
  async function recordWorktreeStarted(worktreeTarget, meta = {}) {
1821
1927
  if (!worktreeTarget?.worktreeName || !meta.taskId) return null;
1822
1928
  const entry = {
@@ -1831,7 +1937,7 @@ async function recordWorktreeStarted(worktreeTarget, meta = {}) {
1831
1937
  reason: "worktree allocated before execution"
1832
1938
  };
1833
1939
  const ledger = recoveryLedgerPathForRoot(worktreeTarget.root || process.cwd());
1834
- await fsp8.mkdir(path8.dirname(ledger), { recursive: true });
1940
+ await fsp8.mkdir(path9.dirname(ledger), { recursive: true });
1835
1941
  await fsp8.appendFile(ledger, `${JSON.stringify(entry)}
1836
1942
  `, "utf8");
1837
1943
  return { entry, ledger };
@@ -1844,9 +1950,9 @@ var init_worktree_recovery_start = __esm({
1844
1950
  });
1845
1951
 
1846
1952
  // src/runner/worktree-helper.mjs
1847
- import fs5 from "node:fs";
1953
+ import fs6 from "node:fs";
1848
1954
  import fsp9 from "node:fs/promises";
1849
- import path9 from "node:path";
1955
+ import path10 from "node:path";
1850
1956
  function repoRoot() {
1851
1957
  return process.env.VO_CODE_RUNNER_REPO || process.cwd();
1852
1958
  }
@@ -1862,7 +1968,7 @@ function cloneDirForSlug(repoSlug, clonesRootDir) {
1862
1968
  const [owner, name] = String(repoSlug).split("/");
1863
1969
  if (owner === "." || owner === ".." || name === "." || name === "..") return null;
1864
1970
  if (owner.startsWith("-") || name.startsWith("-")) return null;
1865
- return path9.join(clonesRootDir, `${sanitize(owner, "owner")}__${sanitize(name, "repo")}`);
1971
+ return path10.join(clonesRootDir, `${sanitize(owner, "owner")}__${sanitize(name, "repo")}`);
1866
1972
  }
1867
1973
  function cloneLockDir(dir) {
1868
1974
  return `${dir}.clone-lock`;
@@ -1877,7 +1983,7 @@ async function pathExists5(target) {
1877
1983
  }
1878
1984
  async function readLockMeta2(lockDir) {
1879
1985
  try {
1880
- return JSON.parse(await fsp9.readFile(path9.join(lockDir, "owner.json"), "utf8"));
1986
+ return JSON.parse(await fsp9.readFile(path10.join(lockDir, "owner.json"), "utf8"));
1881
1987
  } catch {
1882
1988
  return null;
1883
1989
  }
@@ -1898,11 +2004,11 @@ async function acquireCloneLock(dir, options = {}) {
1898
2004
  const sleep3 = options.sleep || sleepMs;
1899
2005
  const lockDir = cloneLockDir(dir);
1900
2006
  const deadline = nowMs() + waitMs;
1901
- await fsp9.mkdir(path9.dirname(lockDir), { recursive: true });
2007
+ await fsp9.mkdir(path10.dirname(lockDir), { recursive: true });
1902
2008
  for (; ; ) {
1903
2009
  try {
1904
2010
  await fsp9.mkdir(lockDir);
1905
- await fsp9.writeFile(path9.join(lockDir, "owner.json"), `${JSON.stringify({
2011
+ await fsp9.writeFile(path10.join(lockDir, "owner.json"), `${JSON.stringify({
1906
2012
  pid: process.pid,
1907
2013
  createdAt: new Date(nowMs()).toISOString(),
1908
2014
  dir
@@ -1931,7 +2037,7 @@ async function acquireCloneLock(dir, options = {}) {
1931
2037
  }
1932
2038
  }
1933
2039
  async function isUsableGitClone(dir, runner = runProcess) {
1934
- if (!await pathExists5(path9.join(dir, ".git"))) return false;
2040
+ if (!await pathExists5(path10.join(dir, ".git"))) return false;
1935
2041
  const result = await runner("git", ["-C", dir, "rev-parse", "HEAD"], { timeoutMs: 1e4 });
1936
2042
  return result.status === 0 && Boolean(String(result.stdout || "").trim());
1937
2043
  }
@@ -1952,7 +2058,7 @@ async function ensureUsableClone(repoSlug, dir, options = {}) {
1952
2058
  const maxAttempts = options.maxAttempts || 5;
1953
2059
  const raceWaitMs = options.raceWaitMs ?? 1e4;
1954
2060
  let lastError = null;
1955
- await fsp9.mkdir(path9.dirname(dir), { recursive: true });
2061
+ await fsp9.mkdir(path10.dirname(dir), { recursive: true });
1956
2062
  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
1957
2063
  if (await pathExists5(dir)) {
1958
2064
  if (await waitForUsableClone(dir, runner, sleep3, raceWaitMs)) return dir;
@@ -1964,7 +2070,7 @@ async function ensureUsableClone(repoSlug, dir, options = {}) {
1964
2070
  ["clone", "--no-tags", `https://github.com/${owner}/${name}.git`, tmpDir],
1965
2071
  { timeoutMs: 6e5, env: githubGitAuthEnv(options.githubToken) }
1966
2072
  );
1967
- if (clone.status !== 0 || !await pathExists5(path9.join(tmpDir, ".git"))) {
2073
+ if (clone.status !== 0 || !await pathExists5(path10.join(tmpDir, ".git"))) {
1968
2074
  await fsp9.rm(tmpDir, { recursive: true, force: true });
1969
2075
  lastError = new Error(`[vo-mcp runner] clone failed for ${repoSlug}: ${describeGitFailure(clone)}`);
1970
2076
  continue;
@@ -1991,7 +2097,7 @@ async function ensureUsableClone(repoSlug, dir, options = {}) {
1991
2097
  }
1992
2098
  async function resolveTaskRoot(repoSlug, options = {}) {
1993
2099
  const root = clonesRoot();
1994
- if (root && !path9.isAbsolute(root)) {
2100
+ if (root && !path10.isAbsolute(root)) {
1995
2101
  throw new Error(`[vo-mcp runner] VO_CODE_RUNNER_CLONES_ROOT must be an absolute path (got '${root}')`);
1996
2102
  }
1997
2103
  const dir = cloneDirForSlug(repoSlug, root);
@@ -2040,7 +2146,7 @@ async function createFixWorktree(kind, error = {}, options = {}) {
2040
2146
  const dependencyOwnership = createDependencyOwnershipTracker(worktreeDir);
2041
2147
  const prep = await prepare(root, {
2042
2148
  recoverDirtyCanonical: multiRepo,
2043
- managedPool: path9.dirname(worktreeDir)
2149
+ managedPool: path10.dirname(worktreeDir)
2044
2150
  });
2045
2151
  await processRunner("git", ["config", "core.longpaths", "true"], { cwd: root, timeoutMs: 3e4 });
2046
2152
  const add = await addWorktree({ root, branchName, worktreeDir });
@@ -2106,8 +2212,8 @@ function preserveFailedWorktree(worktreeName, meta = {}) {
2106
2212
  };
2107
2213
  try {
2108
2214
  const ledger = recoveryLedgerPathForRoot(root);
2109
- fs5.mkdirSync(path9.dirname(ledger), { recursive: true });
2110
- fs5.appendFileSync(ledger, `${JSON.stringify(entry)}
2215
+ fs6.mkdirSync(path10.dirname(ledger), { recursive: true });
2216
+ fs6.appendFileSync(ledger, `${JSON.stringify(entry)}
2111
2217
  `, "utf8");
2112
2218
  console.error(`[vo-mcp runner] Preserved worktree ${worktreeName} (reason: ${entry.reason})`);
2113
2219
  } catch (error) {
@@ -2152,9 +2258,9 @@ function partitionRecoveryLedger(lines, { nowMs, ttlMs = PRESERVED_WORKTREE_TTL_
2152
2258
  return decisions;
2153
2259
  }
2154
2260
  function isManagedPreservedDir(dir, root = repoRoot()) {
2155
- const normalized = path9.resolve(String(dir || ""));
2156
- const pool = path9.resolve(worktreePoolForRoot(root));
2157
- return normalized.startsWith(pool + path9.sep);
2261
+ const normalized = path10.resolve(String(dir || ""));
2262
+ const pool = path10.resolve(worktreePoolForRoot(root));
2263
+ return normalized.startsWith(pool + path10.sep);
2158
2264
  }
2159
2265
  function mergeAppendedSinceRead(originalRaw, currentRaw, keptLines) {
2160
2266
  const originalSet = new Set(String(originalRaw || "").split(/\r?\n/u).map((l) => l.trim()).filter(Boolean));
@@ -2173,15 +2279,15 @@ function writeLedgerAtomic(ledger, lines, logger) {
2173
2279
  const payload = lines.length ? `${lines.join("\n")}
2174
2280
  ` : "";
2175
2281
  const tmp = `${ledger}.tmp-${process.pid}-${Date.now()}`;
2176
- fs5.writeFileSync(tmp, payload, "utf8");
2282
+ fs6.writeFileSync(tmp, payload, "utf8");
2177
2283
  for (let attempt = 1; ; attempt += 1) {
2178
2284
  try {
2179
- fs5.renameSync(tmp, ledger);
2285
+ fs6.renameSync(tmp, ledger);
2180
2286
  return true;
2181
2287
  } catch (error) {
2182
2288
  if (attempt >= 3) {
2183
2289
  try {
2184
- fs5.rmSync(tmp, { force: true });
2290
+ fs6.rmSync(tmp, { force: true });
2185
2291
  } catch {
2186
2292
  }
2187
2293
  logger(`[vo-mcp runner] GC could not compact recovery ledger: ${String(error?.message || error)}`);
@@ -2203,14 +2309,14 @@ async function sweepPreservedWorktrees({
2203
2309
  const ledger = recoveryLedgerPathForRoot(root);
2204
2310
  let raw;
2205
2311
  try {
2206
- raw = fs5.readFileSync(ledger, "utf8");
2312
+ raw = fs6.readFileSync(ledger, "utf8");
2207
2313
  } catch {
2208
2314
  return { removed: 0, compacted: 0 };
2209
2315
  }
2210
2316
  const decisions = partitionRecoveryLedger(raw.split(/\r?\n/u), {
2211
2317
  nowMs,
2212
2318
  ttlMs,
2213
- dirExists: (dir) => fs5.existsSync(dir)
2319
+ dirExists: (dir) => fs6.existsSync(dir)
2214
2320
  });
2215
2321
  let removed = 0;
2216
2322
  let compacted = 0;
@@ -2250,7 +2356,7 @@ async function sweepPreservedWorktrees({
2250
2356
  if (compacted > 0) {
2251
2357
  let currentRaw = raw;
2252
2358
  try {
2253
- currentRaw = fs5.readFileSync(ledger, "utf8");
2359
+ currentRaw = fs6.readFileSync(ledger, "utf8");
2254
2360
  } catch {
2255
2361
  }
2256
2362
  writeLedgerAtomic(ledger, mergeAppendedSinceRead(raw, currentRaw, output), logger);
@@ -2508,11 +2614,11 @@ function createControlPlaneClient({
2508
2614
  const resolvedBaseUrl = baseUrl ?? env2.VO_CONTROL_PLANE_URL ?? "";
2509
2615
  if (!resolvedBaseUrl) throw new Error("VO_CONTROL_PLANE_URL is required for the code-runner daemon");
2510
2616
  const root = resolvedBaseUrl.replace(/\/+$/, "");
2511
- async function req(method, path19, body, { timeoutMs } = {}) {
2617
+ async function req(method, path20, body, { timeoutMs } = {}) {
2512
2618
  const bearer = await resolveBearer(env2);
2513
2619
  const controller = timeoutMs ? new AbortController() : null;
2514
2620
  let timeoutId;
2515
- const request = Promise.resolve(fetchImpl(`${root}${path19}`, {
2621
+ const request = Promise.resolve(fetchImpl(`${root}${path20}`, {
2516
2622
  method,
2517
2623
  headers: {
2518
2624
  "content-type": "application/json",
@@ -2525,7 +2631,7 @@ function createControlPlaneClient({
2525
2631
  const timeout = new Promise((_, reject) => {
2526
2632
  timeoutId = setTimeout(() => {
2527
2633
  controller.abort();
2528
- reject(new Error(`control-plane ${path19} timed out after ${timeoutMs}ms`));
2634
+ reject(new Error(`control-plane ${path20} timed out after ${timeoutMs}ms`));
2529
2635
  }, timeoutMs);
2530
2636
  });
2531
2637
  try {
@@ -2534,7 +2640,7 @@ function createControlPlaneClient({
2534
2640
  clearTimeout(timeoutId);
2535
2641
  }
2536
2642
  }
2537
- const taskReq = (method, path19, body, options = {}) => req(method, path19, body, { timeoutMs: taskRequestTimeoutMs, ...options });
2643
+ const taskReq = (method, path20, body, options = {}) => req(method, path20, body, { timeoutMs: taskRequestTimeoutMs, ...options });
2538
2644
  return {
2539
2645
  ...makeAutonomousDispatchAdmissionClient(
2540
2646
  req,
@@ -2657,8 +2763,8 @@ function createControlPlaneClient({
2657
2763
  return listAllPrOpenedTasks(taskReq);
2658
2764
  },
2659
2765
  async downloadTaskAttachment(taskId, attachmentId) {
2660
- const path19 = `/api/v1/code-task/${encodeURIComponent(taskId)}/attachment/${encodeURIComponent(attachmentId)}`;
2661
- const res = await taskReq("GET", path19);
2766
+ const path20 = `/api/v1/code-task/${encodeURIComponent(taskId)}/attachment/${encodeURIComponent(attachmentId)}`;
2767
+ const res = await taskReq("GET", path20);
2662
2768
  if (res.status === 401) cachedFirebaseToken = null;
2663
2769
  if (!res.ok) throw new Error(`attachment download failed: HTTP ${res.status}`);
2664
2770
  return Buffer.from(await res.arrayBuffer());
@@ -2852,8 +2958,8 @@ var init_control_plane_client = __esm({
2852
2958
  });
2853
2959
 
2854
2960
  // ../../scripts/virtual-office/code-runner/windows-claude-launch.mjs
2855
- import { existsSync as existsSync2, realpathSync } from "node:fs";
2856
- import { win32 as path10 } from "node:path";
2961
+ import { existsSync as existsSync3, realpathSync } from "node:fs";
2962
+ import { win32 as path11 } from "node:path";
2857
2963
  import { spawnSync } from "node:child_process";
2858
2964
  function pathValue(env2) {
2859
2965
  for (const key of ["Path", "PATH", "path"]) {
@@ -2874,38 +2980,38 @@ function envValue(env2, name) {
2874
2980
  function userClaudeCandidates(bin, env2) {
2875
2981
  if (!/^claude(?:\.(?:exe|cmd|ps1))?$/iu.test(bin)) return [];
2876
2982
  const userProfile = envValue(env2, "USERPROFILE");
2877
- const appData = envValue(env2, "APPDATA") || (userProfile ? path10.join(userProfile, "AppData", "Roaming") : "");
2878
- const localAppData = envValue(env2, "LOCALAPPDATA") || (userProfile ? path10.join(userProfile, "AppData", "Local") : "");
2983
+ const appData = envValue(env2, "APPDATA") || (userProfile ? path11.join(userProfile, "AppData", "Roaming") : "");
2984
+ const localAppData = envValue(env2, "LOCALAPPDATA") || (userProfile ? path11.join(userProfile, "AppData", "Local") : "");
2879
2985
  const candidates = [];
2880
2986
  if (appData) {
2881
- const npmBin = path10.join(appData, "npm");
2987
+ const npmBin = path11.join(appData, "npm");
2882
2988
  candidates.push(
2883
- path10.join(npmBin, "claude.exe"),
2884
- path10.join(npmBin, "claude.cmd"),
2885
- path10.join(npmBin, "claude.ps1"),
2886
- path10.join(npmBin, "claude"),
2887
- path10.join(npmBin, ...NATIVE_CLAUDE_PARTS)
2989
+ path11.join(npmBin, "claude.exe"),
2990
+ path11.join(npmBin, "claude.cmd"),
2991
+ path11.join(npmBin, "claude.ps1"),
2992
+ path11.join(npmBin, "claude"),
2993
+ path11.join(npmBin, ...NATIVE_CLAUDE_PARTS)
2888
2994
  );
2889
2995
  }
2890
- if (userProfile) candidates.push(path10.join(userProfile, ".local", "bin", "claude.exe"));
2996
+ if (userProfile) candidates.push(path11.join(userProfile, ".local", "bin", "claude.exe"));
2891
2997
  if (localAppData) {
2892
2998
  candidates.push(
2893
- path10.join(localAppData, "Microsoft", "WinGet", "Links", "claude.exe"),
2894
- path10.join(localAppData, "Microsoft", "WindowsApps", "claude.exe")
2999
+ path11.join(localAppData, "Microsoft", "WinGet", "Links", "claude.exe"),
3000
+ path11.join(localAppData, "Microsoft", "WindowsApps", "claude.exe")
2895
3001
  );
2896
3002
  }
2897
3003
  return candidates;
2898
3004
  }
2899
3005
  function pathCandidates(bin, env2) {
2900
- if (path10.isAbsolute(bin) || /[\\/]/u.test(bin)) {
2901
- return [path10.resolve(bin)];
2902
- }
2903
- const extension = path10.extname(bin);
2904
- const fromPath = pathValue(env2).split(";").map(cleanPathSegment).filter(Boolean).flatMap((directory) => extension ? [path10.join(directory, bin)] : [
2905
- path10.join(directory, `${bin}.exe`),
2906
- path10.join(directory, `${bin}.cmd`),
2907
- path10.join(directory, `${bin}.ps1`),
2908
- path10.join(directory, bin)
3006
+ if (path11.isAbsolute(bin) || /[\\/]/u.test(bin)) {
3007
+ return [path11.resolve(bin)];
3008
+ }
3009
+ const extension = path11.extname(bin);
3010
+ const fromPath = pathValue(env2).split(";").map(cleanPathSegment).filter(Boolean).flatMap((directory) => extension ? [path11.join(directory, bin)] : [
3011
+ path11.join(directory, `${bin}.exe`),
3012
+ path11.join(directory, `${bin}.cmd`),
3013
+ path11.join(directory, `${bin}.ps1`),
3014
+ path11.join(directory, bin)
2909
3015
  ]);
2910
3016
  const seen = /* @__PURE__ */ new Set();
2911
3017
  return [...fromPath, ...userClaudeCandidates(bin, env2)].filter((candidate) => {
@@ -2926,7 +3032,7 @@ function canonicalExistingPath(candidate, exists, canonicalize) {
2926
3032
  function resolveWindowsClaudeExecutable({
2927
3033
  bin = "claude",
2928
3034
  env: env2 = process.env,
2929
- exists = existsSync2,
3035
+ exists = existsSync3,
2930
3036
  canonicalize = realpathSync
2931
3037
  } = {}) {
2932
3038
  const requested = String(bin || "").trim();
@@ -2936,8 +3042,8 @@ function resolveWindowsClaudeExecutable({
2936
3042
  for (const candidate of pathCandidates(requested, env2)) {
2937
3043
  const found = canonicalExistingPath(candidate, exists, canonicalize);
2938
3044
  if (!found) continue;
2939
- if (path10.extname(found).toLowerCase() === ".exe") return found;
2940
- const native = path10.join(path10.dirname(found), ...NATIVE_CLAUDE_PARTS);
3045
+ if (path11.extname(found).toLowerCase() === ".exe") return found;
3046
+ const native = path11.join(path11.dirname(found), ...NATIVE_CLAUDE_PARTS);
2941
3047
  const resolvedNative = canonicalExistingPath(native, exists, canonicalize);
2942
3048
  if (resolvedNative) return resolvedNative;
2943
3049
  }
@@ -3404,14 +3510,14 @@ var init_terminal_process_cleanup = __esm({
3404
3510
 
3405
3511
  // ../../scripts/virtual-office/code-runner/orphan-agent-reaper.mjs
3406
3512
  import { spawnSync as spawnSync5 } from "node:child_process";
3407
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
3513
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
3408
3514
  import os from "node:os";
3409
- import path11 from "node:path";
3515
+ import path12 from "node:path";
3410
3516
  function registryRoot(tmp = os.tmpdir()) {
3411
- return path11.join(tmp, REGISTRY_ROOT_NAME);
3517
+ return path12.join(tmp, REGISTRY_ROOT_NAME);
3412
3518
  }
3413
3519
  function instanceDir(root, instanceId) {
3414
- return path11.join(root, String(instanceId).replace(/[^A-Za-z0-9_-]/g, ""));
3520
+ return path12.join(root, String(instanceId).replace(/[^A-Za-z0-9_-]/g, ""));
3415
3521
  }
3416
3522
  function registerDaemonInstance({
3417
3523
  root = registryRoot(),
@@ -3421,8 +3527,8 @@ function registerDaemonInstance({
3421
3527
  } = {}) {
3422
3528
  if (!instanceId) return null;
3423
3529
  const dir = instanceDir(root, instanceId);
3424
- mkdirSync2(dir, { recursive: true });
3425
- const file = path11.join(dir, DAEMON_RECORD);
3530
+ mkdirSync3(dir, { recursive: true });
3531
+ const file = path12.join(dir, DAEMON_RECORD);
3426
3532
  writeFileSync2(file, JSON.stringify({ daemonPid, daemonStartedAtMs, instanceId }), {
3427
3533
  encoding: "utf8",
3428
3534
  mode: 384
@@ -3439,9 +3545,9 @@ function recordAgentPid({
3439
3545
  if (!instanceId || !Number.isInteger(pid) || pid <= 0) return false;
3440
3546
  try {
3441
3547
  const dir = instanceDir(root, instanceId);
3442
- mkdirSync2(dir, { recursive: true });
3548
+ mkdirSync3(dir, { recursive: true });
3443
3549
  writeFileSync2(
3444
- path11.join(dir, `${pid}.json`),
3550
+ path12.join(dir, `${pid}.json`),
3445
3551
  JSON.stringify({ pid, agentId, startedAtMs, instanceId }),
3446
3552
  { encoding: "utf8", mode: 384 }
3447
3553
  );
@@ -3457,7 +3563,7 @@ function unrecordAgentPid({
3457
3563
  } = {}) {
3458
3564
  if (!instanceId || !Number.isInteger(pid)) return false;
3459
3565
  try {
3460
- rmSync2(path11.join(instanceDir(root, instanceId), `${pid}.json`), { force: true });
3566
+ rmSync2(path12.join(instanceDir(root, instanceId), `${pid}.json`), { force: true });
3461
3567
  return true;
3462
3568
  } catch {
3463
3569
  return false;
@@ -3475,7 +3581,7 @@ function bootstrapOrphanReaper({ instanceId, log: log2 = () => {
3475
3581
  }
3476
3582
  function readRegistry({ root = registryRoot(), currentInstanceId } = {}) {
3477
3583
  const instances = [];
3478
- if (!existsSync3(root)) return instances;
3584
+ if (!existsSync4(root)) return instances;
3479
3585
  let dirents;
3480
3586
  try {
3481
3587
  dirents = readdirSync(root, { withFileTypes: true });
@@ -3485,7 +3591,7 @@ function readRegistry({ root = registryRoot(), currentInstanceId } = {}) {
3485
3591
  for (const dirent of dirents) {
3486
3592
  if (!dirent.isDirectory()) continue;
3487
3593
  if (currentInstanceId && dirent.name === instanceDirName(currentInstanceId)) continue;
3488
- const dir = path11.join(root, dirent.name);
3594
+ const dir = path12.join(root, dirent.name);
3489
3595
  let daemon = null;
3490
3596
  const agents = [];
3491
3597
  let files;
@@ -3497,7 +3603,7 @@ function readRegistry({ root = registryRoot(), currentInstanceId } = {}) {
3497
3603
  for (const name of files) {
3498
3604
  let parsed;
3499
3605
  try {
3500
- parsed = JSON.parse(readFileSync2(path11.join(dir, name), "utf8"));
3606
+ parsed = JSON.parse(readFileSync2(path12.join(dir, name), "utf8"));
3501
3607
  } catch {
3502
3608
  continue;
3503
3609
  }
@@ -3545,7 +3651,7 @@ function windowsSystemRoot(env2 = process.env) {
3545
3651
  return env2.SystemRoot || env2.WINDIR || "C:\\Windows";
3546
3652
  }
3547
3653
  function windowsPowershellExe(env2 = process.env) {
3548
- return path11.join(windowsSystemRoot(env2), "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
3654
+ return path12.join(windowsSystemRoot(env2), "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
3549
3655
  }
3550
3656
  function listProcessCreationTimes({ platform = process.platform, spawn: spawn5 = spawnSync5, env: env2 = process.env, warn = console.warn } = {}) {
3551
3657
  const map = /* @__PURE__ */ new Map();
@@ -3590,7 +3696,7 @@ function parsePosixPsLine(line) {
3590
3696
  function killProcessTree(pid, { platform = process.platform, spawn: spawn5 = spawnSync5, env: env2 = process.env } = {}) {
3591
3697
  if (!Number.isInteger(pid) || pid <= 0) return false;
3592
3698
  if (platform === "win32") {
3593
- const taskkill = path11.join(windowsSystemRoot(env2), "System32", "taskkill.exe");
3699
+ const taskkill = path12.join(windowsSystemRoot(env2), "System32", "taskkill.exe");
3594
3700
  const r = spawn5(taskkill, ["/PID", String(pid), "/T", "/F"], { windowsHide: true, stdio: "ignore", timeout: 15e3 });
3595
3701
  return !r.error && r.status === 0;
3596
3702
  }
@@ -4379,15 +4485,15 @@ var init_flat_token_usage = __esm({
4379
4485
 
4380
4486
  // ../../scripts/virtual-office/code-runner/codex-runner.mjs
4381
4487
  import { spawnSync as spawnSync6 } from "node:child_process";
4382
- import { existsSync as existsSync4 } from "node:fs";
4383
- import { win32 } from "node:path";
4488
+ import { existsSync as existsSync5 } from "node:fs";
4489
+ import { win32 as win322 } from "node:path";
4384
4490
  function isTruthyFlag2(value) {
4385
4491
  return ["1", "true", "yes", "on"].includes(String(value ?? "").trim().toLowerCase());
4386
4492
  }
4387
4493
  function resolveCodexBinary({
4388
4494
  env: env2 = process.env,
4389
4495
  platform = process.platform,
4390
- exists = existsSync4
4496
+ exists = existsSync5
4391
4497
  } = {}) {
4392
4498
  if (platform !== "win32") return "codex";
4393
4499
  const appData = String(env2.APPDATA || "").trim();
@@ -4395,7 +4501,7 @@ function resolveCodexBinary({
4395
4501
  const localAppData = String(env2.LOCALAPPDATA || "").trim();
4396
4502
  const candidates = [];
4397
4503
  if (appData) {
4398
- candidates.push(win32.join(
4504
+ candidates.push(win322.join(
4399
4505
  appData,
4400
4506
  "npm",
4401
4507
  "node_modules",
@@ -4411,11 +4517,11 @@ function resolveCodexBinary({
4411
4517
  ));
4412
4518
  }
4413
4519
  if (userProfile) {
4414
- candidates.push(win32.join(userProfile, ".local", "bin", "codex.exe"));
4415
- candidates.push(win32.join(userProfile, ".codex", "bin", "codex.exe"));
4520
+ candidates.push(win322.join(userProfile, ".local", "bin", "codex.exe"));
4521
+ candidates.push(win322.join(userProfile, ".codex", "bin", "codex.exe"));
4416
4522
  }
4417
4523
  if (localAppData) {
4418
- candidates.push(win32.join(localAppData, "Microsoft", "WindowsApps", "codex.exe"));
4524
+ candidates.push(win322.join(localAppData, "Microsoft", "WindowsApps", "codex.exe"));
4419
4525
  }
4420
4526
  const absolute = candidates.find((candidate) => exists(candidate));
4421
4527
  if (absolute) return absolute;
@@ -5244,9 +5350,9 @@ var init_rate_limit_detector_core = __esm({
5244
5350
 
5245
5351
  // ../../scripts/virtual-office/code-runner/rate-limit-resume-state.mjs
5246
5352
  import fsp10 from "node:fs/promises";
5247
- import path12 from "node:path";
5353
+ import path13 from "node:path";
5248
5354
  async function atomicWrite(file, content) {
5249
- await fsp10.mkdir(path12.dirname(file), { recursive: true });
5355
+ await fsp10.mkdir(path13.dirname(file), { recursive: true });
5250
5356
  const temp = `${file}.tmp-${process.pid}-${Date.now()}`;
5251
5357
  const handle = await fsp10.open(temp, "wx");
5252
5358
  try {
@@ -5307,7 +5413,7 @@ function writeResumeAttempts(file, store) {
5307
5413
  }
5308
5414
  async function acquireLock(lockFile, { now = Date.now, sleep: sleep3 = delay } = {}) {
5309
5415
  const deadline = now() + LOCK_WAIT_MS;
5310
- await fsp10.mkdir(path12.dirname(lockFile), { recursive: true });
5416
+ await fsp10.mkdir(path13.dirname(lockFile), { recursive: true });
5311
5417
  for (; ; ) {
5312
5418
  let handle;
5313
5419
  try {
@@ -5381,10 +5487,10 @@ var init_rate_limit_resume_state = __esm({
5381
5487
  });
5382
5488
 
5383
5489
  // ../../scripts/virtual-office/code-runner/rate-limit-resume.mjs
5384
- import { homedir as homedir2 } from "node:os";
5490
+ import { homedir as homedir3 } from "node:os";
5385
5491
  import { join as join2 } from "node:path";
5386
5492
  function resumeQueuePath() {
5387
- return join2(homedir2(), ".claude", "resume-queue.jsonl");
5493
+ return join2(homedir3(), ".claude", "resume-queue.jsonl");
5388
5494
  }
5389
5495
  function buildResumeEntry({ task = {}, resumeAfter = null, summary = "", at } = {}) {
5390
5496
  return {
@@ -5585,7 +5691,7 @@ var init_auto_merge = __esm({
5585
5691
 
5586
5692
  // ../../scripts/virtual-office/code-runner/pr-overlap-gate.mjs
5587
5693
  import { spawnSync as spawnSync8 } from "node:child_process";
5588
- import { existsSync as existsSync5 } from "node:fs";
5694
+ import { existsSync as existsSync6 } from "node:fs";
5589
5695
  import { fileURLToPath } from "node:url";
5590
5696
  function stripCredentials(env2 = process.env) {
5591
5697
  const safe = { ...env2 };
@@ -5596,7 +5702,7 @@ function resolveOverlapScript({
5596
5702
  worktreeDir,
5597
5703
  trustedPath = null,
5598
5704
  trustedPaths = TRUSTED_OVERLAP_CANDIDATES,
5599
- existsFn = existsSync5,
5705
+ existsFn = existsSync6,
5600
5706
  joinFn = (dir) => `${dir}/scripts/ci/check-local-pr-overlap.mjs`
5601
5707
  } = {}) {
5602
5708
  const candidates = trustedPath ? [trustedPath] : trustedPaths;
@@ -5665,14 +5771,14 @@ function parsePorcelainZ(out) {
5665
5771
  for (let i = 0; i < tokens.length; i += 1) {
5666
5772
  const token2 = tokens[i];
5667
5773
  if (!token2) continue;
5668
- const path19 = token2.slice(3);
5669
- if (path19) files.push(path19);
5774
+ const path20 = token2.slice(3);
5775
+ if (path20) files.push(path20);
5670
5776
  if (token2[0] === "R" || token2[0] === "C") i += 1;
5671
5777
  }
5672
5778
  return files;
5673
5779
  }
5674
- function isAgentScratch(path19) {
5675
- const normalized = String(path19 || "");
5780
+ function isAgentScratch(path20) {
5781
+ const normalized = String(path20 || "");
5676
5782
  return SCRATCH_PATTERNS.some((pattern) => pattern.test(normalized));
5677
5783
  }
5678
5784
  var SCRATCH_PATTERNS;
@@ -6505,10 +6611,10 @@ var init_task_prompt = __esm({
6505
6611
  });
6506
6612
 
6507
6613
  // ../../scripts/virtual-office/code-runner/task-attachments.mjs
6508
- import { createHash as createHash3, randomUUID } from "node:crypto";
6614
+ import { createHash as createHash3, randomUUID as randomUUID2 } from "node:crypto";
6509
6615
  import { chmod, mkdir, mkdtemp, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
6510
6616
  import os2 from "node:os";
6511
- import path13 from "node:path";
6617
+ import path14 from "node:path";
6512
6618
  function safeTaskToken(taskId) {
6513
6619
  return String(taskId || "task").replace(/[^0-9A-Za-z_-]/gu, "_").slice(0, 48) || "task";
6514
6620
  }
@@ -6518,25 +6624,25 @@ function sanitizeTaskAttachmentName(name, index = 0) {
6518
6624
  return `${String(index + 1).padStart(2, "0")}-${normalized}`;
6519
6625
  }
6520
6626
  function assertGeneratedDirectory(directory, tempRoot) {
6521
- const resolvedDirectory = path13.resolve(directory);
6522
- const resolvedRoot = path13.resolve(tempRoot);
6523
- if (path13.dirname(resolvedDirectory) !== resolvedRoot || !path13.basename(resolvedDirectory).startsWith(DIRECTORY_PREFIX)) {
6627
+ const resolvedDirectory = path14.resolve(directory);
6628
+ const resolvedRoot = path14.resolve(tempRoot);
6629
+ if (path14.dirname(resolvedDirectory) !== resolvedRoot || !path14.basename(resolvedDirectory).startsWith(DIRECTORY_PREFIX)) {
6524
6630
  throw new Error("refusing to clean an unverified task-attachment directory");
6525
6631
  }
6526
6632
  return resolvedDirectory;
6527
6633
  }
6528
6634
  async function createAttachmentDirectory(taskId, tempRoot) {
6529
- const root = path13.resolve(tempRoot);
6635
+ const root = path14.resolve(tempRoot);
6530
6636
  await mkdir(root, { recursive: true });
6531
- const directory = await mkdtemp(path13.join(root, `${DIRECTORY_PREFIX}${safeTaskToken(taskId)}-`));
6532
- const marker = JSON.stringify({ owner: MARKER_OWNER, token: randomUUID(), directory: path13.basename(directory), created_at: (/* @__PURE__ */ new Date()).toISOString() });
6533
- await writeFile(path13.join(directory, MARKER_FILE), marker, { encoding: "utf8", mode: 384 });
6637
+ const directory = await mkdtemp(path14.join(root, `${DIRECTORY_PREFIX}${safeTaskToken(taskId)}-`));
6638
+ const marker = JSON.stringify({ owner: MARKER_OWNER, token: randomUUID2(), directory: path14.basename(directory), created_at: (/* @__PURE__ */ new Date()).toISOString() });
6639
+ await writeFile(path14.join(directory, MARKER_FILE), marker, { encoding: "utf8", mode: 384 });
6534
6640
  return { directory, marker, tempRoot: root };
6535
6641
  }
6536
6642
  async function cleanupGeneratedDirectory(state) {
6537
6643
  if (!state || state.cleaned) return;
6538
6644
  const directory = assertGeneratedDirectory(state.directory, state.tempRoot);
6539
- const marker = await readFile(path13.join(directory, MARKER_FILE), "utf8").catch(() => "");
6645
+ const marker = await readFile(path14.join(directory, MARKER_FILE), "utf8").catch(() => "");
6540
6646
  if (marker !== state.marker) throw new Error("refusing to clean a task-attachment directory without its exact marker");
6541
6647
  await rm(directory, { recursive: true, force: true });
6542
6648
  state.cleaned = true;
@@ -6555,7 +6661,7 @@ async function sweepStaleTaskAttachmentDirectories({
6555
6661
  now = Date.now(),
6556
6662
  maxAgeMs = DEFAULT_STALE_AGE_MS
6557
6663
  } = {}) {
6558
- const root = path13.resolve(tempRoot);
6664
+ const root = path14.resolve(tempRoot);
6559
6665
  if (!Number.isFinite(maxAgeMs) || maxAgeMs <= 0) throw new Error("stale attachment age must be positive");
6560
6666
  const entries = await readdir(root, { withFileTypes: true }).catch((error) => {
6561
6667
  if (error?.code === "ENOENT") return [];
@@ -6564,8 +6670,8 @@ async function sweepStaleTaskAttachmentDirectories({
6564
6670
  let removed = 0;
6565
6671
  for (const entry of entries) {
6566
6672
  if (!entry.isDirectory() || !entry.name.startsWith(DIRECTORY_PREFIX)) continue;
6567
- const directory = assertGeneratedDirectory(path13.join(root, entry.name), root);
6568
- const markerRaw = await readFile(path13.join(directory, MARKER_FILE), "utf8").catch(() => "");
6673
+ const directory = assertGeneratedDirectory(path14.join(root, entry.name), root);
6674
+ const markerRaw = await readFile(path14.join(directory, MARKER_FILE), "utf8").catch(() => "");
6569
6675
  const marker = parseOwnedMarker(markerRaw, entry.name);
6570
6676
  if (!marker) continue;
6571
6677
  const directoryStat = await stat(directory);
@@ -6608,10 +6714,10 @@ async function materializeTaskAttachments(client, task, { tempRoot = os2.tmpdir(
6608
6714
  const sha256 = createHash3("sha256").update(content).digest("hex");
6609
6715
  if (sha256 !== ref.sha256) throw new Error(`attachment ${ref.attachment_id} sha256 mismatch`);
6610
6716
  const name = sanitizeTaskAttachmentName(ref.name, index);
6611
- const filePath = path13.join(state.directory, name);
6717
+ const filePath = path14.join(state.directory, name);
6612
6718
  await writeFile(filePath, content, { flag: "wx", mode: 384 });
6613
6719
  await chmod(filePath, 384);
6614
- files.push({ attachmentId: ref.attachment_id, name, mime: ref.mime, sizeBytes: ref.size_bytes, sha256, path: path13.resolve(filePath) });
6720
+ files.push({ attachmentId: ref.attachment_id, name, mime: ref.mime, sizeBytes: ref.size_bytes, sha256, path: path14.resolve(filePath) });
6615
6721
  }
6616
6722
  return { directory: state.directory, files, manifestMarkdown: buildManifest(files), cleanup: () => cleanupGeneratedDirectory(state) };
6617
6723
  } catch (error) {
@@ -6633,7 +6739,7 @@ var init_task_attachments = __esm({
6633
6739
  });
6634
6740
 
6635
6741
  // ../../scripts/virtual-office/code-runner/session-spool-forwarder.mjs
6636
- import { homedir as homedir3 } from "node:os";
6742
+ import { homedir as homedir4 } from "node:os";
6637
6743
  import { join as join4 } from "node:path";
6638
6744
  import { readdir as readdir2, readFile as readFile2, unlink, writeFile as writeFile2 } from "node:fs/promises";
6639
6745
  import { createHash as createHash4 } from "node:crypto";
@@ -6675,9 +6781,9 @@ async function readSpool(spoolDir = SPOOL_DIR) {
6675
6781
  }
6676
6782
  return out;
6677
6783
  }
6678
- async function readCloudMap(path19) {
6784
+ async function readCloudMap(path20) {
6679
6785
  try {
6680
- return JSON.parse(await readFile2(path19, "utf8"));
6786
+ return JSON.parse(await readFile2(path20, "utf8"));
6681
6787
  } catch {
6682
6788
  return {};
6683
6789
  }
@@ -6750,8 +6856,8 @@ var SPOOL_DIR, CLOUD_MAP_FILE, STALE_MS, ACTIVE_SILENCE_MS;
6750
6856
  var init_session_spool_forwarder = __esm({
6751
6857
  "../../scripts/virtual-office/code-runner/session-spool-forwarder.mjs"() {
6752
6858
  "use strict";
6753
- SPOOL_DIR = join4(homedir3(), ".vo", "session-spool");
6754
- CLOUD_MAP_FILE = join4(homedir3(), ".vo", "session-cloud-map.json");
6859
+ SPOOL_DIR = join4(homedir4(), ".vo", "session-spool");
6860
+ CLOUD_MAP_FILE = join4(homedir4(), ".vo", "session-cloud-map.json");
6755
6861
  STALE_MS = 60 * 60 * 1e3;
6756
6862
  ACTIVE_SILENCE_MS = 10 * 60 * 1e3;
6757
6863
  }
@@ -7434,7 +7540,7 @@ var init_local_model_remote_config = __esm({
7434
7540
 
7435
7541
  // ../../scripts/virtual-office/code-runner/account-usage/shared.mjs
7436
7542
  import crypto from "node:crypto";
7437
- import fs6 from "node:fs";
7543
+ import fs7 from "node:fs";
7438
7544
  function accountKey(agent, rawId) {
7439
7545
  const id = typeof rawId === "string" ? rawId.trim() : "";
7440
7546
  if (!id) return null;
@@ -7498,7 +7604,7 @@ var init_shared = __esm({
7498
7604
  };
7499
7605
  readJson = (p) => {
7500
7606
  try {
7501
- return JSON.parse(fs6.readFileSync(p, "utf8"));
7607
+ return JSON.parse(fs7.readFileSync(p, "utf8"));
7502
7608
  } catch {
7503
7609
  return null;
7504
7610
  }
@@ -7508,9 +7614,9 @@ var init_shared = __esm({
7508
7614
  });
7509
7615
 
7510
7616
  // ../../scripts/virtual-office/code-runner/account-usage/claude.mjs
7511
- import fs7 from "node:fs";
7617
+ import fs8 from "node:fs";
7512
7618
  import os3 from "node:os";
7513
- import path14 from "node:path";
7619
+ import path15 from "node:path";
7514
7620
  function fileCaptureTime(filePath, explicit, statFn) {
7515
7621
  if (typeof explicit === "string" && explicit) return explicit;
7516
7622
  try {
@@ -7524,7 +7630,7 @@ function usageBaseUrl(env2 = process.env) {
7524
7630
  return String(raw).replace(/\/+$/, "");
7525
7631
  }
7526
7632
  function readOAuthToken({ homeDir = os3.homedir(), read = readJson, now = Date.now() } = {}) {
7527
- const creds = read(path14.join(homeDir, ".claude", ".credentials.json"));
7633
+ const creds = read(path15.join(homeDir, ".claude", ".credentials.json"));
7528
7634
  const oauth = creds && typeof creds === "object" ? creds.claudeAiOauth : null;
7529
7635
  if (!oauth || typeof oauth !== "object") return null;
7530
7636
  const token2 = typeof oauth.accessToken === "string" ? oauth.accessToken.trim() : "";
@@ -7534,7 +7640,7 @@ function readOAuthToken({ homeDir = os3.homedir(), read = readJson, now = Date.n
7534
7640
  return token2;
7535
7641
  }
7536
7642
  function readAccountId({ homeDir = os3.homedir(), read = readJson } = {}) {
7537
- const cfg = read(path14.join(homeDir, ".claude.json"));
7643
+ const cfg = read(path15.join(homeDir, ".claude.json"));
7538
7644
  const account = cfg && typeof cfg === "object" ? cfg.oauthAccount : null;
7539
7645
  return account && typeof account.accountUuid === "string" ? account.accountUuid : null;
7540
7646
  }
@@ -7627,7 +7733,7 @@ async function readClaudeOAuthUsage({
7627
7733
  function readClaudeFileUsage({
7628
7734
  homeDir = os3.homedir(),
7629
7735
  read: rawRead = readJson,
7630
- statFn = fs7.statSync,
7736
+ statFn = fs8.statSync,
7631
7737
  now = () => Date.now()
7632
7738
  } = {}) {
7633
7739
  const read = (p) => {
@@ -7644,7 +7750,7 @@ function readClaudeFileUsage({
7644
7750
  if (age === null || age > MAX_FILE_AGE_MS) return null;
7645
7751
  return row;
7646
7752
  };
7647
- const statusPath = path14.join(homeDir, ".claude", "claude-usage.json");
7753
+ const statusPath = path15.join(homeDir, ".claude", "claude-usage.json");
7648
7754
  const status = read(statusPath);
7649
7755
  if (status && (status.seven_day || status.five_hour)) {
7650
7756
  const row = fresh(makeUsageRow({
@@ -7659,7 +7765,7 @@ function readClaudeFileUsage({
7659
7765
  }));
7660
7766
  if (row) return row;
7661
7767
  }
7662
- const weeklyPath = path14.join(homeDir, ".claude", "claude-weekly-usage.json");
7768
+ const weeklyPath = path15.join(homeDir, ".claude", "claude-weekly-usage.json");
7663
7769
  const weekly = read(weeklyPath);
7664
7770
  if (weekly) {
7665
7771
  const row = fresh(makeUsageRow({
@@ -8098,7 +8204,7 @@ var init_watcher_coordination = __esm({
8098
8204
  });
8099
8205
 
8100
8206
  // ../../scripts/virtual-office/code-runner/watcher-state.mjs
8101
- import { randomUUID as randomUUID2 } from "node:crypto";
8207
+ import { randomUUID as randomUUID3 } from "node:crypto";
8102
8208
  import { mkdir as mkdir2, open, readFile as readFile3, rename, unlink as unlink2 } from "node:fs/promises";
8103
8209
  import { dirname as dirname4 } from "node:path";
8104
8210
  async function readWatcherState(stateFile) {
@@ -8118,7 +8224,7 @@ async function readWatcherState(stateFile) {
8118
8224
  async function writeWatcherState(stateFile, state) {
8119
8225
  const directory = dirname4(stateFile);
8120
8226
  await mkdir2(directory, { recursive: true });
8121
- const temp = `${stateFile}.${process.pid}.${randomUUID2()}.tmp`;
8227
+ const temp = `${stateFile}.${process.pid}.${randomUUID3()}.tmp`;
8122
8228
  let handle;
8123
8229
  try {
8124
8230
  handle = await open(temp, "wx");
@@ -8329,7 +8435,7 @@ var init_pr_watcher_github = __esm({
8329
8435
  });
8330
8436
 
8331
8437
  // ../../scripts/virtual-office/code-runner/enqueue-autonomous-code-task.mjs
8332
- import { randomUUID as randomUUID3 } from "node:crypto";
8438
+ import { randomUUID as randomUUID4 } from "node:crypto";
8333
8439
  async function enqueueAutonomousCodeTask(client, task, log2 = () => {
8334
8440
  }) {
8335
8441
  const requestedBudgetUsd = task?.max_budget_usd;
@@ -8343,7 +8449,7 @@ async function enqueueAutonomousCodeTask(client, task, log2 = () => {
8343
8449
  if (typeof client?.reserveAutonomousDispatchBudget !== "function" || typeof client?.releaseAutonomousDispatchBudget !== "function") {
8344
8450
  throw new Error("autonomous dispatch admission client unavailable");
8345
8451
  }
8346
- const reservationId = randomUUID3();
8452
+ const reservationId = randomUUID4();
8347
8453
  const admission = await client.reserveAutonomousDispatchBudget({
8348
8454
  requestedBudgetUsd,
8349
8455
  reservationId,
@@ -8361,7 +8467,7 @@ var init_enqueue_autonomous_code_task = __esm({
8361
8467
  });
8362
8468
 
8363
8469
  // ../../scripts/virtual-office/code-runner/pr-watcher.mjs
8364
- import { homedir as homedir4 } from "node:os";
8470
+ import { homedir as homedir5 } from "node:os";
8365
8471
  import { join as join6 } from "node:path";
8366
8472
  function parsePrCiStatus(view) {
8367
8473
  const state = (view && typeof view.state === "string" ? view.state : "UNKNOWN").toUpperCase();
@@ -8705,7 +8811,7 @@ var init_pr_watcher = __esm({
8705
8811
  init_watcher_state();
8706
8812
  init_superseded_pr_source();
8707
8813
  init_ci_fix_prompt();
8708
- DEFAULT_STATE_FILE = join6(homedir4(), ".vo", "dispatched-prs.json");
8814
+ DEFAULT_STATE_FILE = join6(homedir5(), ".vo", "dispatched-prs.json");
8709
8815
  FAIL_CONCLUSIONS = /* @__PURE__ */ new Set([
8710
8816
  "FAILURE",
8711
8817
  "TIMED_OUT",
@@ -8935,9 +9041,9 @@ function buildControlHandler({ getStatus, requestStop, allowedOrigin }) {
8935
9041
  res.end();
8936
9042
  return;
8937
9043
  }
8938
- const path19 = String(req.url || "").split("?")[0];
9044
+ const path20 = String(req.url || "").split("?")[0];
8939
9045
  res.setHeader("content-type", "application/json");
8940
- if (req.method === "GET" && path19 === "/status") {
9046
+ if (req.method === "GET" && path20 === "/status") {
8941
9047
  let status;
8942
9048
  try {
8943
9049
  status = getStatus();
@@ -8948,7 +9054,7 @@ function buildControlHandler({ getStatus, requestStop, allowedOrigin }) {
8948
9054
  res.end(JSON.stringify({ ok: true, ...status }));
8949
9055
  return;
8950
9056
  }
8951
- if (req.method === "POST" && path19 === "/stop") {
9057
+ if (req.method === "POST" && path20 === "/stop") {
8952
9058
  if (!isControlOriginAllowed(req.headers.origin, allowedOrigin) || !req.headers["x-vo-control"]) {
8953
9059
  res.statusCode = 403;
8954
9060
  res.end(JSON.stringify({ ok: false, error: "forbidden" }));
@@ -9111,8 +9217,8 @@ var init_effort_mode_config = __esm({
9111
9217
  });
9112
9218
 
9113
9219
  // ../../scripts/virtual-office/model-registry.mjs
9114
- import fs8 from "node:fs";
9115
- import path15 from "node:path";
9220
+ import fs9 from "node:fs";
9221
+ import path16 from "node:path";
9116
9222
  import { fileURLToPath as fileURLToPath4 } from "node:url";
9117
9223
  function uniqueModels(models = []) {
9118
9224
  return [...new Set(models.map((model) => String(model || "").trim()).filter(Boolean))];
@@ -9224,9 +9330,9 @@ async function fetchGoogleModels(fetchImpl, env2 = process.env) {
9224
9330
  return (data?.models || []).map((model) => normalizeCatalogModel({ ...model, provider: "google", source: "google" })).filter(Boolean);
9225
9331
  }
9226
9332
  function readCache(cacheFile = DEFAULT_CACHE_FILE, nowMs = Date.now(), ttlMs = DEFAULT_TTL_MS3) {
9227
- if (!fs8.existsSync(cacheFile)) return null;
9333
+ if (!fs9.existsSync(cacheFile)) return null;
9228
9334
  try {
9229
- const parsed = JSON.parse(fs8.readFileSync(cacheFile, "utf-8"));
9335
+ const parsed = JSON.parse(fs9.readFileSync(cacheFile, "utf-8"));
9230
9336
  if (nowMs - Number(parsed.checkedAtMs || 0) > ttlMs) return null;
9231
9337
  if (!Array.isArray(parsed.models)) return null;
9232
9338
  return parsed;
@@ -9235,8 +9341,8 @@ function readCache(cacheFile = DEFAULT_CACHE_FILE, nowMs = Date.now(), ttlMs = D
9235
9341
  }
9236
9342
  }
9237
9343
  function writeCache(cacheFile = DEFAULT_CACHE_FILE, payload) {
9238
- fs8.mkdirSync(path15.dirname(cacheFile), { recursive: true });
9239
- fs8.writeFileSync(cacheFile, JSON.stringify(payload, null, 2));
9344
+ fs9.mkdirSync(path16.dirname(cacheFile), { recursive: true });
9345
+ fs9.writeFileSync(cacheFile, JSON.stringify(payload, null, 2));
9240
9346
  }
9241
9347
  async function fetchRegistryCatalog({
9242
9348
  fetchImpl = fetch,
@@ -9293,10 +9399,10 @@ var __dirname, ROOT, DEFAULT_CACHE_DIR, DEFAULT_CACHE_FILE, DEFAULT_TTL_MS3, ANT
9293
9399
  var init_model_registry = __esm({
9294
9400
  "../../scripts/virtual-office/model-registry.mjs"() {
9295
9401
  "use strict";
9296
- __dirname = path15.dirname(fileURLToPath4(import.meta.url));
9297
- ROOT = path15.resolve(__dirname, "..", "..");
9298
- DEFAULT_CACHE_DIR = path15.join(ROOT, ".virtual-office-cache", "model-registry");
9299
- DEFAULT_CACHE_FILE = path15.join(DEFAULT_CACHE_DIR, "catalog.json");
9402
+ __dirname = path16.dirname(fileURLToPath4(import.meta.url));
9403
+ ROOT = path16.resolve(__dirname, "..", "..");
9404
+ DEFAULT_CACHE_DIR = path16.join(ROOT, ".virtual-office-cache", "model-registry");
9405
+ DEFAULT_CACHE_FILE = path16.join(DEFAULT_CACHE_DIR, "catalog.json");
9300
9406
  DEFAULT_TTL_MS3 = 60 * 60 * 1e3;
9301
9407
  ANTHROPIC_API_VERSION = "2023-06-01";
9302
9408
  FAMILY_DEFINITIONS = {
@@ -9904,7 +10010,7 @@ var init_classify_task = __esm({
9904
10010
 
9905
10011
  // ../../scripts/virtual-office/code-runner/auto-router/effort-policy.mjs
9906
10012
  import { readFileSync as readFileSync4 } from "node:fs";
9907
- import { homedir as homedir5 } from "node:os";
10013
+ import { homedir as homedir6 } from "node:os";
9908
10014
  import { join as join7 } from "node:path";
9909
10015
  function difficultyToRung(difficulty, thresholds) {
9910
10016
  const b = thresholds.rungBounds;
@@ -9930,9 +10036,9 @@ function operatorTierToRung(tier, difficulty, thresholds) {
9930
10036
  if (tier === "best" && difficulty >= thresholds.rungBounds.R5) return "R5";
9931
10037
  return base;
9932
10038
  }
9933
- function readCodexModelsCache({ path: path19 = DEFAULT_CODEX_MODELS_CACHE, read = readFileSync4 } = {}) {
10039
+ function readCodexModelsCache({ path: path20 = DEFAULT_CODEX_MODELS_CACHE, read = readFileSync4 } = {}) {
9934
10040
  try {
9935
- const parsed = JSON.parse(read(path19, "utf8"));
10041
+ const parsed = JSON.parse(read(path20, "utf8"));
9936
10042
  return Array.isArray(parsed?.models) ? parsed : null;
9937
10043
  } catch {
9938
10044
  return null;
@@ -9982,7 +10088,7 @@ var init_effort_policy = __esm({
9982
10088
  init_meta_model_catalog();
9983
10089
  RUNG_ORDER = ["R1", "R2", "R3", "R4", "R5"];
9984
10090
  rungIndex = (rung) => RUNG_ORDER.indexOf(rung);
9985
- DEFAULT_CODEX_MODELS_CACHE = join7(homedir5(), ".codex", "models_cache.json");
10091
+ DEFAULT_CODEX_MODELS_CACHE = join7(homedir6(), ".codex", "models_cache.json");
9986
10092
  }
9987
10093
  });
9988
10094
 
@@ -10112,8 +10218,8 @@ var init_role_cost_shadow = __esm({
10112
10218
  });
10113
10219
 
10114
10220
  // ../../scripts/virtual-office/code-runner/auto-router/auto-router.mjs
10115
- import { readFileSync as readFileSync5, appendFileSync, mkdirSync as mkdirSync3 } from "node:fs";
10116
- import { homedir as homedir6 } from "node:os";
10221
+ import { readFileSync as readFileSync5, appendFileSync, mkdirSync as mkdirSync4 } from "node:fs";
10222
+ import { homedir as homedir7 } from "node:os";
10117
10223
  import { join as join8, dirname as dirname5 } from "node:path";
10118
10224
  import { fileURLToPath as fileURLToPath5 } from "node:url";
10119
10225
  function getAutoRouterMode(env2 = process.env) {
@@ -10189,15 +10295,15 @@ function formatDecisionReason(decision, maxLen = 480) {
10189
10295
  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("; ")}`;
10190
10296
  return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
10191
10297
  }
10192
- function appendDecisionFallback(decision, { path: path19 = DECISION_FALLBACK_PATH, append = appendFileSync, mkdir: mkdir4 = mkdirSync3, task, thresholds, roleCostInputs } = {}) {
10298
+ function appendDecisionFallback(decision, { path: path20 = DECISION_FALLBACK_PATH, append = appendFileSync, mkdir: mkdir4 = mkdirSync4, task, thresholds, roleCostInputs } = {}) {
10193
10299
  try {
10194
- mkdir4(dirname5(path19), { recursive: true });
10195
- append(path19, `${JSON.stringify(decision)}
10300
+ mkdir4(dirname5(path20), { recursive: true });
10301
+ append(path20, `${JSON.stringify(decision)}
10196
10302
  `, "utf8");
10197
10303
  if (isRouterDecision(decision)) {
10198
10304
  try {
10199
10305
  const records = buildShadowRecords({ decision, task, thresholds: thresholds || loadThresholds(), roleCostInputs });
10200
- for (const record of records) append(path19, `${JSON.stringify(record)}
10306
+ for (const record of records) append(path20, `${JSON.stringify(record)}
10201
10307
  `, "utf8");
10202
10308
  } catch {
10203
10309
  }
@@ -10215,7 +10321,7 @@ var init_auto_router = __esm({
10215
10321
  init_effort_policy();
10216
10322
  init_role_cost_shadow();
10217
10323
  ROUTER_VERSION = "0.1.0";
10218
- DECISION_FALLBACK_PATH = join8(homedir6(), ".claude", "vo-auto-router-decisions.jsonl");
10324
+ DECISION_FALLBACK_PATH = join8(homedir7(), ".claude", "vo-auto-router-decisions.jsonl");
10219
10325
  MODES = /* @__PURE__ */ new Set(["off", "shadow", "on"]);
10220
10326
  cachedThresholds = null;
10221
10327
  isRouterDecision = (d) => Boolean(d && typeof d === "object" && typeof d.taskClass === "string" && typeof d.confidence === "number");
@@ -11188,9 +11294,9 @@ var init_inference_task_runner = __esm({
11188
11294
  });
11189
11295
 
11190
11296
  // ../../scripts/virtual-office/code-runner/isolation-audit.mjs
11191
- import fs9 from "node:fs";
11297
+ import fs10 from "node:fs";
11192
11298
  import fsp11 from "node:fs/promises";
11193
- import path16 from "node:path";
11299
+ import path17 from "node:path";
11194
11300
  async function defaultRun(command, args, cwd, options = {}) {
11195
11301
  return runProcess2(command, args, { cwd, timeout: 6e4, ...options });
11196
11302
  }
@@ -11203,7 +11309,7 @@ async function canonicalRootForWorktree(worktreeDir, run) {
11203
11309
  "--path-format=absolute",
11204
11310
  "--git-common-dir"
11205
11311
  ])).trim();
11206
- const root = path16.dirname(commonDir);
11312
+ const root = path17.dirname(commonDir);
11207
11313
  return samePath2(root, worktreeDir) ? null : root;
11208
11314
  }
11209
11315
  async function snapshot(root, run) {
@@ -11245,21 +11351,21 @@ async function changedPaths(root, run) {
11245
11351
  }
11246
11352
  async function quarantineCanonicalWrites({ baseline, worktreeDir, taskId, run, now }) {
11247
11353
  const paths = await changedPaths(baseline.root, run);
11248
- const quarantineDir = path16.join(
11249
- path16.dirname(worktreeDir),
11354
+ const quarantineDir = path17.join(
11355
+ path17.dirname(worktreeDir),
11250
11356
  ".canonical-recovery",
11251
11357
  `${String(taskId || "unknown").replace(/[^a-z0-9-]/gi, "-")}-${now().toISOString().replace(/[:.]/g, "-")}`
11252
11358
  );
11253
11359
  await fsp11.mkdir(quarantineDir, { recursive: true });
11254
11360
  const patch = await git2(run, baseline.root, ["diff", "--binary", "HEAD"], { raw: true });
11255
- await fsp11.writeFile(path16.join(quarantineDir, "tracked.patch"), patch, "utf8");
11361
+ await fsp11.writeFile(path17.join(quarantineDir, "tracked.patch"), patch, "utf8");
11256
11362
  for (const relative of paths.untracked) {
11257
- const source = path16.join(baseline.root, relative);
11258
- const target = path16.join(quarantineDir, "untracked", relative);
11259
- await fsp11.mkdir(path16.dirname(target), { recursive: true });
11363
+ const source = path17.join(baseline.root, relative);
11364
+ const target = path17.join(quarantineDir, "untracked", relative);
11365
+ await fsp11.mkdir(path17.dirname(target), { recursive: true });
11260
11366
  await fsp11.copyFile(source, target);
11261
11367
  }
11262
- await fsp11.writeFile(path16.join(quarantineDir, "manifest.json"), `${JSON.stringify({
11368
+ await fsp11.writeFile(path17.join(quarantineDir, "manifest.json"), `${JSON.stringify({
11263
11369
  taskId,
11264
11370
  canonicalRoot: baseline.root,
11265
11371
  canonicalHead: baseline.head,
@@ -11281,9 +11387,9 @@ async function restoreExactCanonicalPaths(baseline, evidence, run) {
11281
11387
  ]);
11282
11388
  }
11283
11389
  for (const relative of evidence.untracked) {
11284
- const target = path16.resolve(baseline.root, relative);
11285
- const prefix = `${path16.resolve(baseline.root)}${path16.sep}`;
11286
- if (!target.startsWith(prefix) || !fs9.existsSync(target)) continue;
11390
+ const target = path17.resolve(baseline.root, relative);
11391
+ const prefix = `${path17.resolve(baseline.root)}${path17.sep}`;
11392
+ if (!target.startsWith(prefix) || !fs10.existsSync(target)) continue;
11287
11393
  await fsp11.rm(target, { force: true });
11288
11394
  }
11289
11395
  }
@@ -11319,7 +11425,7 @@ var init_isolation_audit = __esm({
11319
11425
  init_process_runner2();
11320
11426
  splitZ2 = (value) => String(value || "").split("\0").map((item) => item.trim()).filter(Boolean);
11321
11427
  samePath2 = (left, right) => {
11322
- const [a, b] = [left, right].map((value) => path16.resolve(value));
11428
+ const [a, b] = [left, right].map((value) => path17.resolve(value));
11323
11429
  return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
11324
11430
  };
11325
11431
  }
@@ -11679,7 +11785,7 @@ var init_publication_outcome = __esm({
11679
11785
 
11680
11786
  // ../../scripts/virtual-office/code-runner/committed-scratch-cleanup.mjs
11681
11787
  import fsp12 from "node:fs/promises";
11682
- import path17 from "node:path";
11788
+ import path18 from "node:path";
11683
11789
  function defaultRun2(command, args, cwd, options = {}) {
11684
11790
  return runProcess2(command, args, { cwd, ...options });
11685
11791
  }
@@ -11687,13 +11793,13 @@ async function resolveSafeScratchTarget(worktreeDir, file) {
11687
11793
  if (!isAgentScratch(file)) {
11688
11794
  throw new Error(`refusing to remove non-scratch publication path: ${file}`);
11689
11795
  }
11690
- const root = path17.resolve(worktreeDir);
11691
- const target = path17.resolve(root, file);
11692
- const relative = path17.relative(root, target);
11693
- if (!relative || relative.startsWith(`..${path17.sep}`) || path17.isAbsolute(relative)) {
11796
+ const root = path18.resolve(worktreeDir);
11797
+ const target = path18.resolve(root, file);
11798
+ const relative = path18.relative(root, target);
11799
+ if (!relative || relative.startsWith(`..${path18.sep}`) || path18.isAbsolute(relative)) {
11694
11800
  throw new Error(`refusing to remove publication scratch outside worktree: ${file}`);
11695
11801
  }
11696
- for (let cursor = target; cursor !== root; cursor = path17.dirname(cursor)) {
11802
+ for (let cursor = target; cursor !== root; cursor = path18.dirname(cursor)) {
11697
11803
  try {
11698
11804
  if ((await fsp12.lstat(cursor)).isSymbolicLink()) {
11699
11805
  throw new Error(`refusing to follow symlink while removing publication scratch: ${file}`);
@@ -11813,9 +11919,9 @@ var init_publication_scope = __esm({
11813
11919
  });
11814
11920
 
11815
11921
  // ../../scripts/virtual-office/code-runner/recovery-ledger.mjs
11816
- import fs10 from "node:fs";
11922
+ import fs11 from "node:fs";
11817
11923
  import fsp13 from "node:fs/promises";
11818
- import path18 from "node:path";
11924
+ import path19 from "node:path";
11819
11925
  function recoveryTaskId(prompt) {
11820
11926
  const match = String(prompt || "").match(/VO_RECOVERY_FROM_CODE_TASK:\s*([0-9a-f-]{36})/i);
11821
11927
  return match ? match[1].toLowerCase() : null;
@@ -11829,10 +11935,10 @@ function cloneLeaf(repo) {
11829
11935
  function recoveryLedgerCandidates(repo, clonesRoot2) {
11830
11936
  const leaf = cloneLeaf(repo);
11831
11937
  if (!leaf || !clonesRoot2) return [];
11832
- const canonical = path18.join(clonesRoot2, leaf);
11938
+ const canonical = path19.join(clonesRoot2, leaf);
11833
11939
  return [
11834
- path18.join(clonesRoot2, ".agent-worktrees", leaf, "recovery-ledger.jsonl"),
11835
- path18.join(canonical, ".agent-worktrees", "recovery-ledger.jsonl")
11940
+ path19.join(clonesRoot2, ".agent-worktrees", leaf, "recovery-ledger.jsonl"),
11941
+ path19.join(canonical, ".agent-worktrees", "recovery-ledger.jsonl")
11836
11942
  ];
11837
11943
  }
11838
11944
  async function readLedger(file, readFile5) {
@@ -11851,7 +11957,7 @@ async function readLedger(file, readFile5) {
11851
11957
  async function findPreservedRecovery(task, {
11852
11958
  clonesRoot: clonesRoot2 = process.env.VO_CODE_RUNNER_CLONES_ROOT || "",
11853
11959
  readFile: readFile5 = fsp13.readFile,
11854
- exists = fs10.existsSync
11960
+ exists = fs11.existsSync
11855
11961
  } = {}) {
11856
11962
  const resumedFrom = /^[0-9a-f-]{36}$/iu.test(String(task.resumed_from || "")) ? String(task.resumed_from).toLowerCase() : null;
11857
11963
  const originalTaskId = recoveryTaskId(task.prompt) ?? resumedFrom;
@@ -12165,7 +12271,7 @@ var init_cancellation_probe = __esm({
12165
12271
  });
12166
12272
 
12167
12273
  // ../../scripts/virtual-office/code-runner/detached-economics-spool.mjs
12168
- import { homedir as homedir7 } from "node:os";
12274
+ import { homedir as homedir8 } from "node:os";
12169
12275
  import { dirname as dirname6, join as join9 } from "node:path";
12170
12276
  import { mkdir as mkdir3, readFile as readFile4, rename as rename2, writeFile as writeFile3 } from "node:fs/promises";
12171
12277
  function withLock(operation) {
@@ -12230,13 +12336,13 @@ var DEFAULT_FILE, serialized;
12230
12336
  var init_detached_economics_spool = __esm({
12231
12337
  "../../scripts/virtual-office/code-runner/detached-economics-spool.mjs"() {
12232
12338
  "use strict";
12233
- DEFAULT_FILE = join9(homedir7(), ".vo", "detached-run-economics.json");
12339
+ DEFAULT_FILE = join9(homedir8(), ".vo", "detached-run-economics.json");
12234
12340
  serialized = Promise.resolve();
12235
12341
  }
12236
12342
  });
12237
12343
 
12238
12344
  // ../../scripts/virtual-office/code-runner/killed-run-outcome.mjs
12239
- import { randomUUID as randomUUID4 } from "node:crypto";
12345
+ import { randomUUID as randomUUID5 } from "node:crypto";
12240
12346
  async function handleKilledRun({
12241
12347
  client,
12242
12348
  id,
@@ -12273,7 +12379,7 @@ async function handleKilledRun({
12273
12379
  };
12274
12380
  }
12275
12381
  if (reason === "claim_authority_changed") {
12276
- const occurrenceId = randomUUID4();
12382
+ const occurrenceId = randomUUID5();
12277
12383
  const economics = {
12278
12384
  occurrence_id: occurrenceId,
12279
12385
  runner_id: runnerId,
@@ -12570,7 +12676,7 @@ var code_runner_daemon_exports = {};
12570
12676
  __export(code_runner_daemon_exports, {
12571
12677
  main: () => main
12572
12678
  });
12573
- import { randomUUID as randomUUID5 } from "node:crypto";
12679
+ import { randomUUID as randomUUID6 } from "node:crypto";
12574
12680
  import { fileURLToPath as fileURLToPath6 } from "node:url";
12575
12681
  function log(msg) {
12576
12682
  console.log(`[code-runner ${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}`);
@@ -12800,7 +12906,7 @@ Closes #${publicationTarget.supersedesPrNumber}` : "";
12800
12906
  async function main({ env: env2 = process.env, once: once2 = false } = {}) {
12801
12907
  const cfg = loadCodeRunnerConfig(env2, { log });
12802
12908
  await sweepStaleTaskAttachmentDirectories().catch((error) => log(`stale attachment cleanup failed: ${error.message}`));
12803
- const runnerInstanceId = randomUUID5();
12909
+ const runnerInstanceId = randomUUID6();
12804
12910
  const client = createControlPlaneClient({
12805
12911
  env: env2,
12806
12912
  runnerId: cfg.runnerId,
@@ -13129,6 +13235,98 @@ function pairedOperatorScope(readiness) {
13129
13235
  return readiness?.ok === true && readiness?.paired === true && typeof readiness.operatorId === "string" && readiness.operatorId.trim() ? readiness.operatorId.trim() : null;
13130
13236
  }
13131
13237
 
13238
+ // src/runner/root-config.mjs
13239
+ import { closeSync, existsSync, mkdirSync, openSync, unlinkSync } from "node:fs";
13240
+ import { homedir } from "node:os";
13241
+ import { posix, win32 } from "node:path";
13242
+ import { randomUUID } from "node:crypto";
13243
+ var APP_IDENTIFIER = "ai.algosuite.vo-runner";
13244
+ var CLONES_DIR = "clones";
13245
+ function pathsFor(platform) {
13246
+ return platform === "win32" ? win32 : posix;
13247
+ }
13248
+ function absoluteOrNull(value, pathApi) {
13249
+ const normalized = String(value || "").trim();
13250
+ return normalized && pathApi.isAbsolute(normalized) ? pathApi.resolve(normalized) : null;
13251
+ }
13252
+ function defaultClonesRoot({
13253
+ platform = process.platform,
13254
+ env: env2 = process.env,
13255
+ home = homedir()
13256
+ } = {}) {
13257
+ const pathApi = pathsFor(platform);
13258
+ if (platform === "win32") {
13259
+ const appData = absoluteOrNull(env2.APPDATA, pathApi);
13260
+ return appData ? pathApi.join(appData, APP_IDENTIFIER, CLONES_DIR) : null;
13261
+ }
13262
+ const absoluteHome = absoluteOrNull(home, pathApi);
13263
+ if (!absoluteHome) return null;
13264
+ if (platform === "darwin") {
13265
+ return pathApi.join(absoluteHome, "Library", "Application Support", APP_IDENTIFIER, CLONES_DIR);
13266
+ }
13267
+ const xdg = absoluteOrNull(env2.XDG_CONFIG_HOME, pathApi);
13268
+ return pathApi.join(xdg || pathApi.join(absoluteHome, ".config"), APP_IDENTIFIER, CLONES_DIR);
13269
+ }
13270
+ function findGitRoot(cwd, { platform, exists = existsSync }) {
13271
+ const pathApi = pathsFor(platform);
13272
+ let cursor = pathApi.resolve(cwd);
13273
+ for (; ; ) {
13274
+ if (exists(pathApi.join(cursor, ".git"))) return cursor;
13275
+ const parent = pathApi.dirname(cursor);
13276
+ if (parent === cursor) return null;
13277
+ cursor = parent;
13278
+ }
13279
+ }
13280
+ function resolveRunnerRootConfig({
13281
+ env: env2 = process.env,
13282
+ cwd = process.cwd(),
13283
+ platform = process.platform,
13284
+ home = homedir(),
13285
+ exists = existsSync
13286
+ } = {}) {
13287
+ const pathApi = pathsFor(platform);
13288
+ const explicitRepoValue = String(env2.VO_CODE_RUNNER_REPO || "").trim();
13289
+ const explicitClonesValue = String(env2.VO_CODE_RUNNER_CLONES_ROOT || "").trim();
13290
+ if (explicitRepoValue && !pathApi.isAbsolute(explicitRepoValue)) {
13291
+ throw new Error(`VO_CODE_RUNNER_REPO must be an absolute path (got '${explicitRepoValue}')`);
13292
+ }
13293
+ if (explicitClonesValue && !pathApi.isAbsolute(explicitClonesValue)) {
13294
+ throw new Error(`VO_CODE_RUNNER_CLONES_ROOT must be an absolute path (got '${explicitClonesValue}')`);
13295
+ }
13296
+ const repoRoot2 = explicitRepoValue ? pathApi.resolve(explicitRepoValue) : findGitRoot(cwd, { platform, exists });
13297
+ const clonesRoot2 = explicitClonesValue ? pathApi.resolve(explicitClonesValue) : repoRoot2 ? null : defaultClonesRoot({ platform, env: env2, home });
13298
+ if (!repoRoot2 && !clonesRoot2) {
13299
+ throw new Error(
13300
+ "No safe runner root is available. Set VO_CODE_RUNNER_REPO or VO_CODE_RUNNER_CLONES_ROOT to an absolute path."
13301
+ );
13302
+ }
13303
+ return { repoRoot: repoRoot2, clonesRoot: clonesRoot2 };
13304
+ }
13305
+ function runnerWorkingDirectory({ repoRoot: repoRoot2, clonesRoot: clonesRoot2 }) {
13306
+ const root = repoRoot2 || clonesRoot2;
13307
+ if (!root) throw new Error("Runner root configuration has no working directory");
13308
+ return root;
13309
+ }
13310
+ function assertWritableRunnerDirectory(root) {
13311
+ mkdirSync(root, { recursive: true });
13312
+ const probe = pathsFor(process.platform).join(root, `.vo-runner-write-probe-${process.pid}-${randomUUID()}`);
13313
+ let handle;
13314
+ try {
13315
+ handle = openSync(probe, "wx", 384);
13316
+ } catch (error) {
13317
+ throw new Error(
13318
+ `Runner clones root is not writable: ${root} (${error instanceof Error ? error.message : String(error)})`,
13319
+ { cause: error }
13320
+ );
13321
+ } finally {
13322
+ if (handle !== void 0) closeSync(handle);
13323
+ try {
13324
+ unlinkSync(probe);
13325
+ } catch {
13326
+ }
13327
+ }
13328
+ }
13329
+
13132
13330
  // src/runner-cli.mjs
13133
13331
  var DEFAULT_CONTROL_PLANE_URL = "https://vo-control-plane-bzjphrajaq-uc.a.run.app";
13134
13332
  function packageVersion() {
@@ -13143,11 +13341,45 @@ if (process.argv.includes("--version") || process.argv.includes("-v")) {
13143
13341
  `);
13144
13342
  process.exit(0);
13145
13343
  }
13344
+ var USAGE = `vo-mcp runner \u2014 bring-your-own agent runner daemon
13345
+
13346
+ Usage:
13347
+ vo-mcp runner poll for tasks forever (default)
13348
+ vo-mcp runner --once claim + run one task, then exit
13349
+ vo-mcp runner --status print pairing/readiness JSON, then exit
13350
+ vo-mcp runner --version print the version, then exit
13351
+ vo-mcp runner --help print this help, then exit
13352
+
13353
+ Env (all optional):
13354
+ VO_CONTROL_PLANE_ADMIN_TOKEN explicit bearer (wins over the stored credential)
13355
+ VO_CONTROL_PLANE_URL control-plane base URL (default: production)
13356
+ VO_CODE_RUNNER_REPO path to your repo clone (default: Git cwd)
13357
+ VO_CODE_RUNNER_CLONES_ROOT managed clone directory (default outside Git cwd)
13358
+ VO_CODE_RUNNER_OPERATOR_IDS operator id(s) this runner serves (your own)
13359
+ VO_CODE_RUNNER_REPOS owner/name repo(s) this runner builds
13360
+
13361
+ Pair this computer first with \`vo-mcp login\`.
13362
+ `;
13363
+ if (process.argv.includes("--help") || process.argv.includes("-h")) {
13364
+ process.stdout.write(USAGE);
13365
+ process.exit(0);
13366
+ }
13146
13367
  var { readStoredCredential: readStoredCredential2 } = await Promise.resolve().then(() => (init_credential_store(), credential_store_exports));
13147
13368
  var storedCredential = readStoredCredential2();
13148
13369
  var explicitAdminToken = process.env.VO_CONTROL_PLANE_ADMIN_TOKEN?.trim();
13149
13370
  var token = explicitAdminToken || storedCredential?.vo_credential;
13150
13371
  var statusOnly = process.argv.includes("--status");
13372
+ function configureRunnerFilesystem() {
13373
+ const config = resolveRunnerRootConfig();
13374
+ if (config.repoRoot) process.env.VO_CODE_RUNNER_REPO = config.repoRoot;
13375
+ else delete process.env.VO_CODE_RUNNER_REPO;
13376
+ if (config.clonesRoot) {
13377
+ assertWritableRunnerDirectory(config.clonesRoot);
13378
+ process.env.VO_CODE_RUNNER_CLONES_ROOT = config.clonesRoot;
13379
+ } else delete process.env.VO_CODE_RUNNER_CLONES_ROOT;
13380
+ process.chdir(runnerWorkingDirectory(config));
13381
+ return config;
13382
+ }
13151
13383
  if (statusOnly) {
13152
13384
  if (!storedCredential?.vo_credential) {
13153
13385
  process.stdout.write(`${JSON.stringify({
@@ -13156,6 +13388,7 @@ if (statusOnly) {
13156
13388
  operatorId: null,
13157
13389
  tenantId: null,
13158
13390
  githubReady: null,
13391
+ filesystemReady: null,
13159
13392
  error: "credential_missing",
13160
13393
  message: "This computer is not paired. Pair it to your AlgoHQ account first."
13161
13394
  })}
@@ -13166,9 +13399,28 @@ if (statusOnly) {
13166
13399
  controlPlaneUrl: process.env.VO_CONTROL_PLANE_URL || DEFAULT_CONTROL_PLANE_URL,
13167
13400
  token: storedCredential.vo_credential
13168
13401
  });
13169
- process.stdout.write(`${JSON.stringify(readiness)}
13402
+ if (!readiness.ok) {
13403
+ process.stdout.write(`${JSON.stringify({ ...readiness, filesystemReady: null })}
13170
13404
  `);
13171
- process.exit(readiness.ok ? 0 : 1);
13405
+ process.exit(1);
13406
+ }
13407
+ try {
13408
+ configureRunnerFilesystem();
13409
+ process.stdout.write(`${JSON.stringify({ ...readiness, filesystemReady: true })}
13410
+ `);
13411
+ process.exit(0);
13412
+ } catch (error) {
13413
+ const detail = error instanceof Error ? error.message : String(error);
13414
+ process.stdout.write(`${JSON.stringify({
13415
+ ...readiness,
13416
+ ok: false,
13417
+ filesystemReady: false,
13418
+ error: "filesystem_not_ready",
13419
+ message: `Filesystem readiness failed: ${detail}`
13420
+ })}
13421
+ `);
13422
+ process.exit(1);
13423
+ }
13172
13424
  }
13173
13425
  if (!token) {
13174
13426
  console.error("[vo-mcp runner] No credential found. Run `vo-mcp login` first.");
@@ -13193,6 +13445,13 @@ if (!explicitAdminToken) {
13193
13445
  process.exit(1);
13194
13446
  }
13195
13447
  }
13448
+ var rootConfig;
13449
+ try {
13450
+ rootConfig = configureRunnerFilesystem();
13451
+ } catch (error) {
13452
+ console.error(`[vo-mcp runner] Filesystem readiness failed: ${error instanceof Error ? error.message : String(error)}`);
13453
+ process.exit(1);
13454
+ }
13196
13455
  var env = {
13197
13456
  ...process.env,
13198
13457
  VO_CONTROL_PLANE_ADMIN_TOKEN: token,
@@ -13201,7 +13460,8 @@ var env = {
13201
13460
  // VO_CODE_RUNNER_VERSION, which a desktop host may set to its own shell
13202
13461
  // release even after runner-control updates this package in place.
13203
13462
  VO_CODE_RUNNER_DAEMON_VERSION: `vo-mcp/${packageVersion()}`,
13204
- VO_CODE_RUNNER_REPO: process.env.VO_CODE_RUNNER_REPO || process.cwd(),
13463
+ ...rootConfig.repoRoot ? { VO_CODE_RUNNER_REPO: rootConfig.repoRoot } : {},
13464
+ ...rootConfig.clonesRoot ? { VO_CODE_RUNNER_CLONES_ROOT: rootConfig.clonesRoot } : {},
13205
13465
  // Server-resolved identity replaces stale/hardcoded desktop scope. Besides
13206
13466
  // filtering heartbeat/claims, this keeps GitHub App auth required for the
13207
13467
  // entire task lifecycle (never ambient-gh fallback after a paired preflight).