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

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);
@@ -2476,6 +2582,11 @@ var init_control_plane_auth_stub = __esm({
2476
2582
  });
2477
2583
 
2478
2584
  // ../../scripts/virtual-office/code-runner/control-plane-client.mjs
2585
+ var control_plane_client_exports = {};
2586
+ __export(control_plane_client_exports, {
2587
+ ClaimAuthorityChangedError: () => ClaimAuthorityChangedError,
2588
+ createControlPlaneClient: () => createControlPlaneClient
2589
+ });
2479
2590
  async function resolveBearer(env2) {
2480
2591
  const adminToken = env2.VO_CONTROL_PLANE_ADMIN_TOKEN;
2481
2592
  if (adminToken) return adminToken;
@@ -2508,11 +2619,11 @@ function createControlPlaneClient({
2508
2619
  const resolvedBaseUrl = baseUrl ?? env2.VO_CONTROL_PLANE_URL ?? "";
2509
2620
  if (!resolvedBaseUrl) throw new Error("VO_CONTROL_PLANE_URL is required for the code-runner daemon");
2510
2621
  const root = resolvedBaseUrl.replace(/\/+$/, "");
2511
- async function req(method, path19, body, { timeoutMs } = {}) {
2622
+ async function req(method, path23, body, { timeoutMs } = {}) {
2512
2623
  const bearer = await resolveBearer(env2);
2513
2624
  const controller = timeoutMs ? new AbortController() : null;
2514
2625
  let timeoutId;
2515
- const request = Promise.resolve(fetchImpl(`${root}${path19}`, {
2626
+ const request = Promise.resolve(fetchImpl(`${root}${path23}`, {
2516
2627
  method,
2517
2628
  headers: {
2518
2629
  "content-type": "application/json",
@@ -2525,7 +2636,7 @@ function createControlPlaneClient({
2525
2636
  const timeout = new Promise((_, reject) => {
2526
2637
  timeoutId = setTimeout(() => {
2527
2638
  controller.abort();
2528
- reject(new Error(`control-plane ${path19} timed out after ${timeoutMs}ms`));
2639
+ reject(new Error(`control-plane ${path23} timed out after ${timeoutMs}ms`));
2529
2640
  }, timeoutMs);
2530
2641
  });
2531
2642
  try {
@@ -2534,7 +2645,7 @@ function createControlPlaneClient({
2534
2645
  clearTimeout(timeoutId);
2535
2646
  }
2536
2647
  }
2537
- const taskReq = (method, path19, body, options = {}) => req(method, path19, body, { timeoutMs: taskRequestTimeoutMs, ...options });
2648
+ const taskReq = (method, path23, body, options = {}) => req(method, path23, body, { timeoutMs: taskRequestTimeoutMs, ...options });
2538
2649
  return {
2539
2650
  ...makeAutonomousDispatchAdmissionClient(
2540
2651
  req,
@@ -2657,8 +2768,8 @@ function createControlPlaneClient({
2657
2768
  return listAllPrOpenedTasks(taskReq);
2658
2769
  },
2659
2770
  async downloadTaskAttachment(taskId, attachmentId) {
2660
- const path19 = `/api/v1/code-task/${encodeURIComponent(taskId)}/attachment/${encodeURIComponent(attachmentId)}`;
2661
- const res = await taskReq("GET", path19);
2771
+ const path23 = `/api/v1/code-task/${encodeURIComponent(taskId)}/attachment/${encodeURIComponent(attachmentId)}`;
2772
+ const res = await taskReq("GET", path23);
2662
2773
  if (res.status === 401) cachedFirebaseToken = null;
2663
2774
  if (!res.ok) throw new Error(`attachment download failed: HTTP ${res.status}`);
2664
2775
  return Buffer.from(await res.arrayBuffer());
@@ -2852,8 +2963,8 @@ var init_control_plane_client = __esm({
2852
2963
  });
2853
2964
 
2854
2965
  // ../../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";
2966
+ import { existsSync as existsSync3, realpathSync } from "node:fs";
2967
+ import { win32 as path11 } from "node:path";
2857
2968
  import { spawnSync } from "node:child_process";
2858
2969
  function pathValue(env2) {
2859
2970
  for (const key of ["Path", "PATH", "path"]) {
@@ -2874,38 +2985,38 @@ function envValue(env2, name) {
2874
2985
  function userClaudeCandidates(bin, env2) {
2875
2986
  if (!/^claude(?:\.(?:exe|cmd|ps1))?$/iu.test(bin)) return [];
2876
2987
  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") : "");
2988
+ const appData = envValue(env2, "APPDATA") || (userProfile ? path11.join(userProfile, "AppData", "Roaming") : "");
2989
+ const localAppData = envValue(env2, "LOCALAPPDATA") || (userProfile ? path11.join(userProfile, "AppData", "Local") : "");
2879
2990
  const candidates = [];
2880
2991
  if (appData) {
2881
- const npmBin = path10.join(appData, "npm");
2992
+ const npmBin = path11.join(appData, "npm");
2882
2993
  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)
2994
+ path11.join(npmBin, "claude.exe"),
2995
+ path11.join(npmBin, "claude.cmd"),
2996
+ path11.join(npmBin, "claude.ps1"),
2997
+ path11.join(npmBin, "claude"),
2998
+ path11.join(npmBin, ...NATIVE_CLAUDE_PARTS)
2888
2999
  );
2889
3000
  }
2890
- if (userProfile) candidates.push(path10.join(userProfile, ".local", "bin", "claude.exe"));
3001
+ if (userProfile) candidates.push(path11.join(userProfile, ".local", "bin", "claude.exe"));
2891
3002
  if (localAppData) {
2892
3003
  candidates.push(
2893
- path10.join(localAppData, "Microsoft", "WinGet", "Links", "claude.exe"),
2894
- path10.join(localAppData, "Microsoft", "WindowsApps", "claude.exe")
3004
+ path11.join(localAppData, "Microsoft", "WinGet", "Links", "claude.exe"),
3005
+ path11.join(localAppData, "Microsoft", "WindowsApps", "claude.exe")
2895
3006
  );
2896
3007
  }
2897
3008
  return candidates;
2898
3009
  }
2899
3010
  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)
3011
+ if (path11.isAbsolute(bin) || /[\\/]/u.test(bin)) {
3012
+ return [path11.resolve(bin)];
3013
+ }
3014
+ const extension = path11.extname(bin);
3015
+ const fromPath = pathValue(env2).split(";").map(cleanPathSegment).filter(Boolean).flatMap((directory) => extension ? [path11.join(directory, bin)] : [
3016
+ path11.join(directory, `${bin}.exe`),
3017
+ path11.join(directory, `${bin}.cmd`),
3018
+ path11.join(directory, `${bin}.ps1`),
3019
+ path11.join(directory, bin)
2909
3020
  ]);
2910
3021
  const seen = /* @__PURE__ */ new Set();
2911
3022
  return [...fromPath, ...userClaudeCandidates(bin, env2)].filter((candidate) => {
@@ -2926,7 +3037,7 @@ function canonicalExistingPath(candidate, exists, canonicalize) {
2926
3037
  function resolveWindowsClaudeExecutable({
2927
3038
  bin = "claude",
2928
3039
  env: env2 = process.env,
2929
- exists = existsSync2,
3040
+ exists = existsSync3,
2930
3041
  canonicalize = realpathSync
2931
3042
  } = {}) {
2932
3043
  const requested = String(bin || "").trim();
@@ -2936,8 +3047,8 @@ function resolveWindowsClaudeExecutable({
2936
3047
  for (const candidate of pathCandidates(requested, env2)) {
2937
3048
  const found = canonicalExistingPath(candidate, exists, canonicalize);
2938
3049
  if (!found) continue;
2939
- if (path10.extname(found).toLowerCase() === ".exe") return found;
2940
- const native = path10.join(path10.dirname(found), ...NATIVE_CLAUDE_PARTS);
3050
+ if (path11.extname(found).toLowerCase() === ".exe") return found;
3051
+ const native = path11.join(path11.dirname(found), ...NATIVE_CLAUDE_PARTS);
2941
3052
  const resolvedNative = canonicalExistingPath(native, exists, canonicalize);
2942
3053
  if (resolvedNative) return resolvedNative;
2943
3054
  }
@@ -3404,14 +3515,14 @@ var init_terminal_process_cleanup = __esm({
3404
3515
 
3405
3516
  // ../../scripts/virtual-office/code-runner/orphan-agent-reaper.mjs
3406
3517
  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";
3518
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
3408
3519
  import os from "node:os";
3409
- import path11 from "node:path";
3520
+ import path12 from "node:path";
3410
3521
  function registryRoot(tmp = os.tmpdir()) {
3411
- return path11.join(tmp, REGISTRY_ROOT_NAME);
3522
+ return path12.join(tmp, REGISTRY_ROOT_NAME);
3412
3523
  }
3413
3524
  function instanceDir(root, instanceId) {
3414
- return path11.join(root, String(instanceId).replace(/[^A-Za-z0-9_-]/g, ""));
3525
+ return path12.join(root, String(instanceId).replace(/[^A-Za-z0-9_-]/g, ""));
3415
3526
  }
3416
3527
  function registerDaemonInstance({
3417
3528
  root = registryRoot(),
@@ -3421,8 +3532,8 @@ function registerDaemonInstance({
3421
3532
  } = {}) {
3422
3533
  if (!instanceId) return null;
3423
3534
  const dir = instanceDir(root, instanceId);
3424
- mkdirSync2(dir, { recursive: true });
3425
- const file = path11.join(dir, DAEMON_RECORD);
3535
+ mkdirSync3(dir, { recursive: true });
3536
+ const file = path12.join(dir, DAEMON_RECORD);
3426
3537
  writeFileSync2(file, JSON.stringify({ daemonPid, daemonStartedAtMs, instanceId }), {
3427
3538
  encoding: "utf8",
3428
3539
  mode: 384
@@ -3439,9 +3550,9 @@ function recordAgentPid({
3439
3550
  if (!instanceId || !Number.isInteger(pid) || pid <= 0) return false;
3440
3551
  try {
3441
3552
  const dir = instanceDir(root, instanceId);
3442
- mkdirSync2(dir, { recursive: true });
3553
+ mkdirSync3(dir, { recursive: true });
3443
3554
  writeFileSync2(
3444
- path11.join(dir, `${pid}.json`),
3555
+ path12.join(dir, `${pid}.json`),
3445
3556
  JSON.stringify({ pid, agentId, startedAtMs, instanceId }),
3446
3557
  { encoding: "utf8", mode: 384 }
3447
3558
  );
@@ -3457,7 +3568,7 @@ function unrecordAgentPid({
3457
3568
  } = {}) {
3458
3569
  if (!instanceId || !Number.isInteger(pid)) return false;
3459
3570
  try {
3460
- rmSync2(path11.join(instanceDir(root, instanceId), `${pid}.json`), { force: true });
3571
+ rmSync2(path12.join(instanceDir(root, instanceId), `${pid}.json`), { force: true });
3461
3572
  return true;
3462
3573
  } catch {
3463
3574
  return false;
@@ -3475,7 +3586,7 @@ function bootstrapOrphanReaper({ instanceId, log: log2 = () => {
3475
3586
  }
3476
3587
  function readRegistry({ root = registryRoot(), currentInstanceId } = {}) {
3477
3588
  const instances = [];
3478
- if (!existsSync3(root)) return instances;
3589
+ if (!existsSync4(root)) return instances;
3479
3590
  let dirents;
3480
3591
  try {
3481
3592
  dirents = readdirSync(root, { withFileTypes: true });
@@ -3485,7 +3596,7 @@ function readRegistry({ root = registryRoot(), currentInstanceId } = {}) {
3485
3596
  for (const dirent of dirents) {
3486
3597
  if (!dirent.isDirectory()) continue;
3487
3598
  if (currentInstanceId && dirent.name === instanceDirName(currentInstanceId)) continue;
3488
- const dir = path11.join(root, dirent.name);
3599
+ const dir = path12.join(root, dirent.name);
3489
3600
  let daemon = null;
3490
3601
  const agents = [];
3491
3602
  let files;
@@ -3497,7 +3608,7 @@ function readRegistry({ root = registryRoot(), currentInstanceId } = {}) {
3497
3608
  for (const name of files) {
3498
3609
  let parsed;
3499
3610
  try {
3500
- parsed = JSON.parse(readFileSync2(path11.join(dir, name), "utf8"));
3611
+ parsed = JSON.parse(readFileSync2(path12.join(dir, name), "utf8"));
3501
3612
  } catch {
3502
3613
  continue;
3503
3614
  }
@@ -3545,7 +3656,7 @@ function windowsSystemRoot(env2 = process.env) {
3545
3656
  return env2.SystemRoot || env2.WINDIR || "C:\\Windows";
3546
3657
  }
3547
3658
  function windowsPowershellExe(env2 = process.env) {
3548
- return path11.join(windowsSystemRoot(env2), "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
3659
+ return path12.join(windowsSystemRoot(env2), "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
3549
3660
  }
3550
3661
  function listProcessCreationTimes({ platform = process.platform, spawn: spawn5 = spawnSync5, env: env2 = process.env, warn = console.warn } = {}) {
3551
3662
  const map = /* @__PURE__ */ new Map();
@@ -3590,7 +3701,7 @@ function parsePosixPsLine(line) {
3590
3701
  function killProcessTree(pid, { platform = process.platform, spawn: spawn5 = spawnSync5, env: env2 = process.env } = {}) {
3591
3702
  if (!Number.isInteger(pid) || pid <= 0) return false;
3592
3703
  if (platform === "win32") {
3593
- const taskkill = path11.join(windowsSystemRoot(env2), "System32", "taskkill.exe");
3704
+ const taskkill = path12.join(windowsSystemRoot(env2), "System32", "taskkill.exe");
3594
3705
  const r = spawn5(taskkill, ["/PID", String(pid), "/T", "/F"], { windowsHide: true, stdio: "ignore", timeout: 15e3 });
3595
3706
  return !r.error && r.status === 0;
3596
3707
  }
@@ -4379,15 +4490,15 @@ var init_flat_token_usage = __esm({
4379
4490
 
4380
4491
  // ../../scripts/virtual-office/code-runner/codex-runner.mjs
4381
4492
  import { spawnSync as spawnSync6 } from "node:child_process";
4382
- import { existsSync as existsSync4 } from "node:fs";
4383
- import { win32 } from "node:path";
4493
+ import { existsSync as existsSync5 } from "node:fs";
4494
+ import { win32 as win322 } from "node:path";
4384
4495
  function isTruthyFlag2(value) {
4385
4496
  return ["1", "true", "yes", "on"].includes(String(value ?? "").trim().toLowerCase());
4386
4497
  }
4387
4498
  function resolveCodexBinary({
4388
4499
  env: env2 = process.env,
4389
4500
  platform = process.platform,
4390
- exists = existsSync4
4501
+ exists = existsSync5
4391
4502
  } = {}) {
4392
4503
  if (platform !== "win32") return "codex";
4393
4504
  const appData = String(env2.APPDATA || "").trim();
@@ -4395,7 +4506,7 @@ function resolveCodexBinary({
4395
4506
  const localAppData = String(env2.LOCALAPPDATA || "").trim();
4396
4507
  const candidates = [];
4397
4508
  if (appData) {
4398
- candidates.push(win32.join(
4509
+ candidates.push(win322.join(
4399
4510
  appData,
4400
4511
  "npm",
4401
4512
  "node_modules",
@@ -4411,11 +4522,11 @@ function resolveCodexBinary({
4411
4522
  ));
4412
4523
  }
4413
4524
  if (userProfile) {
4414
- candidates.push(win32.join(userProfile, ".local", "bin", "codex.exe"));
4415
- candidates.push(win32.join(userProfile, ".codex", "bin", "codex.exe"));
4525
+ candidates.push(win322.join(userProfile, ".local", "bin", "codex.exe"));
4526
+ candidates.push(win322.join(userProfile, ".codex", "bin", "codex.exe"));
4416
4527
  }
4417
4528
  if (localAppData) {
4418
- candidates.push(win32.join(localAppData, "Microsoft", "WindowsApps", "codex.exe"));
4529
+ candidates.push(win322.join(localAppData, "Microsoft", "WindowsApps", "codex.exe"));
4419
4530
  }
4420
4531
  const absolute = candidates.find((candidate) => exists(candidate));
4421
4532
  if (absolute) return absolute;
@@ -4764,6 +4875,188 @@ var init_cursor_runner = __esm({
4764
4875
  }
4765
4876
  });
4766
4877
 
4878
+ // ../../scripts/virtual-office/code-runner/ollama-agent-tools.mjs
4879
+ var MAX_READ_BYTES, MAX_WRITE_BYTES, TOOL_DEFS, TOOL_NAMES, READ_ONLY_TOOL_NAMES, READ_ONLY_TOOL_DEFS;
4880
+ var init_ollama_agent_tools = __esm({
4881
+ "../../scripts/virtual-office/code-runner/ollama-agent-tools.mjs"() {
4882
+ "use strict";
4883
+ MAX_READ_BYTES = 64 * 1024;
4884
+ MAX_WRITE_BYTES = 512 * 1024;
4885
+ TOOL_DEFS = [
4886
+ {
4887
+ type: "function",
4888
+ function: {
4889
+ name: "read_file",
4890
+ description: "Read a UTF-8 text file inside the working directory. Returns up to 64 KiB.",
4891
+ parameters: {
4892
+ type: "object",
4893
+ properties: {
4894
+ path: { type: "string", description: "File path relative to the working directory." }
4895
+ },
4896
+ required: ["path"]
4897
+ }
4898
+ }
4899
+ },
4900
+ {
4901
+ type: "function",
4902
+ function: {
4903
+ name: "list_files",
4904
+ description: "List entries in a directory inside the working directory (files and subdirs).",
4905
+ parameters: {
4906
+ type: "object",
4907
+ properties: {
4908
+ path: { type: "string", description: 'Directory path relative to the working directory. Default ".".' }
4909
+ }
4910
+ }
4911
+ }
4912
+ },
4913
+ {
4914
+ type: "function",
4915
+ function: {
4916
+ name: "write_file",
4917
+ description: "Create or overwrite a UTF-8 text file inside the working directory. Parent dirs are created.",
4918
+ parameters: {
4919
+ type: "object",
4920
+ properties: {
4921
+ path: { type: "string", description: "File path relative to the working directory." },
4922
+ content: { type: "string", description: "Full new file contents." }
4923
+ },
4924
+ required: ["path", "content"]
4925
+ }
4926
+ }
4927
+ }
4928
+ ];
4929
+ TOOL_NAMES = TOOL_DEFS.map((t) => t.function.name);
4930
+ READ_ONLY_TOOL_NAMES = Object.freeze(["read_file", "list_files"]);
4931
+ READ_ONLY_TOOL_DEFS = Object.freeze(
4932
+ TOOL_DEFS.filter((tool) => READ_ONLY_TOOL_NAMES.includes(tool.function.name))
4933
+ );
4934
+ }
4935
+ });
4936
+
4937
+ // ../../scripts/virtual-office/code-runner/ollama-agent-core.mjs
4938
+ function parseOllamaAgentEvent(line) {
4939
+ const trimmed = String(line || "").trim();
4940
+ if (!trimmed) return null;
4941
+ let evt;
4942
+ try {
4943
+ evt = JSON.parse(trimmed);
4944
+ } catch {
4945
+ return null;
4946
+ }
4947
+ if (!evt || typeof evt !== "object") return null;
4948
+ if (evt.type === "progress") {
4949
+ const text = String(evt.text || "").trim();
4950
+ return text ? { kind: "progress", text } : null;
4951
+ }
4952
+ if (evt.type === "tool") {
4953
+ const via = evt.recovered ? " (recovered from text)" : "";
4954
+ const label = `${evt.ok === false ? "tool failed" : "tool"}: ${evt.name}${evt.path ? ` ${evt.path}` : ""}${via}`;
4955
+ return { kind: "progress", text: label };
4956
+ }
4957
+ if (evt.type === "result") {
4958
+ const usage = evt.usage || null;
4959
+ const tokenUsage = usage ? { inputTokens: usage.inputTokens ?? null, outputTokens: usage.outputTokens ?? null, totalTokens: usage.totalTokens ?? null } : void 0;
4960
+ return {
4961
+ kind: "result",
4962
+ isError: Boolean(evt.isError),
4963
+ costUsd: 0,
4964
+ // sovereign local inference is free — no meter.
4965
+ summary: String(evt.summary || (evt.isError ? "local run failed" : "completed")),
4966
+ numTurns: Number.isInteger(evt.numTurns) ? evt.numTurns : null,
4967
+ ...tokenUsage ? { tokenUsage } : {},
4968
+ // Pass the sovereign receipt through to the daemon. Omitted entirely when
4969
+ // absent so the event shape is unchanged for every other transport.
4970
+ ...evt.receipt ? { receipt: evt.receipt } : {}
4971
+ };
4972
+ }
4973
+ return null;
4974
+ }
4975
+ var MAX_TURNS_DEFAULT, NUM_CTX_DEFAULT;
4976
+ var init_ollama_agent_core = __esm({
4977
+ "../../scripts/virtual-office/code-runner/ollama-agent-core.mjs"() {
4978
+ "use strict";
4979
+ init_ollama_agent_tools();
4980
+ init_ollama_agent_tools();
4981
+ MAX_TURNS_DEFAULT = 20;
4982
+ NUM_CTX_DEFAULT = 16384;
4983
+ }
4984
+ });
4985
+
4986
+ // ../../scripts/virtual-office/code-runner/ollama-native-transport.mjs
4987
+ import { fileURLToPath } from "node:url";
4988
+ import { dirname as dirname2, join as join2 } from "node:path";
4989
+ function resolveLocalNativeProfile(env2 = process.env) {
4990
+ const profile = String(env2.VO_CODE_RUNNER_LOCAL_PROFILE || "").trim().toLowerCase() || DEFAULT_LOCAL_NATIVE_PROFILE;
4991
+ if (!LOCAL_NATIVE_PROFILES.includes(profile)) {
4992
+ throw new Error(`local-model runner (native): unknown profile "${profile}" (coding|verification).`);
4993
+ }
4994
+ return profile;
4995
+ }
4996
+ function resolveLocalTransport(env2 = process.env) {
4997
+ return String(env2.VO_CODE_RUNNER_LOCAL_TRANSPORT || "").trim().toLowerCase() === "native" ? "native" : DEFAULT_LOCAL_TRANSPORT;
4998
+ }
4999
+ function ollamaAgentScriptPath() {
5000
+ return join2(dirname2(fileURLToPath(import.meta.url)), "ollama-agent.mjs");
5001
+ }
5002
+ function posIntOr(raw, fallback) {
5003
+ const n = Number(String(raw ?? "").trim());
5004
+ return Number.isInteger(n) && n > 0 ? n : fallback;
5005
+ }
5006
+ function buildOllamaAgentArgs({ model, numCtx, maxTurns, profile = DEFAULT_LOCAL_NATIVE_PROFILE } = {}) {
5007
+ return [
5008
+ ollamaAgentScriptPath(),
5009
+ "--model",
5010
+ String(model),
5011
+ "--profile",
5012
+ String(profile),
5013
+ "--num-ctx",
5014
+ String(posIntOr(numCtx, NUM_CTX_DEFAULT)),
5015
+ "--max-turns",
5016
+ String(posIntOr(maxTurns, MAX_TURNS_DEFAULT))
5017
+ ];
5018
+ }
5019
+ function buildLocalNativeArgs(opts = {}, env2 = process.env) {
5020
+ const provider = resolveLocalProvider(env2);
5021
+ if (provider !== "ollama") {
5022
+ throw new Error(
5023
+ `local-model runner (native): the native tool-loop executor speaks Ollama's /api/chat; provider "${provider}" is not supported on native transport. Set VO_CODE_RUNNER_LOCAL_PROVIDER=ollama, or use VO_CODE_RUNNER_LOCAL_TRANSPORT=codex for LM Studio.`
5024
+ );
5025
+ }
5026
+ const model = resolveLocalModel(env2);
5027
+ if (!model) {
5028
+ throw new Error(
5029
+ "local-model runner (native): set VO_CODE_RUNNER_LOCAL_MODEL to a coding model your Ollama server already has (e.g. qwen2.5-coder:7b). Refusing to run with no explicit model (fail-closed)."
5030
+ );
5031
+ }
5032
+ if (!isValidLocalModel(model)) {
5033
+ throw new Error(`local-model runner (native): "${model}" is not a valid local model id.`);
5034
+ }
5035
+ const baseUrl = resolveLocalBaseUrl(env2);
5036
+ if (baseUrl && !isLoopbackBaseUrl(baseUrl)) {
5037
+ throw new Error(
5038
+ "local-model runner (native): VO_CODE_RUNNER_LOCAL_BASE_URL must be a loopback http(s) URL (localhost / 127.0.0.1 / [::1]). Remote endpoints are refused \u2014 use the Model Firewall lanes."
5039
+ );
5040
+ }
5041
+ return buildOllamaAgentArgs({
5042
+ model,
5043
+ profile: resolveLocalNativeProfile(env2),
5044
+ numCtx: env2.VO_CODE_RUNNER_LOCAL_NUM_CTX,
5045
+ maxTurns: env2.VO_CODE_RUNNER_LOCAL_MAX_TURNS
5046
+ });
5047
+ }
5048
+ var DEFAULT_LOCAL_TRANSPORT, LOCAL_NATIVE_PROFILES, DEFAULT_LOCAL_NATIVE_PROFILE;
5049
+ var init_ollama_native_transport = __esm({
5050
+ "../../scripts/virtual-office/code-runner/ollama-native-transport.mjs"() {
5051
+ "use strict";
5052
+ init_ollama_agent_core();
5053
+ init_local_model_runner();
5054
+ DEFAULT_LOCAL_TRANSPORT = "codex";
5055
+ LOCAL_NATIVE_PROFILES = Object.freeze(["coding", "verification"]);
5056
+ DEFAULT_LOCAL_NATIVE_PROFILE = "coding";
5057
+ }
5058
+ });
5059
+
4767
5060
  // ../../scripts/virtual-office/code-runner/local-model-runner.mjs
4768
5061
  function resolveLocalProvider(env2 = process.env) {
4769
5062
  return String(env2.VO_CODE_RUNNER_LOCAL_PROVIDER || "").trim().toLowerCase() || DEFAULT_LOCAL_PROVIDER;
@@ -4837,20 +5130,37 @@ function buildLocalArgs(opts = {}, env2 = process.env) {
4837
5130
  }
4838
5131
  function applyLocalAuthEnv(baseEnv = process.env, configEnv = process.env) {
4839
5132
  const out = withAgentKey("local", baseEnv);
5133
+ for (const key of Object.keys(out)) {
5134
+ if (FORBIDDEN_LOCAL_CHILD_CREDENTIALS.has(key.toUpperCase())) delete out[key];
5135
+ }
4840
5136
  const baseUrl = resolveLocalBaseUrl(configEnv);
4841
5137
  if (baseUrl && isLoopbackBaseUrl(baseUrl) && resolveLocalProvider(configEnv) === "ollama" && !String(out.OLLAMA_HOST || "").trim()) {
4842
5138
  out.OLLAMA_HOST = baseUrl.replace(/\/+$/, "");
4843
5139
  }
4844
5140
  return out;
4845
5141
  }
4846
- var LOCAL_API_KEY_ENV, LOCAL_PROVIDERS, DEFAULT_LOCAL_PROVIDER, LOCAL_PROBE_URLS, remoteDesiredLocalModel, LOCAL_MODEL_RE, LocalModelRunner, localModelRunner;
5142
+ var LOCAL_API_KEY_ENV, FORBIDDEN_LOCAL_CHILD_CREDENTIALS, LOCAL_PROVIDERS, DEFAULT_LOCAL_PROVIDER, LOCAL_PROBE_URLS, remoteDesiredLocalModel, LOCAL_MODEL_RE, LocalModelRunner, localModelRunner;
4847
5143
  var init_local_model_runner = __esm({
4848
5144
  "../../scripts/virtual-office/code-runner/local-model-runner.mjs"() {
4849
5145
  "use strict";
4850
5146
  init_codex_runner();
4851
5147
  init_agent_key_store();
4852
5148
  init_agent_auth_tier();
5149
+ init_ollama_agent_core();
5150
+ init_ollama_native_transport();
4853
5151
  LOCAL_API_KEY_ENV = "VO_CODE_RUNNER_LOCAL_API_KEY";
5152
+ FORBIDDEN_LOCAL_CHILD_CREDENTIALS = /* @__PURE__ */ new Set([
5153
+ "ANTHROPIC_API_KEY",
5154
+ "AWS_ACCESS_KEY_ID",
5155
+ "AWS_SECRET_ACCESS_KEY",
5156
+ "AWS_SESSION_TOKEN",
5157
+ "FIREBASE_TOKEN",
5158
+ "GH_TOKEN",
5159
+ "GITHUB_TOKEN",
5160
+ "GOOGLE_API_KEY",
5161
+ "GOOGLE_APPLICATION_CREDENTIALS",
5162
+ "OPENAI_API_KEY"
5163
+ ]);
4854
5164
  LOCAL_PROVIDERS = ["ollama", "lmstudio"];
4855
5165
  DEFAULT_LOCAL_PROVIDER = "ollama";
4856
5166
  LOCAL_PROBE_URLS = {
@@ -4871,19 +5181,25 @@ var init_local_model_runner = __esm({
4871
5181
  this.env = env2;
4872
5182
  this.fetchImpl = fetchImpl;
4873
5183
  }
4874
- /** Codex is the transport binary; the model/endpoint are the user's. */
5184
+ /**
5185
+ * Transport binary. codex transport → the codex CLI. native transport →
5186
+ * this daemon's own node (process.execPath), which runs ollama-agent.mjs; no
5187
+ * external CLI is involved on the native path.
5188
+ */
4875
5189
  get binary() {
4876
- return this.resolveBinary();
5190
+ return resolveLocalTransport(this.env) === "native" ? process.execPath : this.resolveBinary();
4877
5191
  }
4878
5192
  buildArgs(opts = {}) {
4879
- return buildLocalArgs(opts, this.env);
5193
+ return resolveLocalTransport(this.env) === "native" ? buildLocalNativeArgs(opts, this.env) : buildLocalArgs(opts, this.env);
4880
5194
  }
4881
5195
  /**
4882
- * Codex JSONL events map identically, except a sovereign local model has no
4883
- * vendor bill. Codex omits total_cost_usd for OSS runs; turn that known fact
4884
- * into a measured zero at the producer so readers never have to guess.
5196
+ * native transport → parse the executor's own JSONL contract. codex transport
5197
+ * → codex JSONL maps identically, except a sovereign local model has no vendor
5198
+ * bill: codex omits total_cost_usd for OSS runs, so turn that known fact into a
5199
+ * measured zero at the producer. (The native parser already stamps costUsd:0.)
4885
5200
  */
4886
5201
  parseEvent(line) {
5202
+ if (resolveLocalTransport(this.env) === "native") return parseOllamaAgentEvent(line);
4887
5203
  const event = parseCodexEvent(line);
4888
5204
  return event?.kind === "result" ? { ...event, costUsd: 0 } : event;
4889
5205
  }
@@ -5244,9 +5560,9 @@ var init_rate_limit_detector_core = __esm({
5244
5560
 
5245
5561
  // ../../scripts/virtual-office/code-runner/rate-limit-resume-state.mjs
5246
5562
  import fsp10 from "node:fs/promises";
5247
- import path12 from "node:path";
5563
+ import path13 from "node:path";
5248
5564
  async function atomicWrite(file, content) {
5249
- await fsp10.mkdir(path12.dirname(file), { recursive: true });
5565
+ await fsp10.mkdir(path13.dirname(file), { recursive: true });
5250
5566
  const temp = `${file}.tmp-${process.pid}-${Date.now()}`;
5251
5567
  const handle = await fsp10.open(temp, "wx");
5252
5568
  try {
@@ -5307,7 +5623,7 @@ function writeResumeAttempts(file, store) {
5307
5623
  }
5308
5624
  async function acquireLock(lockFile, { now = Date.now, sleep: sleep3 = delay } = {}) {
5309
5625
  const deadline = now() + LOCK_WAIT_MS;
5310
- await fsp10.mkdir(path12.dirname(lockFile), { recursive: true });
5626
+ await fsp10.mkdir(path13.dirname(lockFile), { recursive: true });
5311
5627
  for (; ; ) {
5312
5628
  let handle;
5313
5629
  try {
@@ -5381,10 +5697,10 @@ var init_rate_limit_resume_state = __esm({
5381
5697
  });
5382
5698
 
5383
5699
  // ../../scripts/virtual-office/code-runner/rate-limit-resume.mjs
5384
- import { homedir as homedir2 } from "node:os";
5385
- import { join as join2 } from "node:path";
5700
+ import { homedir as homedir3 } from "node:os";
5701
+ import { join as join3 } from "node:path";
5386
5702
  function resumeQueuePath() {
5387
- return join2(homedir2(), ".claude", "resume-queue.jsonl");
5703
+ return join3(homedir3(), ".claude", "resume-queue.jsonl");
5388
5704
  }
5389
5705
  function buildResumeEntry({ task = {}, resumeAfter = null, summary = "", at } = {}) {
5390
5706
  return {
@@ -5585,8 +5901,8 @@ var init_auto_merge = __esm({
5585
5901
 
5586
5902
  // ../../scripts/virtual-office/code-runner/pr-overlap-gate.mjs
5587
5903
  import { spawnSync as spawnSync8 } from "node:child_process";
5588
- import { existsSync as existsSync5 } from "node:fs";
5589
- import { fileURLToPath } from "node:url";
5904
+ import { existsSync as existsSync6 } from "node:fs";
5905
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
5590
5906
  function stripCredentials(env2 = process.env) {
5591
5907
  const safe = { ...env2 };
5592
5908
  for (const key of CREDENTIAL_ENV_KEYS) delete safe[key];
@@ -5596,7 +5912,7 @@ function resolveOverlapScript({
5596
5912
  worktreeDir,
5597
5913
  trustedPath = null,
5598
5914
  trustedPaths = TRUSTED_OVERLAP_CANDIDATES,
5599
- existsFn = existsSync5,
5915
+ existsFn = existsSync6,
5600
5916
  joinFn = (dir) => `${dir}/scripts/ci/check-local-pr-overlap.mjs`
5601
5917
  } = {}) {
5602
5918
  const candidates = trustedPath ? [trustedPath] : trustedPaths;
@@ -5612,7 +5928,7 @@ var init_pr_overlap_gate = __esm({
5612
5928
  TRUSTED_OVERLAP_CANDIDATES = [
5613
5929
  new URL("../../ci/check-local-pr-overlap.mjs", import.meta.url),
5614
5930
  new URL("./ci/check-local-pr-overlap.js", import.meta.url)
5615
- ].map((candidate) => fileURLToPath(candidate));
5931
+ ].map((candidate) => fileURLToPath2(candidate));
5616
5932
  CREDENTIAL_ENV_KEYS = Object.freeze([
5617
5933
  "GH_TOKEN",
5618
5934
  "GITHUB_TOKEN",
@@ -5665,14 +5981,14 @@ function parsePorcelainZ(out) {
5665
5981
  for (let i = 0; i < tokens.length; i += 1) {
5666
5982
  const token2 = tokens[i];
5667
5983
  if (!token2) continue;
5668
- const path19 = token2.slice(3);
5669
- if (path19) files.push(path19);
5984
+ const path23 = token2.slice(3);
5985
+ if (path23) files.push(path23);
5670
5986
  if (token2[0] === "R" || token2[0] === "C") i += 1;
5671
5987
  }
5672
5988
  return files;
5673
5989
  }
5674
- function isAgentScratch(path19) {
5675
- const normalized = String(path19 || "");
5990
+ function isAgentScratch(path23) {
5991
+ const normalized = String(path23 || "");
5676
5992
  return SCRATCH_PATTERNS.some((pattern) => pattern.test(normalized));
5677
5993
  }
5678
5994
  var SCRATCH_PATTERNS;
@@ -5739,6 +6055,540 @@ var init_publish = __esm({
5739
6055
  }
5740
6056
  });
5741
6057
 
6058
+ // ../../scripts/virtual-office/test-gen/auto-tier.mjs
6059
+ function lower(s) {
6060
+ return typeof s === "string" ? s.toLowerCase() : "";
6061
+ }
6062
+ function isAlgoProduct(product) {
6063
+ return /^algo/i.test(String(product || ""));
6064
+ }
6065
+ function isAdminRoute(route) {
6066
+ return /(^|\/)admin(\/|$)/i.test(String(route || ""));
6067
+ }
6068
+ function importsMatch(imports, re) {
6069
+ return Array.isArray(imports) && imports.some((i) => re.test(String(i || "")));
6070
+ }
6071
+ function classifyTier(contract = {}) {
6072
+ const product = lower(contract.product);
6073
+ const callable = String(contract.callable || "");
6074
+ const route = String(contract.route || "");
6075
+ const imports = contract.imports;
6076
+ const ov = contract.tier_override;
6077
+ if (ov === 1 || ov === 2 || ov === 3 || ov === 4) {
6078
+ return { tier: ov, rationale: `operator override \u2192 Tier ${ov}`, tier_override: true };
6079
+ }
6080
+ const algo = isAlgoProduct(product);
6081
+ let tier;
6082
+ let why;
6083
+ const legalDomain = LEGAL_PRODUCTS.has(product);
6084
+ const hasGovernedSource = Boolean(contract.authoritative_source_url) || importsMatch(imports, LEGAL_IMPORT_RE);
6085
+ if (legalDomain && hasGovernedSource) {
6086
+ tier = 4;
6087
+ why = `legal-correctness domain (${product}) with a governed source \u2192 source-grounded Tier 4`;
6088
+ } else if (isAdminRoute(route) || MONEY_OR_SAFETY_RE.test(callable)) {
6089
+ tier = 3;
6090
+ why = isAdminRoute(route) ? "admin/safety-critical route \u2192 real-time monitor Tier 3" : `money-moving or grading callable (${callable}) \u2192 real-time monitor Tier 3`;
6091
+ } else if (importsMatch(imports, GENERATIVE_RE) || GENERATIVE_CALLABLE_RE.test(callable)) {
6092
+ const long = Number(contract.workflow_seconds) > 30;
6093
+ tier = long ? 3 : 2;
6094
+ why = `generative/AI feature \u2192 E2E with verified results${long ? " + monitor (long workflow) Tier 3" : " Tier 2"}`;
6095
+ } else if (callable || route) {
6096
+ tier = 2;
6097
+ why = "standard business logic \u2192 E2E with verified results (read-back) Tier 2";
6098
+ } else {
6099
+ tier = 1;
6100
+ why = "pure navigation/surface, no backend effect \u2192 surface smoke Tier 1";
6101
+ }
6102
+ if (algo && tier < 2) {
6103
+ return {
6104
+ tier: 2,
6105
+ rationale: `${why}; raised to Tier 2 (E2E floor: Algo* apps never auto-pick surface-smoke-only)`,
6106
+ tier_override: false
6107
+ };
6108
+ }
6109
+ return { tier, rationale: why, tier_override: false };
6110
+ }
6111
+ function tierLabel(tier) {
6112
+ return {
6113
+ 1: "Tier 1 \u2014 surface & navigation smoke",
6114
+ 2: "Tier 2 \u2014 E2E with verified results",
6115
+ 3: "Tier 3 \u2014 real-time monitor / sentinel",
6116
+ 4: "Tier 4 \u2014 source-grounded governed-fact verification"
6117
+ }[tier] || `Tier ${tier}`;
6118
+ }
6119
+ var LEGAL_PRODUCTS, LEGAL_IMPORT_RE, GENERATIVE_RE, GENERATIVE_CALLABLE_RE, MONEY_OR_SAFETY_RE;
6120
+ var init_auto_tier = __esm({
6121
+ "../../scripts/virtual-office/test-gen/auto-tier.mjs"() {
6122
+ "use strict";
6123
+ LEGAL_PRODUCTS = /* @__PURE__ */ new Set(["algotax", "algolaw", "algoteach", "algolegal"]);
6124
+ LEGAL_IMPORT_RE = /functions-core-(tax|law)|cornell|irs|\bstatute\b/i;
6125
+ GENERATIVE_RE = /@google\/generative-ai|@anthropic|anthropic|openai|@google\/genai|consensus|\bllm\b/i;
6126
+ GENERATIVE_CALLABLE_RE = /generate|summari[sz]e|\bai\b|consensus|draft|classify/i;
6127
+ MONEY_OR_SAFETY_RE = /grade|payment|transfer|trade|charge|refund|payout|disburse|withdraw|remit/i;
6128
+ }
6129
+ });
6130
+
6131
+ // ../../scripts/virtual-office/test-gen/dispatch.mjs
6132
+ import { spawnSync as spawnSync10 } from "node:child_process";
6133
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
6134
+ import path14 from "node:path";
6135
+ function buildGenerationPrompt({ contract = {}, tier }) {
6136
+ const label = tierLabel(tier);
6137
+ const target = contract.callable ? `the \`${contract.callable}\` callable` : contract.route ? `the \`${contract.route}\` route` : contract.feature_name || "the feature";
6138
+ return [
6139
+ `Write a ${label} test for ${target} in product "${contract.product || "unknown"}".`,
6140
+ contract.source_file ? `Source: ${contract.source_file}.` : "",
6141
+ contract.expected_behavior ? `Expected behavior: ${contract.expected_behavior}.` : "",
6142
+ "",
6143
+ `TIER REQUIREMENT \u2014 ${TIER_GUIDANCE[tier] || TIER_GUIDANCE[2]}`,
6144
+ "",
6145
+ "Follow the AlgoSuite test-honesty standard: NO fake-green (no bare toBeTruthy, no broad",
6146
+ "try/catch that swallows failures, no treating INVALID_ARGUMENT / empty / null / SKIP as a",
6147
+ "pass). Use a real fixture (smoke@algosuite.ai) and real data shapes.",
6148
+ "",
6149
+ "After writing the test, it will be GATED server-side: deterministic ratchets, then",
6150
+ "multi-model consensus that it proves the behavior with VERIFIED-CORRECT expected values.",
6151
+ "A test that does not pass BOTH gates will NOT ship \u2014 so make the assertions real and the",
6152
+ "expected values known-correct. Leave the test file UNCOMMITTED; the runner opens the PR."
6153
+ ].filter((l) => l !== "").join("\n");
6154
+ }
6155
+ function planTestGenDispatch({ contract = {} }) {
6156
+ const { tier, rationale } = classifyTier(contract);
6157
+ return {
6158
+ contract,
6159
+ tier,
6160
+ tier_label: tierLabel(tier),
6161
+ tier_rationale: rationale,
6162
+ generation_prompt: buildGenerationPrompt({ contract, tier })
6163
+ };
6164
+ }
6165
+ function buildTestGenTaskPrompt(dispatch = {}) {
6166
+ const contractJson = JSON.stringify(dispatch.contract ?? {});
6167
+ return `${TEST_GEN_MARKER} ${contractJson}
6168
+
6169
+ ${dispatch.generation_prompt ?? ""}`;
6170
+ }
6171
+ function pickNextTarget({ gaps = [] }) {
6172
+ if (!Array.isArray(gaps) || gaps.length === 0) return null;
6173
+ const ranked = [...gaps].sort((a, b) => {
6174
+ const al = LEGAL.has(String(a.product)) ? 0 : 1;
6175
+ const bl = LEGAL.has(String(b.product)) ? 0 : 1;
6176
+ return al - bl;
6177
+ });
6178
+ return ranked[0];
6179
+ }
6180
+ function resolveRepo(argv, env2) {
6181
+ const repoArg = argv.indexOf("--repo");
6182
+ if (repoArg >= 0 && argv[repoArg + 1]) return argv[repoArg + 1];
6183
+ if (env2.VO_TEST_GEN_REPO) return env2.VO_TEST_GEN_REPO;
6184
+ const remote = spawnSync10("git", ["remote", "get-url", "origin"], { encoding: "utf8" });
6185
+ if (remote.status === 0) {
6186
+ const m = String(remote.stdout).trim().match(/[:/]([^/]+\/[^/]+?)(?:\.git)?$/);
6187
+ if (m) return m[1];
6188
+ }
6189
+ return null;
6190
+ }
6191
+ async function main(argv = process.argv.slice(2)) {
6192
+ const countArg = argv.indexOf("--count");
6193
+ const count3 = countArg >= 0 ? Math.max(1, Number(argv[countArg + 1]) || 1) : 1;
6194
+ const enqueue = argv.includes("--enqueue");
6195
+ const here = path14.dirname(fileURLToPath3(import.meta.url));
6196
+ const scan = spawnSync10("node", [path14.join(here, "coverage-scan.mjs"), "--limit", String(count3 * 8)], {
6197
+ encoding: "utf8",
6198
+ maxBuffer: 64 * 1024 * 1024
6199
+ });
6200
+ if (scan.status !== 0) {
6201
+ console.error(`[dispatch] coverage-scan failed: ${scan.stderr || scan.stdout}`);
6202
+ process.exit(1);
6203
+ }
6204
+ let gaps = [];
6205
+ try {
6206
+ gaps = JSON.parse(scan.stdout).gaps || [];
6207
+ } catch (err) {
6208
+ console.error(`[dispatch] could not parse scan output: ${err.message}`);
6209
+ process.exit(1);
6210
+ }
6211
+ const dispatches = [];
6212
+ const seen = /* @__PURE__ */ new Set();
6213
+ let pool = gaps;
6214
+ while (dispatches.length < count3 && pool.length > 0) {
6215
+ const target = pickNextTarget({ gaps: pool });
6216
+ if (!target) break;
6217
+ const key = `${target.product}:${target.callable}`;
6218
+ if (!seen.has(key)) {
6219
+ seen.add(key);
6220
+ dispatches.push(planTestGenDispatch({ contract: target }));
6221
+ }
6222
+ pool = pool.filter((g) => `${g.product}:${g.callable}` !== key);
6223
+ }
6224
+ if (!enqueue) {
6225
+ console.log(JSON.stringify({ requested: count3, emitted: dispatches.length, dispatches }, null, 2));
6226
+ return;
6227
+ }
6228
+ const repo = resolveRepo(argv, process.env);
6229
+ if (!repo) {
6230
+ console.error("[dispatch] --enqueue needs a repo: pass --repo owner/name or set VO_TEST_GEN_REPO (could not derive from git origin)");
6231
+ process.exit(1);
6232
+ }
6233
+ const { createControlPlaneClient: createControlPlaneClient2 } = await Promise.resolve().then(() => (init_control_plane_client(), control_plane_client_exports));
6234
+ const client = createControlPlaneClient2({ env: process.env });
6235
+ const enqueued = [];
6236
+ for (const d of dispatches) {
6237
+ const task = await client.enqueueCodeTask({
6238
+ repo,
6239
+ prompt: buildTestGenTaskPrompt(d),
6240
+ max_turns: 40
6241
+ });
6242
+ const taskId = task && task.code_task_id ? task.code_task_id : "(unknown)";
6243
+ enqueued.push({ task_id: taskId, product: d.contract.product, callable: d.contract.callable, tier: d.tier_label });
6244
+ console.error(`[dispatch] enqueued ${d.tier_label} test-gen for ${d.contract.product}:${d.contract.callable} \u2192 task ${taskId}`);
6245
+ }
6246
+ console.log(JSON.stringify({ requested: count3, enqueued: enqueued.length, repo, tasks: enqueued }, null, 2));
6247
+ }
6248
+ var TEST_GEN_MARKER, TIER_GUIDANCE, LEGAL, invokedDirectly;
6249
+ var init_dispatch = __esm({
6250
+ "../../scripts/virtual-office/test-gen/dispatch.mjs"() {
6251
+ "use strict";
6252
+ init_auto_tier();
6253
+ TEST_GEN_MARKER = "[VO-TEST-GEN]";
6254
+ TIER_GUIDANCE = {
6255
+ 1: "Surface/navigation smoke: assert the surface renders and the key controls are present. No backend effect needed.",
6256
+ 2: "E2E with VERIFIED RESULTS: drive the real user/callable workflow, then READ BACK the produced artifact and assert KNOWN-CORRECT expected values (not just a 200 / truthiness).",
6257
+ 3: "Real-time monitor / sentinel: drive the workflow AND verify the state transitions + safety rails (no money moved / graded / admin action without the guard). Assert the post-conditions, not just the response.",
6258
+ 4: "Source-grounded governed-fact: assert expected values that are GROUNDED in the cited authoritative source (the contract's authoritative_source_url). The fixture and the expected number/string must trace to that source."
6259
+ };
6260
+ LEGAL = /* @__PURE__ */ new Set(["algotax", "algolaw", "algoteach", "algolegal"]);
6261
+ invokedDirectly = process.argv[1] && fileURLToPath3(import.meta.url) === path14.resolve(process.argv[1]);
6262
+ if (invokedDirectly) {
6263
+ main().catch((err) => {
6264
+ console.error(`[dispatch] ${err.message}`);
6265
+ process.exit(1);
6266
+ });
6267
+ }
6268
+ }
6269
+ });
6270
+
6271
+ // ../../scripts/virtual-office/test-gen/executor.mjs
6272
+ function consensusQuestion(tier) {
6273
+ const tierAsk = tier >= 4 ? " For this governed-fact (Tier 4) test, the expected values MUST be grounded in the cited authoritative source." : tier >= 3 ? " For this safety-critical (Tier 3) test, it must verify state transitions and the safety rails, not just a 200." : "";
6274
+ return "Does this test PROVE the stated behavior with VERIFIED-CORRECT expected values (not fake-green)? It must exercise the real product path (no stub/mock that bypasses the feature) and assert known-correct values, not placeholders." + tierAsk;
6275
+ }
6276
+ function buildContractSummary(contract, tier) {
6277
+ const parts = [
6278
+ `Feature: ${contract.feature_name || contract.callable || contract.route || "unnamed"}`,
6279
+ `Product: ${contract.product || "unknown"}`,
6280
+ contract.route ? `Route: ${contract.route}` : "",
6281
+ contract.callable ? `Callable: ${contract.callable}` : "",
6282
+ `Auto-selected tier: ${tierLabel(tier)}`,
6283
+ contract.expected_behavior ? `Expected behavior: ${contract.expected_behavior}` : "",
6284
+ contract.authoritative_source_url ? `Authoritative source: ${contract.authoritative_source_url}` : ""
6285
+ ];
6286
+ return parts.filter(Boolean).join("\n");
6287
+ }
6288
+ async function gateGeneratedTest({ contract = {}, testSource = "", verify, taskId = "autotest" }) {
6289
+ if (typeof verify !== "function") throw new Error("gateGeneratedTest requires a `verify` transport");
6290
+ const { tier, rationale } = classifyTier(contract);
6291
+ const label = tierLabel(tier);
6292
+ const filename = typeof contract.test_filename === "string" ? contract.test_filename : void 0;
6293
+ const ratchets = await verify({
6294
+ task_id: taskId,
6295
+ gate_type: "ratchets",
6296
+ excerpt: testSource,
6297
+ ...filename ? { context: { filename } } : {}
6298
+ });
6299
+ if (!ratchets || ratchets.approved !== true) {
6300
+ return {
6301
+ ship: false,
6302
+ tier,
6303
+ tierLabel: label,
6304
+ stage: "ratchets",
6305
+ reason: ratchets?.reason || "ratchets gate did not approve",
6306
+ ratchets: ratchets || null,
6307
+ consensus: null
6308
+ };
6309
+ }
6310
+ const consensus = await verify({
6311
+ task_id: taskId,
6312
+ gate_type: CONSENSUS_GATE_TYPE,
6313
+ excerpt: `${buildContractSummary(contract, tier)}
6314
+
6315
+ --- TEST ---
6316
+ ${testSource}`,
6317
+ question: consensusQuestion(tier)
6318
+ });
6319
+ const consensusBlocks = consensus?.available === true && consensus?.approved !== true;
6320
+ if (consensusBlocks) {
6321
+ return {
6322
+ ship: false,
6323
+ tier,
6324
+ tierLabel: label,
6325
+ stage: "consensus",
6326
+ reason: consensus?.reason || "consensus did not confirm verified-correct expected values",
6327
+ ratchets,
6328
+ consensus
6329
+ };
6330
+ }
6331
+ return {
6332
+ ship: true,
6333
+ tier,
6334
+ tierLabel: label,
6335
+ stage: "passed",
6336
+ reason: consensus?.available === true ? `Passed ratchets + consensus (${label}). ${rationale}` : `Passed ratchets; consensus unavailable so not blocking (${label}). ${rationale}`,
6337
+ ratchets,
6338
+ consensus
6339
+ };
6340
+ }
6341
+ function makeHttpVerify({ baseUrl, token: token2, fetchImpl = fetch }) {
6342
+ if (!baseUrl) throw new Error("makeHttpVerify requires a baseUrl");
6343
+ const url = `${baseUrl.replace(/\/$/, "")}/api/v1/verify`;
6344
+ return async function httpVerify(req) {
6345
+ let res;
6346
+ try {
6347
+ res = await fetchImpl(url, {
6348
+ method: "POST",
6349
+ headers: { "Content-Type": "application/json", ...token2 ? { Authorization: `Bearer ${token2}` } : {} },
6350
+ body: JSON.stringify(req)
6351
+ });
6352
+ } catch (err) {
6353
+ return { ok: false, available: false, approved: false, reason: `transport error: ${err?.message || err}` };
6354
+ }
6355
+ let body = {};
6356
+ try {
6357
+ body = await res.json();
6358
+ } catch {
6359
+ }
6360
+ if (!res.ok) {
6361
+ return { ok: false, available: false, approved: false, reason: body?.error || `http ${res.status}` };
6362
+ }
6363
+ return body;
6364
+ };
6365
+ }
6366
+ var CONSENSUS_GATE_TYPE;
6367
+ var init_executor = __esm({
6368
+ "../../scripts/virtual-office/test-gen/executor.mjs"() {
6369
+ "use strict";
6370
+ init_auto_tier();
6371
+ CONSENSUS_GATE_TYPE = "test_correctness_consensus";
6372
+ }
6373
+ });
6374
+
6375
+ // ../../scripts/virtual-office/code-runner/test-gen-gate.mjs
6376
+ import fs7 from "node:fs";
6377
+ import path15 from "node:path";
6378
+ async function postFailed(client, id, message, result) {
6379
+ try {
6380
+ await client.postProgress(id, {
6381
+ status: "failed",
6382
+ message: String(message).slice(0, 1500),
6383
+ result
6384
+ });
6385
+ } catch {
6386
+ }
6387
+ }
6388
+ async function gateTestGenTaskOrFail({ client, id, task, files, worktreeDir, env: env2 = process.env, log: log2 = () => {
6389
+ }, verify: verifyInjected } = {}) {
6390
+ const prompt = String(task && task.prompt || "");
6391
+ if (!prompt.startsWith(TEST_GEN_MARKER)) return false;
6392
+ let contract;
6393
+ try {
6394
+ const after = prompt.slice(TEST_GEN_MARKER.length).trimStart();
6395
+ contract = JSON.parse(after.split("\n\n")[0]);
6396
+ } catch (err) {
6397
+ log2(`task ${id}: test-gen contract parse failed: ${err.message}`);
6398
+ await postFailed(client, id, `test-gen contract parse failed: ${err.message}`, "gate_contract_unparseable");
6399
+ return true;
6400
+ }
6401
+ const testFile = (files || []).find((f) => TEST_FILE_RE.test(f));
6402
+ if (!testFile) {
6403
+ await postFailed(client, id, "test-gen task produced no *.test.* file", "gate_no_test_file");
6404
+ return true;
6405
+ }
6406
+ const baseUrl = env2.VO_MOAT_PLANE_URL;
6407
+ if (!verifyInjected && !baseUrl) {
6408
+ log2(`task ${id}: VO_MOAT_PLANE_URL unset \u2014 skipping test-gen gate (fail-safe, ADR-002 opt-out)`);
6409
+ return false;
6410
+ }
6411
+ let testSource = "";
6412
+ try {
6413
+ testSource = fs7.readFileSync(path15.join(worktreeDir, testFile), "utf8");
6414
+ } catch (err) {
6415
+ await postFailed(client, id, `could not read generated test ${testFile}: ${err.message}`, "gate_test_unreadable");
6416
+ return true;
6417
+ }
6418
+ const verify = verifyInjected || makeHttpVerify({ baseUrl: String(baseUrl).replace(/\/$/, ""), token: env2.VO_MOAT_PLANE_TOKEN || "" });
6419
+ let verdict;
6420
+ try {
6421
+ verdict = await gateGeneratedTest({
6422
+ contract: { ...contract, test_filename: testFile },
6423
+ testSource,
6424
+ verify,
6425
+ taskId: id
6426
+ });
6427
+ } catch (err) {
6428
+ log2(`task ${id}: test-gen gate could not run: ${err.message}`);
6429
+ await postFailed(client, id, `test-gen gate could not run (${err.message}) \u2014 not shipping un-gated`, "gate_transport_error");
6430
+ return true;
6431
+ }
6432
+ if (!verdict.ship) {
6433
+ log2(`task ${id}: test-gen gate REJECTED at ${verdict.stage}`);
6434
+ await postFailed(
6435
+ client,
6436
+ id,
6437
+ `test-gen gate rejected (${verdict.stage}): ${verdict.reason || "did not pass ratchets + consensus"}`,
6438
+ "gate_rejected"
6439
+ );
6440
+ return true;
6441
+ }
6442
+ try {
6443
+ await client.postProgress(id, {
6444
+ message: `test-gen gate PASSED (${verdict.tierLabel || "tier"}): ${verdict.reason || "ratchets + consensus"}`
6445
+ });
6446
+ } catch {
6447
+ }
6448
+ return false;
6449
+ }
6450
+ var TEST_FILE_RE;
6451
+ var init_test_gen_gate = __esm({
6452
+ "../../scripts/virtual-office/code-runner/test-gen-gate.mjs"() {
6453
+ "use strict";
6454
+ init_dispatch();
6455
+ init_executor();
6456
+ TEST_FILE_RE = /\.(test|spec)\.(ts|tsx|mjs|js)$/;
6457
+ }
6458
+ });
6459
+
6460
+ // ../../scripts/virtual-office/code-runner/completion-gate.mjs
6461
+ import { execFile } from "node:child_process";
6462
+ import fs8 from "node:fs";
6463
+ import path16 from "node:path";
6464
+ function resolveCompletionGate(task) {
6465
+ const raw = task?.completion_gate;
6466
+ if (raw === void 0 || raw === null) return null;
6467
+ if (typeof raw !== "string" || raw.trim() === "") {
6468
+ return { invalid: true, reason: "completion_gate must be a non-empty string" };
6469
+ }
6470
+ const trimmed = raw.trim();
6471
+ if (trimmed.length > 500) {
6472
+ return { invalid: true, reason: "completion_gate exceeds 500 characters" };
6473
+ }
6474
+ if (SHELL_METACHARACTERS.test(trimmed)) {
6475
+ return { invalid: true, reason: "completion_gate contains shell metacharacters (no-shell contract: plain argv only, no pipes/redirects/quotes)" };
6476
+ }
6477
+ const [command, ...args] = trimmed.split(/\s+/);
6478
+ const allowedCommand = ALLOWED_GATE_COMMANDS.get(command);
6479
+ if (allowedCommand === void 0) {
6480
+ const shown = command.length > 40 ? `${command.slice(0, 40)}...` : command;
6481
+ return {
6482
+ invalid: true,
6483
+ reason: `completion_gate command ${shown} is not an allowed gate runner (${[...ALLOWED_GATE_COMMANDS.keys()].join(", ")})`
6484
+ };
6485
+ }
6486
+ return { argv: [allowedCommand, ...args] };
6487
+ }
6488
+ function boundedTail(text) {
6489
+ const s = String(text ?? "");
6490
+ return s.length <= COMPLETION_GATE_OUTPUT_CAP ? s : s.slice(s.length - COMPLETION_GATE_OUTPUT_CAP);
6491
+ }
6492
+ function workspaceFingerprint(worktreeDir, execFileImpl = execFile) {
6493
+ return new Promise((resolve2) => {
6494
+ execFileImpl("git", ["rev-parse", "HEAD^{tree}"], { cwd: worktreeDir }, (err, stdout) => {
6495
+ resolve2(err ? null : String(stdout).trim() || null);
6496
+ });
6497
+ });
6498
+ }
6499
+ function readState(worktreeDir) {
6500
+ try {
6501
+ return JSON.parse(fs8.readFileSync(path16.join(worktreeDir, COMPLETION_GATE_STATE_FILE), "utf8"));
6502
+ } catch {
6503
+ return null;
6504
+ }
6505
+ }
6506
+ function writeState(worktreeDir, state) {
6507
+ try {
6508
+ fs8.writeFileSync(path16.join(worktreeDir, COMPLETION_GATE_STATE_FILE), `${JSON.stringify(state)}
6509
+ `, "utf8");
6510
+ } catch {
6511
+ }
6512
+ }
6513
+ function runGateCommand({ argv, worktreeDir, timeoutMs = COMPLETION_GATE_TIMEOUT_MS, execFileImpl = execFile }) {
6514
+ return new Promise((resolve2) => {
6515
+ const command = ALLOWED_GATE_COMMANDS.get(argv?.[0]);
6516
+ if (command === void 0) {
6517
+ return resolve2({
6518
+ exitCode: 1,
6519
+ output: `completion_gate executable is not an allowed gate runner (${[...ALLOWED_GATE_COMMANDS.keys()].join(", ")})`,
6520
+ timedOut: false
6521
+ });
6522
+ }
6523
+ execFileImpl(
6524
+ command,
6525
+ argv.slice(1),
6526
+ { cwd: worktreeDir, timeout: timeoutMs, maxBuffer: 16 * 1024 * 1024, windowsHide: true },
6527
+ (err, stdout, stderr) => {
6528
+ const output = boundedTail(`${stdout ?? ""}
6529
+ ${stderr ?? ""}`.trim());
6530
+ if (!err) return resolve2({ exitCode: 0, output, timedOut: false });
6531
+ const timedOut = err.killed === true || err.signal === "SIGTERM";
6532
+ const exitCode = typeof err.code === "number" ? err.code : 1;
6533
+ resolve2({ exitCode, output: output || boundedTail(err.message), timedOut });
6534
+ }
6535
+ );
6536
+ });
6537
+ }
6538
+ async function postFailed2(client, id, message, result) {
6539
+ try {
6540
+ await client.postProgress(id, { status: "failed", message: String(message).slice(0, 1500), result });
6541
+ } catch {
6542
+ }
6543
+ }
6544
+ function messageExcerpt(output) {
6545
+ const s = String(output ?? "");
6546
+ return s.length > 1200 ? `...${s.slice(-1200)}` : s;
6547
+ }
6548
+ async function enforceCompletionGateOrFail({ client, id, task, worktreeDir, log: log2 = () => {
6549
+ }, execFileImpl = execFile } = {}) {
6550
+ const resolved = resolveCompletionGate(task);
6551
+ if (resolved === null) return false;
6552
+ if (resolved.invalid) {
6553
+ log2(`task ${id}: completion_gate invalid \u2014 ${resolved.reason}`);
6554
+ await postFailed2(client, id, `completion_gate invalid (${resolved.reason}) \u2014 failing closed, not publishing`, "completion_gate_invalid");
6555
+ return true;
6556
+ }
6557
+ const fingerprint = await workspaceFingerprint(worktreeDir, execFileImpl);
6558
+ const cached2 = readState(worktreeDir);
6559
+ if (fingerprint && cached2 && cached2.fingerprint === fingerprint && cached2.exitCode !== 0) {
6560
+ log2(`task ${id}: completion gate skip-on-unchanged (tree ${fingerprint.slice(0, 12)}) \u2014 reusing recorded failure exit ${cached2.exitCode}`);
6561
+ await postFailed2(client, id, `completion gate '${task.completion_gate}' previously failed (exit ${cached2.exitCode}) and the workspace is unchanged:
6562
+ ${messageExcerpt(cached2.output)}`, "completion_gate_failed");
6563
+ return true;
6564
+ }
6565
+ log2(`task ${id}: running completion gate: ${resolved.argv.join(" ")}`);
6566
+ const outcome = await runGateCommand({ argv: resolved.argv, worktreeDir, execFileImpl });
6567
+ if (fingerprint) writeState(worktreeDir, { fingerprint, exitCode: outcome.exitCode, output: outcome.output, at: (/* @__PURE__ */ new Date()).toISOString() });
6568
+ if (outcome.exitCode === 0) {
6569
+ log2(`task ${id}: completion gate PASSED`);
6570
+ return false;
6571
+ }
6572
+ const kind = outcome.timedOut ? `timed out after ${COMPLETION_GATE_TIMEOUT_MS}ms` : `exited ${outcome.exitCode}`;
6573
+ log2(`task ${id}: completion gate FAILED (${kind}) \u2014 not publishing`);
6574
+ await postFailed2(client, id, `completion gate '${task.completion_gate}' ${kind} \u2014 task may not claim completion:
6575
+ ${messageExcerpt(outcome.output)}`, "completion_gate_failed");
6576
+ return true;
6577
+ }
6578
+ var COMPLETION_GATE_TIMEOUT_MS, COMPLETION_GATE_OUTPUT_CAP, COMPLETION_GATE_STATE_FILE, SHELL_METACHARACTERS, ALLOWED_GATE_COMMANDS;
6579
+ var init_completion_gate = __esm({
6580
+ "../../scripts/virtual-office/code-runner/completion-gate.mjs"() {
6581
+ "use strict";
6582
+ COMPLETION_GATE_TIMEOUT_MS = 5 * 6e4;
6583
+ COMPLETION_GATE_OUTPUT_CAP = 8 * 1024;
6584
+ COMPLETION_GATE_STATE_FILE = ".vo-completion-gate-state.json";
6585
+ SHELL_METACHARACTERS = /[|&;<>$`(){}[\]*?~#\\'"]/;
6586
+ ALLOWED_GATE_COMMANDS = new Map(
6587
+ ["pnpm", "npm", "yarn", "node", "git", "make", "python", "python3", "cargo", "go"].map((name) => [name, name])
6588
+ );
6589
+ }
6590
+ });
6591
+
5742
6592
  // ../../scripts/virtual-office/code-runner/process-runner.mjs
5743
6593
  import { spawn as spawn3 } from "node:child_process";
5744
6594
  function buildStepLabel(cmd, args = []) {
@@ -6232,8 +7082,8 @@ var init_publish_async = __esm({
6232
7082
 
6233
7083
  // ../../scripts/virtual-office/code-runner/skill-catalog.mjs
6234
7084
  import { readdirSync as readdirSync2, readFileSync as readFileSync3, statSync } from "node:fs";
6235
- import { dirname as dirname2, join as join3 } from "node:path";
6236
- import { fileURLToPath as fileURLToPath2 } from "node:url";
7085
+ import { dirname as dirname3, join as join4 } from "node:path";
7086
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
6237
7087
  function parseFrontmatterNameDescription(raw) {
6238
7088
  const text = String(raw).replace(/\r\n/g, "\n");
6239
7089
  if (!text.startsWith("---\n")) return null;
@@ -6252,15 +7102,15 @@ function parseFrontmatterNameDescription(raw) {
6252
7102
  return name && description ? { name, description } : null;
6253
7103
  }
6254
7104
  function resolveDefaultRepoRoot() {
6255
- const starts = [dirname2(fileURLToPath2(import.meta.url)), process.cwd()];
7105
+ const starts = [dirname3(fileURLToPath4(import.meta.url)), process.cwd()];
6256
7106
  for (const start of starts) {
6257
7107
  let dir = start;
6258
7108
  for (let i = 0; i < 8; i += 1) {
6259
7109
  try {
6260
- if (statSync(join3(dir, ".claude", "skills")).isDirectory()) return dir;
7110
+ if (statSync(join4(dir, ".claude", "skills")).isDirectory()) return dir;
6261
7111
  } catch {
6262
7112
  }
6263
- const parent = dirname2(dir);
7113
+ const parent = dirname3(dir);
6264
7114
  if (parent === dir) break;
6265
7115
  dir = parent;
6266
7116
  }
@@ -6269,14 +7119,14 @@ function resolveDefaultRepoRoot() {
6269
7119
  }
6270
7120
  function loadSkillCatalog({ repoRoot: repoRoot2 = resolveDefaultRepoRoot() } = {}) {
6271
7121
  try {
6272
- const skillsDir = join3(repoRoot2, ".claude", "skills");
7122
+ const skillsDir = join4(repoRoot2, ".claude", "skills");
6273
7123
  const catalog = [];
6274
7124
  for (const entry of readdirSync2(skillsDir)) {
6275
- const dir = join3(skillsDir, entry);
7125
+ const dir = join4(skillsDir, entry);
6276
7126
  try {
6277
7127
  if (!statSync(dir).isDirectory()) continue;
6278
7128
  const parsed = parseFrontmatterNameDescription(
6279
- readFileSync3(join3(dir, "SKILL.md"), "utf8")
7129
+ readFileSync3(join4(dir, "SKILL.md"), "utf8")
6280
7130
  );
6281
7131
  if (parsed) catalog.push(parsed);
6282
7132
  } catch {
@@ -6505,10 +7355,10 @@ var init_task_prompt = __esm({
6505
7355
  });
6506
7356
 
6507
7357
  // ../../scripts/virtual-office/code-runner/task-attachments.mjs
6508
- import { createHash as createHash3, randomUUID } from "node:crypto";
7358
+ import { createHash as createHash3, randomUUID as randomUUID2 } from "node:crypto";
6509
7359
  import { chmod, mkdir, mkdtemp, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
6510
7360
  import os2 from "node:os";
6511
- import path13 from "node:path";
7361
+ import path17 from "node:path";
6512
7362
  function safeTaskToken(taskId) {
6513
7363
  return String(taskId || "task").replace(/[^0-9A-Za-z_-]/gu, "_").slice(0, 48) || "task";
6514
7364
  }
@@ -6518,25 +7368,25 @@ function sanitizeTaskAttachmentName(name, index = 0) {
6518
7368
  return `${String(index + 1).padStart(2, "0")}-${normalized}`;
6519
7369
  }
6520
7370
  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)) {
7371
+ const resolvedDirectory = path17.resolve(directory);
7372
+ const resolvedRoot = path17.resolve(tempRoot);
7373
+ if (path17.dirname(resolvedDirectory) !== resolvedRoot || !path17.basename(resolvedDirectory).startsWith(DIRECTORY_PREFIX)) {
6524
7374
  throw new Error("refusing to clean an unverified task-attachment directory");
6525
7375
  }
6526
7376
  return resolvedDirectory;
6527
7377
  }
6528
7378
  async function createAttachmentDirectory(taskId, tempRoot) {
6529
- const root = path13.resolve(tempRoot);
7379
+ const root = path17.resolve(tempRoot);
6530
7380
  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 });
7381
+ const directory = await mkdtemp(path17.join(root, `${DIRECTORY_PREFIX}${safeTaskToken(taskId)}-`));
7382
+ const marker = JSON.stringify({ owner: MARKER_OWNER, token: randomUUID2(), directory: path17.basename(directory), created_at: (/* @__PURE__ */ new Date()).toISOString() });
7383
+ await writeFile(path17.join(directory, MARKER_FILE), marker, { encoding: "utf8", mode: 384 });
6534
7384
  return { directory, marker, tempRoot: root };
6535
7385
  }
6536
7386
  async function cleanupGeneratedDirectory(state) {
6537
7387
  if (!state || state.cleaned) return;
6538
7388
  const directory = assertGeneratedDirectory(state.directory, state.tempRoot);
6539
- const marker = await readFile(path13.join(directory, MARKER_FILE), "utf8").catch(() => "");
7389
+ const marker = await readFile(path17.join(directory, MARKER_FILE), "utf8").catch(() => "");
6540
7390
  if (marker !== state.marker) throw new Error("refusing to clean a task-attachment directory without its exact marker");
6541
7391
  await rm(directory, { recursive: true, force: true });
6542
7392
  state.cleaned = true;
@@ -6555,7 +7405,7 @@ async function sweepStaleTaskAttachmentDirectories({
6555
7405
  now = Date.now(),
6556
7406
  maxAgeMs = DEFAULT_STALE_AGE_MS
6557
7407
  } = {}) {
6558
- const root = path13.resolve(tempRoot);
7408
+ const root = path17.resolve(tempRoot);
6559
7409
  if (!Number.isFinite(maxAgeMs) || maxAgeMs <= 0) throw new Error("stale attachment age must be positive");
6560
7410
  const entries = await readdir(root, { withFileTypes: true }).catch((error) => {
6561
7411
  if (error?.code === "ENOENT") return [];
@@ -6564,8 +7414,8 @@ async function sweepStaleTaskAttachmentDirectories({
6564
7414
  let removed = 0;
6565
7415
  for (const entry of entries) {
6566
7416
  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(() => "");
7417
+ const directory = assertGeneratedDirectory(path17.join(root, entry.name), root);
7418
+ const markerRaw = await readFile(path17.join(directory, MARKER_FILE), "utf8").catch(() => "");
6569
7419
  const marker = parseOwnedMarker(markerRaw, entry.name);
6570
7420
  if (!marker) continue;
6571
7421
  const directoryStat = await stat(directory);
@@ -6608,10 +7458,10 @@ async function materializeTaskAttachments(client, task, { tempRoot = os2.tmpdir(
6608
7458
  const sha256 = createHash3("sha256").update(content).digest("hex");
6609
7459
  if (sha256 !== ref.sha256) throw new Error(`attachment ${ref.attachment_id} sha256 mismatch`);
6610
7460
  const name = sanitizeTaskAttachmentName(ref.name, index);
6611
- const filePath = path13.join(state.directory, name);
7461
+ const filePath = path17.join(state.directory, name);
6612
7462
  await writeFile(filePath, content, { flag: "wx", mode: 384 });
6613
7463
  await chmod(filePath, 384);
6614
- files.push({ attachmentId: ref.attachment_id, name, mime: ref.mime, sizeBytes: ref.size_bytes, sha256, path: path13.resolve(filePath) });
7464
+ files.push({ attachmentId: ref.attachment_id, name, mime: ref.mime, sizeBytes: ref.size_bytes, sha256, path: path17.resolve(filePath) });
6615
7465
  }
6616
7466
  return { directory: state.directory, files, manifestMarkdown: buildManifest(files), cleanup: () => cleanupGeneratedDirectory(state) };
6617
7467
  } catch (error) {
@@ -6633,8 +7483,8 @@ var init_task_attachments = __esm({
6633
7483
  });
6634
7484
 
6635
7485
  // ../../scripts/virtual-office/code-runner/session-spool-forwarder.mjs
6636
- import { homedir as homedir3 } from "node:os";
6637
- import { join as join4 } from "node:path";
7486
+ import { homedir as homedir4 } from "node:os";
7487
+ import { join as join5 } from "node:path";
6638
7488
  import { readdir as readdir2, readFile as readFile2, unlink, writeFile as writeFile2 } from "node:fs/promises";
6639
7489
  import { createHash as createHash4 } from "node:crypto";
6640
7490
  function deriveUuid(seed) {
@@ -6666,18 +7516,18 @@ async function readSpool(spoolDir = SPOOL_DIR) {
6666
7516
  for (const f of files) {
6667
7517
  if (!f.endsWith(".json")) continue;
6668
7518
  try {
6669
- const record = JSON.parse(await readFile2(join4(spoolDir, f), "utf8"));
7519
+ const record = JSON.parse(await readFile2(join5(spoolDir, f), "utf8"));
6670
7520
  if (record && typeof record.session_key === "string") {
6671
- out.push({ full: join4(spoolDir, f), record });
7521
+ out.push({ full: join5(spoolDir, f), record });
6672
7522
  }
6673
7523
  } catch {
6674
7524
  }
6675
7525
  }
6676
7526
  return out;
6677
7527
  }
6678
- async function readCloudMap(path19) {
7528
+ async function readCloudMap(path23) {
6679
7529
  try {
6680
- return JSON.parse(await readFile2(path19, "utf8"));
7530
+ return JSON.parse(await readFile2(path23, "utf8"));
6681
7531
  } catch {
6682
7532
  return {};
6683
7533
  }
@@ -6750,8 +7600,8 @@ var SPOOL_DIR, CLOUD_MAP_FILE, STALE_MS, ACTIVE_SILENCE_MS;
6750
7600
  var init_session_spool_forwarder = __esm({
6751
7601
  "../../scripts/virtual-office/code-runner/session-spool-forwarder.mjs"() {
6752
7602
  "use strict";
6753
- SPOOL_DIR = join4(homedir3(), ".vo", "session-spool");
6754
- CLOUD_MAP_FILE = join4(homedir3(), ".vo", "session-cloud-map.json");
7603
+ SPOOL_DIR = join5(homedir4(), ".vo", "session-spool");
7604
+ CLOUD_MAP_FILE = join5(homedir4(), ".vo", "session-cloud-map.json");
6755
7605
  STALE_MS = 60 * 60 * 1e3;
6756
7606
  ACTIVE_SILENCE_MS = 10 * 60 * 1e3;
6757
7607
  }
@@ -6822,7 +7672,7 @@ var init_rate_limit_resume_scheduler_core = __esm({
6822
7672
  });
6823
7673
 
6824
7674
  // ../../scripts/virtual-office/code-runner/rate-limit-resume-scheduler.mjs
6825
- import { dirname as dirname3, join as join5, resolve } from "node:path";
7675
+ import { dirname as dirname4, join as join6, resolve } from "node:path";
6826
7676
  function defaultLog(message) {
6827
7677
  console.log(`[rate-limit-scheduler ${(/* @__PURE__ */ new Date()).toISOString()}] ${message}`);
6828
7678
  }
@@ -6904,7 +7754,7 @@ async function runLockedScheduler({
6904
7754
  async function runScheduler({
6905
7755
  env: env2 = process.env,
6906
7756
  queuePath = resumeQueuePath(),
6907
- attemptsPath = join5(dirname3(queuePath), "resume-attempts.json"),
7757
+ attemptsPath = join6(dirname4(queuePath), "resume-attempts.json"),
6908
7758
  client,
6909
7759
  now,
6910
7760
  log: log2 = defaultLog
@@ -7196,7 +8046,7 @@ var init_runner_capacity = __esm({
7196
8046
  });
7197
8047
 
7198
8048
  // ../../scripts/virtual-office/code-runner/agent-auth-probe-process.mjs
7199
- import { fileURLToPath as fileURLToPath3 } from "node:url";
8049
+ import { fileURLToPath as fileURLToPath5 } from "node:url";
7200
8050
  async function probeAgentInChild(agent, timeoutMs) {
7201
8051
  const stdout = await runProcess2(process.execPath, [probeCli, agent], {
7202
8052
  timeout: timeoutMs,
@@ -7209,7 +8059,7 @@ var init_agent_auth_probe_process = __esm({
7209
8059
  "../../scripts/virtual-office/code-runner/agent-auth-probe-process.mjs"() {
7210
8060
  "use strict";
7211
8061
  init_process_runner2();
7212
- probeCli = fileURLToPath3(new URL("./agent-auth-probe-cli.mjs", import.meta.url));
8062
+ probeCli = fileURLToPath5(new URL("./agent-auth-probe-cli.mjs", import.meta.url));
7213
8063
  }
7214
8064
  });
7215
8065
 
@@ -7434,7 +8284,7 @@ var init_local_model_remote_config = __esm({
7434
8284
 
7435
8285
  // ../../scripts/virtual-office/code-runner/account-usage/shared.mjs
7436
8286
  import crypto from "node:crypto";
7437
- import fs6 from "node:fs";
8287
+ import fs9 from "node:fs";
7438
8288
  function accountKey(agent, rawId) {
7439
8289
  const id = typeof rawId === "string" ? rawId.trim() : "";
7440
8290
  if (!id) return null;
@@ -7498,7 +8348,7 @@ var init_shared = __esm({
7498
8348
  };
7499
8349
  readJson = (p) => {
7500
8350
  try {
7501
- return JSON.parse(fs6.readFileSync(p, "utf8"));
8351
+ return JSON.parse(fs9.readFileSync(p, "utf8"));
7502
8352
  } catch {
7503
8353
  return null;
7504
8354
  }
@@ -7508,9 +8358,9 @@ var init_shared = __esm({
7508
8358
  });
7509
8359
 
7510
8360
  // ../../scripts/virtual-office/code-runner/account-usage/claude.mjs
7511
- import fs7 from "node:fs";
8361
+ import fs10 from "node:fs";
7512
8362
  import os3 from "node:os";
7513
- import path14 from "node:path";
8363
+ import path18 from "node:path";
7514
8364
  function fileCaptureTime(filePath, explicit, statFn) {
7515
8365
  if (typeof explicit === "string" && explicit) return explicit;
7516
8366
  try {
@@ -7524,7 +8374,7 @@ function usageBaseUrl(env2 = process.env) {
7524
8374
  return String(raw).replace(/\/+$/, "");
7525
8375
  }
7526
8376
  function readOAuthToken({ homeDir = os3.homedir(), read = readJson, now = Date.now() } = {}) {
7527
- const creds = read(path14.join(homeDir, ".claude", ".credentials.json"));
8377
+ const creds = read(path18.join(homeDir, ".claude", ".credentials.json"));
7528
8378
  const oauth = creds && typeof creds === "object" ? creds.claudeAiOauth : null;
7529
8379
  if (!oauth || typeof oauth !== "object") return null;
7530
8380
  const token2 = typeof oauth.accessToken === "string" ? oauth.accessToken.trim() : "";
@@ -7534,7 +8384,7 @@ function readOAuthToken({ homeDir = os3.homedir(), read = readJson, now = Date.n
7534
8384
  return token2;
7535
8385
  }
7536
8386
  function readAccountId({ homeDir = os3.homedir(), read = readJson } = {}) {
7537
- const cfg = read(path14.join(homeDir, ".claude.json"));
8387
+ const cfg = read(path18.join(homeDir, ".claude.json"));
7538
8388
  const account = cfg && typeof cfg === "object" ? cfg.oauthAccount : null;
7539
8389
  return account && typeof account.accountUuid === "string" ? account.accountUuid : null;
7540
8390
  }
@@ -7627,7 +8477,7 @@ async function readClaudeOAuthUsage({
7627
8477
  function readClaudeFileUsage({
7628
8478
  homeDir = os3.homedir(),
7629
8479
  read: rawRead = readJson,
7630
- statFn = fs7.statSync,
8480
+ statFn = fs10.statSync,
7631
8481
  now = () => Date.now()
7632
8482
  } = {}) {
7633
8483
  const read = (p) => {
@@ -7644,7 +8494,7 @@ function readClaudeFileUsage({
7644
8494
  if (age === null || age > MAX_FILE_AGE_MS) return null;
7645
8495
  return row;
7646
8496
  };
7647
- const statusPath = path14.join(homeDir, ".claude", "claude-usage.json");
8497
+ const statusPath = path18.join(homeDir, ".claude", "claude-usage.json");
7648
8498
  const status = read(statusPath);
7649
8499
  if (status && (status.seven_day || status.five_hour)) {
7650
8500
  const row = fresh(makeUsageRow({
@@ -7659,7 +8509,7 @@ function readClaudeFileUsage({
7659
8509
  }));
7660
8510
  if (row) return row;
7661
8511
  }
7662
- const weeklyPath = path14.join(homeDir, ".claude", "claude-weekly-usage.json");
8512
+ const weeklyPath = path18.join(homeDir, ".claude", "claude-weekly-usage.json");
7663
8513
  const weekly = read(weeklyPath);
7664
8514
  if (weekly) {
7665
8515
  const row = fresh(makeUsageRow({
@@ -8098,9 +8948,9 @@ var init_watcher_coordination = __esm({
8098
8948
  });
8099
8949
 
8100
8950
  // ../../scripts/virtual-office/code-runner/watcher-state.mjs
8101
- import { randomUUID as randomUUID2 } from "node:crypto";
8951
+ import { randomUUID as randomUUID3 } from "node:crypto";
8102
8952
  import { mkdir as mkdir2, open, readFile as readFile3, rename, unlink as unlink2 } from "node:fs/promises";
8103
- import { dirname as dirname4 } from "node:path";
8953
+ import { dirname as dirname5 } from "node:path";
8104
8954
  async function readWatcherState(stateFile) {
8105
8955
  let raw;
8106
8956
  try {
@@ -8116,9 +8966,9 @@ async function readWatcherState(stateFile) {
8116
8966
  return parsed;
8117
8967
  }
8118
8968
  async function writeWatcherState(stateFile, state) {
8119
- const directory = dirname4(stateFile);
8969
+ const directory = dirname5(stateFile);
8120
8970
  await mkdir2(directory, { recursive: true });
8121
- const temp = `${stateFile}.${process.pid}.${randomUUID2()}.tmp`;
8971
+ const temp = `${stateFile}.${process.pid}.${randomUUID3()}.tmp`;
8122
8972
  let handle;
8123
8973
  try {
8124
8974
  handle = await open(temp, "wx");
@@ -8329,7 +9179,7 @@ var init_pr_watcher_github = __esm({
8329
9179
  });
8330
9180
 
8331
9181
  // ../../scripts/virtual-office/code-runner/enqueue-autonomous-code-task.mjs
8332
- import { randomUUID as randomUUID3 } from "node:crypto";
9182
+ import { randomUUID as randomUUID4 } from "node:crypto";
8333
9183
  async function enqueueAutonomousCodeTask(client, task, log2 = () => {
8334
9184
  }) {
8335
9185
  const requestedBudgetUsd = task?.max_budget_usd;
@@ -8343,7 +9193,7 @@ async function enqueueAutonomousCodeTask(client, task, log2 = () => {
8343
9193
  if (typeof client?.reserveAutonomousDispatchBudget !== "function" || typeof client?.releaseAutonomousDispatchBudget !== "function") {
8344
9194
  throw new Error("autonomous dispatch admission client unavailable");
8345
9195
  }
8346
- const reservationId = randomUUID3();
9196
+ const reservationId = randomUUID4();
8347
9197
  const admission = await client.reserveAutonomousDispatchBudget({
8348
9198
  requestedBudgetUsd,
8349
9199
  reservationId,
@@ -8361,8 +9211,8 @@ var init_enqueue_autonomous_code_task = __esm({
8361
9211
  });
8362
9212
 
8363
9213
  // ../../scripts/virtual-office/code-runner/pr-watcher.mjs
8364
- import { homedir as homedir4 } from "node:os";
8365
- import { join as join6 } from "node:path";
9214
+ import { homedir as homedir5 } from "node:os";
9215
+ import { join as join7 } from "node:path";
8366
9216
  function parsePrCiStatus(view) {
8367
9217
  const state = (view && typeof view.state === "string" ? view.state : "UNKNOWN").toUpperCase();
8368
9218
  const rollup = view && Array.isArray(view.statusCheckRollup) ? view.statusCheckRollup : [];
@@ -8705,7 +9555,7 @@ var init_pr_watcher = __esm({
8705
9555
  init_watcher_state();
8706
9556
  init_superseded_pr_source();
8707
9557
  init_ci_fix_prompt();
8708
- DEFAULT_STATE_FILE = join6(homedir4(), ".vo", "dispatched-prs.json");
9558
+ DEFAULT_STATE_FILE = join7(homedir5(), ".vo", "dispatched-prs.json");
8709
9559
  FAIL_CONCLUSIONS = /* @__PURE__ */ new Set([
8710
9560
  "FAILURE",
8711
9561
  "TIMED_OUT",
@@ -8935,9 +9785,9 @@ function buildControlHandler({ getStatus, requestStop, allowedOrigin }) {
8935
9785
  res.end();
8936
9786
  return;
8937
9787
  }
8938
- const path19 = String(req.url || "").split("?")[0];
9788
+ const path23 = String(req.url || "").split("?")[0];
8939
9789
  res.setHeader("content-type", "application/json");
8940
- if (req.method === "GET" && path19 === "/status") {
9790
+ if (req.method === "GET" && path23 === "/status") {
8941
9791
  let status;
8942
9792
  try {
8943
9793
  status = getStatus();
@@ -8948,7 +9798,7 @@ function buildControlHandler({ getStatus, requestStop, allowedOrigin }) {
8948
9798
  res.end(JSON.stringify({ ok: true, ...status }));
8949
9799
  return;
8950
9800
  }
8951
- if (req.method === "POST" && path19 === "/stop") {
9801
+ if (req.method === "POST" && path23 === "/stop") {
8952
9802
  if (!isControlOriginAllowed(req.headers.origin, allowedOrigin) || !req.headers["x-vo-control"]) {
8953
9803
  res.statusCode = 403;
8954
9804
  res.end(JSON.stringify({ ok: false, error: "forbidden" }));
@@ -9111,18 +9961,35 @@ var init_effort_mode_config = __esm({
9111
9961
  });
9112
9962
 
9113
9963
  // ../../scripts/virtual-office/model-registry.mjs
9114
- import fs8 from "node:fs";
9115
- import path15 from "node:path";
9116
- import { fileURLToPath as fileURLToPath4 } from "node:url";
9964
+ import { randomUUID as randomUUID5 } from "node:crypto";
9965
+ import fs11 from "node:fs";
9966
+ import os4 from "node:os";
9967
+ import path19 from "node:path";
9968
+ import { fileURLToPath as fileURLToPath6 } from "node:url";
9969
+ function userCacheRoot() {
9970
+ try {
9971
+ const home = os4.homedir();
9972
+ if (home) return path19.join(home, ".claude");
9973
+ } catch {
9974
+ }
9975
+ return path19.join(os4.tmpdir(), `vo-model-registry-${randomUUID5()}`);
9976
+ }
9977
+ function resolveCacheBaseDir(env2 = process.env, moduleDir = __dirname) {
9978
+ if (env2.VO_MODEL_REGISTRY_CACHE_DIR) return env2.VO_MODEL_REGISTRY_CACHE_DIR;
9979
+ if (env2.VO_RUNNER_RUNTIME_ROOT) return env2.VO_RUNNER_RUNTIME_ROOT;
9980
+ const segments = moduleDir.split(path19.sep);
9981
+ const isRepoCheckout = segments.at(-1) === "virtual-office" && segments.at(-2) === "scripts";
9982
+ return isRepoCheckout ? path19.resolve(moduleDir, "..", "..") : userCacheRoot();
9983
+ }
9117
9984
  function uniqueModels(models = []) {
9118
9985
  return [...new Set(models.map((model) => String(model || "").trim()).filter(Boolean))];
9119
9986
  }
9120
9987
  function normalizeProvider(value = "") {
9121
- const lower = String(value || "").trim().toLowerCase();
9122
- if (lower.includes("anthropic")) return "anthropic";
9123
- if (lower.includes("openai")) return "openai";
9124
- if (lower.includes("google") || lower.includes("gemini")) return "google";
9125
- return lower;
9988
+ const lower2 = String(value || "").trim().toLowerCase();
9989
+ if (lower2.includes("anthropic")) return "anthropic";
9990
+ if (lower2.includes("openai")) return "openai";
9991
+ if (lower2.includes("google") || lower2.includes("gemini")) return "google";
9992
+ return lower2;
9126
9993
  }
9127
9994
  function stripProviderPrefix(id = "") {
9128
9995
  const raw = String(id || "").trim();
@@ -9164,15 +10031,15 @@ function normalizeCatalogModel(model = {}) {
9164
10031
  };
9165
10032
  }
9166
10033
  function parseVersionScore(id = "") {
9167
- const lower = String(id || "").toLowerCase();
9168
- const numbers = [...lower.matchAll(/\d+/g)].map((match) => Number(match[0])).filter(Number.isFinite);
10034
+ const lower2 = String(id || "").toLowerCase();
10035
+ const numbers = [...lower2.matchAll(/\d+/g)].map((match) => Number(match[0])).filter(Number.isFinite);
9169
10036
  let score = 0;
9170
10037
  for (let i = 0; i < numbers.length; i++) score += numbers[i] / Math.pow(1e3, i);
9171
- if (/opus|pro|flagship/.test(lower)) score += 10;
9172
- if (/sonnet/.test(lower)) score += 5;
9173
- if (/preview|latest/.test(lower)) score += 0.25;
9174
- if (/\[1m\]|\(1m\)/i.test(lower)) score += 1;
9175
- if (/mini|nano|haiku|lite/.test(lower)) score -= 20;
10038
+ if (/opus|pro|flagship/.test(lower2)) score += 10;
10039
+ if (/sonnet/.test(lower2)) score += 5;
10040
+ if (/preview|latest/.test(lower2)) score += 0.25;
10041
+ if (/\[1m\]|\(1m\)/i.test(lower2)) score += 1;
10042
+ if (/mini|nano|haiku|lite/.test(lower2)) score -= 20;
9176
10043
  return score;
9177
10044
  }
9178
10045
  function familyMatches(model, family) {
@@ -9224,9 +10091,9 @@ async function fetchGoogleModels(fetchImpl, env2 = process.env) {
9224
10091
  return (data?.models || []).map((model) => normalizeCatalogModel({ ...model, provider: "google", source: "google" })).filter(Boolean);
9225
10092
  }
9226
10093
  function readCache(cacheFile = DEFAULT_CACHE_FILE, nowMs = Date.now(), ttlMs = DEFAULT_TTL_MS3) {
9227
- if (!fs8.existsSync(cacheFile)) return null;
10094
+ if (!fs11.existsSync(cacheFile)) return null;
9228
10095
  try {
9229
- const parsed = JSON.parse(fs8.readFileSync(cacheFile, "utf-8"));
10096
+ const parsed = JSON.parse(fs11.readFileSync(cacheFile, "utf-8"));
9230
10097
  if (nowMs - Number(parsed.checkedAtMs || 0) > ttlMs) return null;
9231
10098
  if (!Array.isArray(parsed.models)) return null;
9232
10099
  return parsed;
@@ -9235,8 +10102,8 @@ function readCache(cacheFile = DEFAULT_CACHE_FILE, nowMs = Date.now(), ttlMs = D
9235
10102
  }
9236
10103
  }
9237
10104
  function writeCache(cacheFile = DEFAULT_CACHE_FILE, payload) {
9238
- fs8.mkdirSync(path15.dirname(cacheFile), { recursive: true });
9239
- fs8.writeFileSync(cacheFile, JSON.stringify(payload, null, 2));
10105
+ fs11.mkdirSync(path19.dirname(cacheFile), { recursive: true });
10106
+ fs11.writeFileSync(cacheFile, JSON.stringify(payload, null, 2));
9240
10107
  }
9241
10108
  async function fetchRegistryCatalog({
9242
10109
  fetchImpl = fetch,
@@ -9289,14 +10156,17 @@ async function resolveModelFamily(family, options = {}) {
9289
10156
  const resolved = selectBestFamilyModel(catalog.models || [], family);
9290
10157
  return resolved || def.fallbacks[0];
9291
10158
  }
9292
- var __dirname, ROOT, DEFAULT_CACHE_DIR, DEFAULT_CACHE_FILE, DEFAULT_TTL_MS3, ANTHROPIC_API_VERSION, FAMILY_DEFINITIONS, memoryCache;
10159
+ var __dirname, DEFAULT_CACHE_DIR, DEFAULT_CACHE_FILE, DEFAULT_TTL_MS3, ANTHROPIC_API_VERSION, FAMILY_DEFINITIONS, memoryCache;
9293
10160
  var init_model_registry = __esm({
9294
10161
  "../../scripts/virtual-office/model-registry.mjs"() {
9295
10162
  "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");
10163
+ __dirname = path19.dirname(fileURLToPath6(import.meta.url));
10164
+ DEFAULT_CACHE_DIR = path19.join(
10165
+ resolveCacheBaseDir(),
10166
+ ".virtual-office-cache",
10167
+ "model-registry"
10168
+ );
10169
+ DEFAULT_CACHE_FILE = path19.join(DEFAULT_CACHE_DIR, "catalog.json");
9300
10170
  DEFAULT_TTL_MS3 = 60 * 60 * 1e3;
9301
10171
  ANTHROPIC_API_VERSION = "2023-06-01";
9302
10172
  FAMILY_DEFINITIONS = {
@@ -9401,17 +10271,17 @@ function modelCompatibleWithAgent(agent, model) {
9401
10271
  if (!model) return true;
9402
10272
  return AGENT_MODEL_COMPATIBILITY[agent]?.(model) ?? false;
9403
10273
  }
9404
- function classifyTier(prompt) {
10274
+ function classifyTier2(prompt) {
9405
10275
  const text = String(prompt || "").trim();
9406
10276
  if (!text) return "mid";
9407
- const lower = text.toLowerCase();
10277
+ const lower2 = text.toLowerCase();
9408
10278
  if (/lint|format|typo|missing import|update deps|chore|maintenance|runner|daemon/.test(
9409
- lower
10279
+ lower2
9410
10280
  )) {
9411
10281
  return "cheap";
9412
10282
  }
9413
10283
  if (/generate roadmap|new feature|implement .* feature|major refactor|strategic/.test(
9414
- lower
10284
+ lower2
9415
10285
  ) || text.length > 1500) {
9416
10286
  return "best";
9417
10287
  }
@@ -9432,7 +10302,7 @@ async function resolveModelForTier(tier, { agent = DEFAULT_AGENT2, resolveModelF
9432
10302
  return fallbacks[effectiveTier];
9433
10303
  }
9434
10304
  async function resolveTaskModel(task, { agent = DEFAULT_AGENT2 } = {}) {
9435
- const tier = task.tier && task.tier !== "auto" ? task.tier : classifyTier(task.prompt);
10305
+ const tier = task.tier && task.tier !== "auto" ? task.tier : classifyTier2(task.prompt);
9436
10306
  const pinned = typeof task.model === "string" ? task.model.trim() : "";
9437
10307
  if (pinned && modelCompatibleWithAgent(normalizeAgent(agent), pinned)) {
9438
10308
  return { tier, model: pinned };
@@ -9904,8 +10774,8 @@ var init_classify_task = __esm({
9904
10774
 
9905
10775
  // ../../scripts/virtual-office/code-runner/auto-router/effort-policy.mjs
9906
10776
  import { readFileSync as readFileSync4 } from "node:fs";
9907
- import { homedir as homedir5 } from "node:os";
9908
- import { join as join7 } from "node:path";
10777
+ import { homedir as homedir6 } from "node:os";
10778
+ import { join as join8 } from "node:path";
9909
10779
  function difficultyToRung(difficulty, thresholds) {
9910
10780
  const b = thresholds.rungBounds;
9911
10781
  if (difficulty >= b.R5) return "R5";
@@ -9930,9 +10800,9 @@ function operatorTierToRung(tier, difficulty, thresholds) {
9930
10800
  if (tier === "best" && difficulty >= thresholds.rungBounds.R5) return "R5";
9931
10801
  return base;
9932
10802
  }
9933
- function readCodexModelsCache({ path: path19 = DEFAULT_CODEX_MODELS_CACHE, read = readFileSync4 } = {}) {
10803
+ function readCodexModelsCache({ path: path23 = DEFAULT_CODEX_MODELS_CACHE, read = readFileSync4 } = {}) {
9934
10804
  try {
9935
- const parsed = JSON.parse(read(path19, "utf8"));
10805
+ const parsed = JSON.parse(read(path23, "utf8"));
9936
10806
  return Array.isArray(parsed?.models) ? parsed : null;
9937
10807
  } catch {
9938
10808
  return null;
@@ -9982,7 +10852,7 @@ var init_effort_policy = __esm({
9982
10852
  init_meta_model_catalog();
9983
10853
  RUNG_ORDER = ["R1", "R2", "R3", "R4", "R5"];
9984
10854
  rungIndex = (rung) => RUNG_ORDER.indexOf(rung);
9985
- DEFAULT_CODEX_MODELS_CACHE = join7(homedir5(), ".codex", "models_cache.json");
10855
+ DEFAULT_CODEX_MODELS_CACHE = join8(homedir6(), ".codex", "models_cache.json");
9986
10856
  }
9987
10857
  });
9988
10858
 
@@ -10112,18 +10982,18 @@ var init_role_cost_shadow = __esm({
10112
10982
  });
10113
10983
 
10114
10984
  // ../../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";
10117
- import { join as join8, dirname as dirname5 } from "node:path";
10118
- import { fileURLToPath as fileURLToPath5 } from "node:url";
10985
+ import { readFileSync as readFileSync5, appendFileSync, mkdirSync as mkdirSync4 } from "node:fs";
10986
+ import { homedir as homedir7 } from "node:os";
10987
+ import { join as join9, dirname as dirname6 } from "node:path";
10988
+ import { fileURLToPath as fileURLToPath7 } from "node:url";
10119
10989
  function getAutoRouterMode(env2 = process.env) {
10120
10990
  const raw = String(env2.VO_CODE_RUNNER_AUTO_ROUTER || "").trim().toLowerCase();
10121
10991
  return MODES.has(raw) ? raw : "off";
10122
10992
  }
10123
10993
  function loadThresholds() {
10124
10994
  if (!cachedThresholds) {
10125
- const here = dirname5(fileURLToPath5(import.meta.url));
10126
- cachedThresholds = JSON.parse(readFileSync5(join8(here, "thresholds.json"), "utf8"));
10995
+ const here = dirname6(fileURLToPath7(import.meta.url));
10996
+ cachedThresholds = JSON.parse(readFileSync5(join9(here, "thresholds.json"), "utf8"));
10127
10997
  }
10128
10998
  return cachedThresholds;
10129
10999
  }
@@ -10189,15 +11059,15 @@ function formatDecisionReason(decision, maxLen = 480) {
10189
11059
  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
11060
  return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
10191
11061
  }
10192
- function appendDecisionFallback(decision, { path: path19 = DECISION_FALLBACK_PATH, append = appendFileSync, mkdir: mkdir4 = mkdirSync3, task, thresholds, roleCostInputs } = {}) {
11062
+ function appendDecisionFallback(decision, { path: path23 = DECISION_FALLBACK_PATH, append = appendFileSync, mkdir: mkdir4 = mkdirSync4, task, thresholds, roleCostInputs } = {}) {
10193
11063
  try {
10194
- mkdir4(dirname5(path19), { recursive: true });
10195
- append(path19, `${JSON.stringify(decision)}
11064
+ mkdir4(dirname6(path23), { recursive: true });
11065
+ append(path23, `${JSON.stringify(decision)}
10196
11066
  `, "utf8");
10197
11067
  if (isRouterDecision(decision)) {
10198
11068
  try {
10199
11069
  const records = buildShadowRecords({ decision, task, thresholds: thresholds || loadThresholds(), roleCostInputs });
10200
- for (const record of records) append(path19, `${JSON.stringify(record)}
11070
+ for (const record of records) append(path23, `${JSON.stringify(record)}
10201
11071
  `, "utf8");
10202
11072
  } catch {
10203
11073
  }
@@ -10215,7 +11085,7 @@ var init_auto_router = __esm({
10215
11085
  init_effort_policy();
10216
11086
  init_role_cost_shadow();
10217
11087
  ROUTER_VERSION = "0.1.0";
10218
- DECISION_FALLBACK_PATH = join8(homedir6(), ".claude", "vo-auto-router-decisions.jsonl");
11088
+ DECISION_FALLBACK_PATH = join9(homedir7(), ".claude", "vo-auto-router-decisions.jsonl");
10219
11089
  MODES = /* @__PURE__ */ new Set(["off", "shadow", "on"]);
10220
11090
  cachedThresholds = null;
10221
11091
  isRouterDecision = (d) => Boolean(d && typeof d === "object" && typeof d.taskClass === "string" && typeof d.confidence === "number");
@@ -11188,9 +12058,9 @@ var init_inference_task_runner = __esm({
11188
12058
  });
11189
12059
 
11190
12060
  // ../../scripts/virtual-office/code-runner/isolation-audit.mjs
11191
- import fs9 from "node:fs";
12061
+ import fs12 from "node:fs";
11192
12062
  import fsp11 from "node:fs/promises";
11193
- import path16 from "node:path";
12063
+ import path20 from "node:path";
11194
12064
  async function defaultRun(command, args, cwd, options = {}) {
11195
12065
  return runProcess2(command, args, { cwd, timeout: 6e4, ...options });
11196
12066
  }
@@ -11203,7 +12073,7 @@ async function canonicalRootForWorktree(worktreeDir, run) {
11203
12073
  "--path-format=absolute",
11204
12074
  "--git-common-dir"
11205
12075
  ])).trim();
11206
- const root = path16.dirname(commonDir);
12076
+ const root = path20.dirname(commonDir);
11207
12077
  return samePath2(root, worktreeDir) ? null : root;
11208
12078
  }
11209
12079
  async function snapshot(root, run) {
@@ -11245,21 +12115,21 @@ async function changedPaths(root, run) {
11245
12115
  }
11246
12116
  async function quarantineCanonicalWrites({ baseline, worktreeDir, taskId, run, now }) {
11247
12117
  const paths = await changedPaths(baseline.root, run);
11248
- const quarantineDir = path16.join(
11249
- path16.dirname(worktreeDir),
12118
+ const quarantineDir = path20.join(
12119
+ path20.dirname(worktreeDir),
11250
12120
  ".canonical-recovery",
11251
12121
  `${String(taskId || "unknown").replace(/[^a-z0-9-]/gi, "-")}-${now().toISOString().replace(/[:.]/g, "-")}`
11252
12122
  );
11253
12123
  await fsp11.mkdir(quarantineDir, { recursive: true });
11254
12124
  const patch = await git2(run, baseline.root, ["diff", "--binary", "HEAD"], { raw: true });
11255
- await fsp11.writeFile(path16.join(quarantineDir, "tracked.patch"), patch, "utf8");
12125
+ await fsp11.writeFile(path20.join(quarantineDir, "tracked.patch"), patch, "utf8");
11256
12126
  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 });
12127
+ const source = path20.join(baseline.root, relative);
12128
+ const target = path20.join(quarantineDir, "untracked", relative);
12129
+ await fsp11.mkdir(path20.dirname(target), { recursive: true });
11260
12130
  await fsp11.copyFile(source, target);
11261
12131
  }
11262
- await fsp11.writeFile(path16.join(quarantineDir, "manifest.json"), `${JSON.stringify({
12132
+ await fsp11.writeFile(path20.join(quarantineDir, "manifest.json"), `${JSON.stringify({
11263
12133
  taskId,
11264
12134
  canonicalRoot: baseline.root,
11265
12135
  canonicalHead: baseline.head,
@@ -11281,9 +12151,9 @@ async function restoreExactCanonicalPaths(baseline, evidence, run) {
11281
12151
  ]);
11282
12152
  }
11283
12153
  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;
12154
+ const target = path20.resolve(baseline.root, relative);
12155
+ const prefix = `${path20.resolve(baseline.root)}${path20.sep}`;
12156
+ if (!target.startsWith(prefix) || !fs12.existsSync(target)) continue;
11287
12157
  await fsp11.rm(target, { force: true });
11288
12158
  }
11289
12159
  }
@@ -11319,7 +12189,7 @@ var init_isolation_audit = __esm({
11319
12189
  init_process_runner2();
11320
12190
  splitZ2 = (value) => String(value || "").split("\0").map((item) => item.trim()).filter(Boolean);
11321
12191
  samePath2 = (left, right) => {
11322
- const [a, b] = [left, right].map((value) => path16.resolve(value));
12192
+ const [a, b] = [left, right].map((value) => path20.resolve(value));
11323
12193
  return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
11324
12194
  };
11325
12195
  }
@@ -11679,7 +12549,7 @@ var init_publication_outcome = __esm({
11679
12549
 
11680
12550
  // ../../scripts/virtual-office/code-runner/committed-scratch-cleanup.mjs
11681
12551
  import fsp12 from "node:fs/promises";
11682
- import path17 from "node:path";
12552
+ import path21 from "node:path";
11683
12553
  function defaultRun2(command, args, cwd, options = {}) {
11684
12554
  return runProcess2(command, args, { cwd, ...options });
11685
12555
  }
@@ -11687,13 +12557,13 @@ async function resolveSafeScratchTarget(worktreeDir, file) {
11687
12557
  if (!isAgentScratch(file)) {
11688
12558
  throw new Error(`refusing to remove non-scratch publication path: ${file}`);
11689
12559
  }
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)) {
12560
+ const root = path21.resolve(worktreeDir);
12561
+ const target = path21.resolve(root, file);
12562
+ const relative = path21.relative(root, target);
12563
+ if (!relative || relative.startsWith(`..${path21.sep}`) || path21.isAbsolute(relative)) {
11694
12564
  throw new Error(`refusing to remove publication scratch outside worktree: ${file}`);
11695
12565
  }
11696
- for (let cursor = target; cursor !== root; cursor = path17.dirname(cursor)) {
12566
+ for (let cursor = target; cursor !== root; cursor = path21.dirname(cursor)) {
11697
12567
  try {
11698
12568
  if ((await fsp12.lstat(cursor)).isSymbolicLink()) {
11699
12569
  throw new Error(`refusing to follow symlink while removing publication scratch: ${file}`);
@@ -11813,9 +12683,9 @@ var init_publication_scope = __esm({
11813
12683
  });
11814
12684
 
11815
12685
  // ../../scripts/virtual-office/code-runner/recovery-ledger.mjs
11816
- import fs10 from "node:fs";
12686
+ import fs13 from "node:fs";
11817
12687
  import fsp13 from "node:fs/promises";
11818
- import path18 from "node:path";
12688
+ import path22 from "node:path";
11819
12689
  function recoveryTaskId(prompt) {
11820
12690
  const match = String(prompt || "").match(/VO_RECOVERY_FROM_CODE_TASK:\s*([0-9a-f-]{36})/i);
11821
12691
  return match ? match[1].toLowerCase() : null;
@@ -11829,10 +12699,10 @@ function cloneLeaf(repo) {
11829
12699
  function recoveryLedgerCandidates(repo, clonesRoot2) {
11830
12700
  const leaf = cloneLeaf(repo);
11831
12701
  if (!leaf || !clonesRoot2) return [];
11832
- const canonical = path18.join(clonesRoot2, leaf);
12702
+ const canonical = path22.join(clonesRoot2, leaf);
11833
12703
  return [
11834
- path18.join(clonesRoot2, ".agent-worktrees", leaf, "recovery-ledger.jsonl"),
11835
- path18.join(canonical, ".agent-worktrees", "recovery-ledger.jsonl")
12704
+ path22.join(clonesRoot2, ".agent-worktrees", leaf, "recovery-ledger.jsonl"),
12705
+ path22.join(canonical, ".agent-worktrees", "recovery-ledger.jsonl")
11836
12706
  ];
11837
12707
  }
11838
12708
  async function readLedger(file, readFile5) {
@@ -11851,7 +12721,7 @@ async function readLedger(file, readFile5) {
11851
12721
  async function findPreservedRecovery(task, {
11852
12722
  clonesRoot: clonesRoot2 = process.env.VO_CODE_RUNNER_CLONES_ROOT || "",
11853
12723
  readFile: readFile5 = fsp13.readFile,
11854
- exists = fs10.existsSync
12724
+ exists = fs13.existsSync
11855
12725
  } = {}) {
11856
12726
  const resumedFrom = /^[0-9a-f-]{36}$/iu.test(String(task.resumed_from || "")) ? String(task.resumed_from).toLowerCase() : null;
11857
12727
  const originalTaskId = recoveryTaskId(task.prompt) ?? resumedFrom;
@@ -12165,8 +13035,8 @@ var init_cancellation_probe = __esm({
12165
13035
  });
12166
13036
 
12167
13037
  // ../../scripts/virtual-office/code-runner/detached-economics-spool.mjs
12168
- import { homedir as homedir7 } from "node:os";
12169
- import { dirname as dirname6, join as join9 } from "node:path";
13038
+ import { homedir as homedir8 } from "node:os";
13039
+ import { dirname as dirname7, join as join10 } from "node:path";
12170
13040
  import { mkdir as mkdir3, readFile as readFile4, rename as rename2, writeFile as writeFile3 } from "node:fs/promises";
12171
13041
  function withLock(operation) {
12172
13042
  const result = serialized.then(operation, operation);
@@ -12184,7 +13054,7 @@ async function readEntries(file) {
12184
13054
  }
12185
13055
  }
12186
13056
  async function writeEntries(file, entries) {
12187
- await mkdir3(dirname6(file), { recursive: true });
13057
+ await mkdir3(dirname7(file), { recursive: true });
12188
13058
  const temp = `${file}.${process.pid}.tmp`;
12189
13059
  await writeFile3(temp, `${JSON.stringify(entries)}
12190
13060
  `, "utf8");
@@ -12230,13 +13100,13 @@ var DEFAULT_FILE, serialized;
12230
13100
  var init_detached_economics_spool = __esm({
12231
13101
  "../../scripts/virtual-office/code-runner/detached-economics-spool.mjs"() {
12232
13102
  "use strict";
12233
- DEFAULT_FILE = join9(homedir7(), ".vo", "detached-run-economics.json");
13103
+ DEFAULT_FILE = join10(homedir8(), ".vo", "detached-run-economics.json");
12234
13104
  serialized = Promise.resolve();
12235
13105
  }
12236
13106
  });
12237
13107
 
12238
13108
  // ../../scripts/virtual-office/code-runner/killed-run-outcome.mjs
12239
- import { randomUUID as randomUUID4 } from "node:crypto";
13109
+ import { randomUUID as randomUUID6 } from "node:crypto";
12240
13110
  async function handleKilledRun({
12241
13111
  client,
12242
13112
  id,
@@ -12273,7 +13143,7 @@ async function handleKilledRun({
12273
13143
  };
12274
13144
  }
12275
13145
  if (reason === "claim_authority_changed") {
12276
- const occurrenceId = randomUUID4();
13146
+ const occurrenceId = randomUUID6();
12277
13147
  const economics = {
12278
13148
  occurrence_id: occurrenceId,
12279
13149
  runner_id: runnerId,
@@ -12373,13 +13243,13 @@ var init_runner_runtime_limits = __esm({
12373
13243
  });
12374
13244
 
12375
13245
  // ../../scripts/virtual-office/code-runner/daemon-config.mjs
12376
- import os4 from "node:os";
13246
+ import os5 from "node:os";
12377
13247
  function loadCodeRunnerConfig(env2 = process.env, { log: log2 = () => {
12378
13248
  } } = {}) {
12379
13249
  const servedOperators = parseList(env2.VO_CODE_RUNNER_OPERATOR_IDS);
12380
13250
  const allowAmbientGithub = env2.VO_CODE_RUNNER_ALLOW_AMBIENT_GH === "1";
12381
13251
  return {
12382
- runnerId: env2.VO_CODE_RUNNER_ID || `vo-code-runner-${os4.hostname()}`,
13252
+ runnerId: env2.VO_CODE_RUNNER_ID || `vo-code-runner-${os5.hostname()}`,
12383
13253
  ...resolveRunner(env2, { warn: (message) => log2(`agent-select: ${message}`) }),
12384
13254
  permissionMode: env2.VO_CODE_RUNNER_PERMISSION_MODE || "acceptEdits",
12385
13255
  maxConcurrency: Math.max(1, Number(env2.VO_CODE_TASK_MAX_CONCURRENCY || 2) || 2),
@@ -12389,7 +13259,7 @@ function loadCodeRunnerConfig(env2 = process.env, { log: log2 = () => {
12389
13259
  requireGithubAppAuth: !allowAmbientGithub && servedOperators.length > 0,
12390
13260
  allowAmbientGithub,
12391
13261
  sessionForwardSec: Math.max(0, Number(env2.VO_SESSION_FORWARD_SEC ?? 30) || 0),
12392
- operatorSeed: env2.VO_LOCAL_OPERATOR_SEED || env2.VO_CODE_RUNNER_ID || `local-${os4.hostname()}`,
13262
+ operatorSeed: env2.VO_LOCAL_OPERATOR_SEED || env2.VO_CODE_RUNNER_ID || `local-${os5.hostname()}`,
12393
13263
  cancelPollMs: Math.max(1e3, Number(env2.VO_CODE_RUNNER_CANCEL_POLL_MS || 2500) || 2500),
12394
13264
  maxWallClockMs: resolveMaxWallClockMs(env2.VO_CODE_RUNNER_MAX_WALL_CLOCK_MS),
12395
13265
  watchEnabled: env2.VO_CODE_RUNNER_WATCH !== "0",
@@ -12568,10 +13438,10 @@ var init_task_worktree_preparation = __esm({
12568
13438
  // ../../scripts/virtual-office/code-runner-daemon.mjs
12569
13439
  var code_runner_daemon_exports = {};
12570
13440
  __export(code_runner_daemon_exports, {
12571
- main: () => main
13441
+ main: () => main2
12572
13442
  });
12573
- import { randomUUID as randomUUID5 } from "node:crypto";
12574
- import { fileURLToPath as fileURLToPath6 } from "node:url";
13443
+ import { randomUUID as randomUUID7 } from "node:crypto";
13444
+ import { fileURLToPath as fileURLToPath8 } from "node:url";
12575
13445
  function log(msg) {
12576
13446
  console.log(`[code-runner ${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}`);
12577
13447
  }
@@ -12713,16 +13583,12 @@ async function processOneTask(client, task, cfg, runnerInstanceId, swarmAdmissio
12713
13583
  return;
12714
13584
  }
12715
13585
  if (await taskWasCancelled({ client, id, run, safeProgress, log })) return;
13586
+ if (await gateTestGenTaskOrFail({ client, id, task, files, worktreeDir: wt.worktreeDir, log })) return;
13587
+ if (await enforceCompletionGateOrFail({ client, id, task, worktreeDir: wt.worktreeDir, log })) return;
12716
13588
  const publicationTarget = await resolvePublicationTarget({ task, continuationRestore, worktreeDir: wt.worktreeDir, githubToken, allowAmbientGithubFallback: cfg.allowAmbientGithub });
12717
13589
  const localBranch = await resolveOrCreateBranchAsync(wt.worktreeDir, "vo/code-task");
12718
13590
  const publicationBranch = publicationTarget.targetBranch || localBranch;
12719
- if (!await recordPublicationIntent({
12720
- client,
12721
- id,
12722
- branch: publicationBranch,
12723
- safeProgress,
12724
- log
12725
- })) {
13591
+ if (!await recordPublicationIntent({ client, id, branch: publicationBranch, safeProgress, log })) {
12726
13592
  await reportCancelledRun({ client, id, run, safeProgress, log });
12727
13593
  return;
12728
13594
  }
@@ -12797,10 +13663,10 @@ Closes #${publicationTarget.supersedesPrNumber}` : "";
12797
13663
  }
12798
13664
  }
12799
13665
  }
12800
- async function main({ env: env2 = process.env, once: once2 = false } = {}) {
13666
+ async function main2({ env: env2 = process.env, once: once2 = false } = {}) {
12801
13667
  const cfg = loadCodeRunnerConfig(env2, { log });
12802
13668
  await sweepStaleTaskAttachmentDirectories().catch((error) => log(`stale attachment cleanup failed: ${error.message}`));
12803
- const runnerInstanceId = randomUUID5();
13669
+ const runnerInstanceId = randomUUID7();
12804
13670
  const client = createControlPlaneClient({
12805
13671
  env: env2,
12806
13672
  runnerId: cfg.runnerId,
@@ -12943,7 +13809,7 @@ async function main({ env: env2 = process.env, once: once2 = false } = {}) {
12943
13809
  if (controlServer) controlServer.close();
12944
13810
  log("stopped");
12945
13811
  }
12946
- var RATE_LIMIT_RESUME_ENABLED, sleep2, safeProgress, invokedDirectly;
13812
+ var RATE_LIMIT_RESUME_ENABLED, sleep2, safeProgress, invokedDirectly2;
12947
13813
  var init_code_runner_daemon = __esm({
12948
13814
  "../../scripts/virtual-office/code-runner-daemon.mjs"() {
12949
13815
  "use strict";
@@ -12954,6 +13820,8 @@ var init_code_runner_daemon = __esm({
12954
13820
  init_resolve_runner();
12955
13821
  init_rate_limit_resume();
12956
13822
  init_publish();
13823
+ init_test_gen_gate();
13824
+ init_completion_gate();
12957
13825
  init_orphan_agent_reaper();
12958
13826
  init_publish_async();
12959
13827
  init_task_prompt();
@@ -12991,11 +13859,11 @@ var init_code_runner_daemon = __esm({
12991
13859
  RATE_LIMIT_RESUME_ENABLED = process.env.VO_RATE_LIMIT_RESUME !== "0";
12992
13860
  sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
12993
13861
  safeProgress = makeSafeProgress(log);
12994
- invokedDirectly = process.argv[1] && fileURLToPath6(import.meta.url) === process.argv[1] && // Bundle-safe: self-start only when THIS file is the real entry (not inlined into vo-mcp's runner-cli.js ⇒ double-claim).
13862
+ invokedDirectly2 = process.argv[1] && fileURLToPath8(import.meta.url) === process.argv[1] && // Bundle-safe: self-start only when THIS file is the real entry (not inlined into vo-mcp's runner-cli.js ⇒ double-claim).
12995
13863
  import.meta.url.endsWith("code-runner-daemon.mjs");
12996
- if (invokedDirectly) {
13864
+ if (invokedDirectly2) {
12997
13865
  const once2 = process.argv.includes("--once");
12998
- main({ once: once2 }).catch((err) => {
13866
+ main2({ once: once2 }).catch((err) => {
12999
13867
  console.error("[code-runner] fatal:", err);
13000
13868
  process.exit(1);
13001
13869
  });
@@ -13129,6 +13997,98 @@ function pairedOperatorScope(readiness) {
13129
13997
  return readiness?.ok === true && readiness?.paired === true && typeof readiness.operatorId === "string" && readiness.operatorId.trim() ? readiness.operatorId.trim() : null;
13130
13998
  }
13131
13999
 
14000
+ // src/runner/root-config.mjs
14001
+ import { closeSync, existsSync, mkdirSync, openSync, unlinkSync } from "node:fs";
14002
+ import { homedir } from "node:os";
14003
+ import { posix, win32 } from "node:path";
14004
+ import { randomUUID } from "node:crypto";
14005
+ var APP_IDENTIFIER = "ai.algosuite.vo-runner";
14006
+ var CLONES_DIR = "clones";
14007
+ function pathsFor(platform) {
14008
+ return platform === "win32" ? win32 : posix;
14009
+ }
14010
+ function absoluteOrNull(value, pathApi) {
14011
+ const normalized = String(value || "").trim();
14012
+ return normalized && pathApi.isAbsolute(normalized) ? pathApi.resolve(normalized) : null;
14013
+ }
14014
+ function defaultClonesRoot({
14015
+ platform = process.platform,
14016
+ env: env2 = process.env,
14017
+ home = homedir()
14018
+ } = {}) {
14019
+ const pathApi = pathsFor(platform);
14020
+ if (platform === "win32") {
14021
+ const appData = absoluteOrNull(env2.APPDATA, pathApi);
14022
+ return appData ? pathApi.join(appData, APP_IDENTIFIER, CLONES_DIR) : null;
14023
+ }
14024
+ const absoluteHome = absoluteOrNull(home, pathApi);
14025
+ if (!absoluteHome) return null;
14026
+ if (platform === "darwin") {
14027
+ return pathApi.join(absoluteHome, "Library", "Application Support", APP_IDENTIFIER, CLONES_DIR);
14028
+ }
14029
+ const xdg = absoluteOrNull(env2.XDG_CONFIG_HOME, pathApi);
14030
+ return pathApi.join(xdg || pathApi.join(absoluteHome, ".config"), APP_IDENTIFIER, CLONES_DIR);
14031
+ }
14032
+ function findGitRoot(cwd, { platform, exists = existsSync }) {
14033
+ const pathApi = pathsFor(platform);
14034
+ let cursor = pathApi.resolve(cwd);
14035
+ for (; ; ) {
14036
+ if (exists(pathApi.join(cursor, ".git"))) return cursor;
14037
+ const parent = pathApi.dirname(cursor);
14038
+ if (parent === cursor) return null;
14039
+ cursor = parent;
14040
+ }
14041
+ }
14042
+ function resolveRunnerRootConfig({
14043
+ env: env2 = process.env,
14044
+ cwd = process.cwd(),
14045
+ platform = process.platform,
14046
+ home = homedir(),
14047
+ exists = existsSync
14048
+ } = {}) {
14049
+ const pathApi = pathsFor(platform);
14050
+ const explicitRepoValue = String(env2.VO_CODE_RUNNER_REPO || "").trim();
14051
+ const explicitClonesValue = String(env2.VO_CODE_RUNNER_CLONES_ROOT || "").trim();
14052
+ if (explicitRepoValue && !pathApi.isAbsolute(explicitRepoValue)) {
14053
+ throw new Error(`VO_CODE_RUNNER_REPO must be an absolute path (got '${explicitRepoValue}')`);
14054
+ }
14055
+ if (explicitClonesValue && !pathApi.isAbsolute(explicitClonesValue)) {
14056
+ throw new Error(`VO_CODE_RUNNER_CLONES_ROOT must be an absolute path (got '${explicitClonesValue}')`);
14057
+ }
14058
+ const repoRoot2 = explicitRepoValue ? pathApi.resolve(explicitRepoValue) : findGitRoot(cwd, { platform, exists });
14059
+ const clonesRoot2 = explicitClonesValue ? pathApi.resolve(explicitClonesValue) : repoRoot2 ? null : defaultClonesRoot({ platform, env: env2, home });
14060
+ if (!repoRoot2 && !clonesRoot2) {
14061
+ throw new Error(
14062
+ "No safe runner root is available. Set VO_CODE_RUNNER_REPO or VO_CODE_RUNNER_CLONES_ROOT to an absolute path."
14063
+ );
14064
+ }
14065
+ return { repoRoot: repoRoot2, clonesRoot: clonesRoot2 };
14066
+ }
14067
+ function runnerWorkingDirectory({ repoRoot: repoRoot2, clonesRoot: clonesRoot2 }) {
14068
+ const root = repoRoot2 || clonesRoot2;
14069
+ if (!root) throw new Error("Runner root configuration has no working directory");
14070
+ return root;
14071
+ }
14072
+ function assertWritableRunnerDirectory(root) {
14073
+ mkdirSync(root, { recursive: true });
14074
+ const probe = pathsFor(process.platform).join(root, `.vo-runner-write-probe-${process.pid}-${randomUUID()}`);
14075
+ let handle;
14076
+ try {
14077
+ handle = openSync(probe, "wx", 384);
14078
+ } catch (error) {
14079
+ throw new Error(
14080
+ `Runner clones root is not writable: ${root} (${error instanceof Error ? error.message : String(error)})`,
14081
+ { cause: error }
14082
+ );
14083
+ } finally {
14084
+ if (handle !== void 0) closeSync(handle);
14085
+ try {
14086
+ unlinkSync(probe);
14087
+ } catch {
14088
+ }
14089
+ }
14090
+ }
14091
+
13132
14092
  // src/runner-cli.mjs
13133
14093
  var DEFAULT_CONTROL_PLANE_URL = "https://vo-control-plane-bzjphrajaq-uc.a.run.app";
13134
14094
  function packageVersion() {
@@ -13143,11 +14103,45 @@ if (process.argv.includes("--version") || process.argv.includes("-v")) {
13143
14103
  `);
13144
14104
  process.exit(0);
13145
14105
  }
14106
+ var USAGE = `vo-mcp runner \u2014 bring-your-own agent runner daemon
14107
+
14108
+ Usage:
14109
+ vo-mcp runner poll for tasks forever (default)
14110
+ vo-mcp runner --once claim + run one task, then exit
14111
+ vo-mcp runner --status print pairing/readiness JSON, then exit
14112
+ vo-mcp runner --version print the version, then exit
14113
+ vo-mcp runner --help print this help, then exit
14114
+
14115
+ Env (all optional):
14116
+ VO_CONTROL_PLANE_ADMIN_TOKEN explicit bearer (wins over the stored credential)
14117
+ VO_CONTROL_PLANE_URL control-plane base URL (default: production)
14118
+ VO_CODE_RUNNER_REPO path to your repo clone (default: Git cwd)
14119
+ VO_CODE_RUNNER_CLONES_ROOT managed clone directory (default outside Git cwd)
14120
+ VO_CODE_RUNNER_OPERATOR_IDS operator id(s) this runner serves (your own)
14121
+ VO_CODE_RUNNER_REPOS owner/name repo(s) this runner builds
14122
+
14123
+ Pair this computer first with \`vo-mcp login\`.
14124
+ `;
14125
+ if (process.argv.includes("--help") || process.argv.includes("-h")) {
14126
+ process.stdout.write(USAGE);
14127
+ process.exit(0);
14128
+ }
13146
14129
  var { readStoredCredential: readStoredCredential2 } = await Promise.resolve().then(() => (init_credential_store(), credential_store_exports));
13147
14130
  var storedCredential = readStoredCredential2();
13148
14131
  var explicitAdminToken = process.env.VO_CONTROL_PLANE_ADMIN_TOKEN?.trim();
13149
14132
  var token = explicitAdminToken || storedCredential?.vo_credential;
13150
14133
  var statusOnly = process.argv.includes("--status");
14134
+ function configureRunnerFilesystem() {
14135
+ const config = resolveRunnerRootConfig();
14136
+ if (config.repoRoot) process.env.VO_CODE_RUNNER_REPO = config.repoRoot;
14137
+ else delete process.env.VO_CODE_RUNNER_REPO;
14138
+ if (config.clonesRoot) {
14139
+ assertWritableRunnerDirectory(config.clonesRoot);
14140
+ process.env.VO_CODE_RUNNER_CLONES_ROOT = config.clonesRoot;
14141
+ } else delete process.env.VO_CODE_RUNNER_CLONES_ROOT;
14142
+ process.chdir(runnerWorkingDirectory(config));
14143
+ return config;
14144
+ }
13151
14145
  if (statusOnly) {
13152
14146
  if (!storedCredential?.vo_credential) {
13153
14147
  process.stdout.write(`${JSON.stringify({
@@ -13156,6 +14150,7 @@ if (statusOnly) {
13156
14150
  operatorId: null,
13157
14151
  tenantId: null,
13158
14152
  githubReady: null,
14153
+ filesystemReady: null,
13159
14154
  error: "credential_missing",
13160
14155
  message: "This computer is not paired. Pair it to your AlgoHQ account first."
13161
14156
  })}
@@ -13166,9 +14161,28 @@ if (statusOnly) {
13166
14161
  controlPlaneUrl: process.env.VO_CONTROL_PLANE_URL || DEFAULT_CONTROL_PLANE_URL,
13167
14162
  token: storedCredential.vo_credential
13168
14163
  });
13169
- process.stdout.write(`${JSON.stringify(readiness)}
14164
+ if (!readiness.ok) {
14165
+ process.stdout.write(`${JSON.stringify({ ...readiness, filesystemReady: null })}
13170
14166
  `);
13171
- process.exit(readiness.ok ? 0 : 1);
14167
+ process.exit(1);
14168
+ }
14169
+ try {
14170
+ configureRunnerFilesystem();
14171
+ process.stdout.write(`${JSON.stringify({ ...readiness, filesystemReady: true })}
14172
+ `);
14173
+ process.exit(0);
14174
+ } catch (error) {
14175
+ const detail = error instanceof Error ? error.message : String(error);
14176
+ process.stdout.write(`${JSON.stringify({
14177
+ ...readiness,
14178
+ ok: false,
14179
+ filesystemReady: false,
14180
+ error: "filesystem_not_ready",
14181
+ message: `Filesystem readiness failed: ${detail}`
14182
+ })}
14183
+ `);
14184
+ process.exit(1);
14185
+ }
13172
14186
  }
13173
14187
  if (!token) {
13174
14188
  console.error("[vo-mcp runner] No credential found. Run `vo-mcp login` first.");
@@ -13193,6 +14207,13 @@ if (!explicitAdminToken) {
13193
14207
  process.exit(1);
13194
14208
  }
13195
14209
  }
14210
+ var rootConfig;
14211
+ try {
14212
+ rootConfig = configureRunnerFilesystem();
14213
+ } catch (error) {
14214
+ console.error(`[vo-mcp runner] Filesystem readiness failed: ${error instanceof Error ? error.message : String(error)}`);
14215
+ process.exit(1);
14216
+ }
13196
14217
  var env = {
13197
14218
  ...process.env,
13198
14219
  VO_CONTROL_PLANE_ADMIN_TOKEN: token,
@@ -13201,15 +14222,16 @@ var env = {
13201
14222
  // VO_CODE_RUNNER_VERSION, which a desktop host may set to its own shell
13202
14223
  // release even after runner-control updates this package in place.
13203
14224
  VO_CODE_RUNNER_DAEMON_VERSION: `vo-mcp/${packageVersion()}`,
13204
- VO_CODE_RUNNER_REPO: process.env.VO_CODE_RUNNER_REPO || process.cwd(),
14225
+ ...rootConfig.repoRoot ? { VO_CODE_RUNNER_REPO: rootConfig.repoRoot } : {},
14226
+ ...rootConfig.clonesRoot ? { VO_CODE_RUNNER_CLONES_ROOT: rootConfig.clonesRoot } : {},
13205
14227
  // Server-resolved identity replaces stale/hardcoded desktop scope. Besides
13206
14228
  // filtering heartbeat/claims, this keeps GitHub App auth required for the
13207
14229
  // entire task lifecycle (never ambient-gh fallback after a paired preflight).
13208
14230
  ...pairedOperatorId ? { VO_CODE_RUNNER_OPERATOR_IDS: pairedOperatorId } : {}
13209
14231
  };
13210
14232
  var once = process.argv.includes("--once");
13211
- var { main: main2 } = await Promise.resolve().then(() => (init_code_runner_daemon(), code_runner_daemon_exports));
13212
- main2({ env, once }).catch((err) => {
14233
+ var { main: main3 } = await Promise.resolve().then(() => (init_code_runner_daemon(), code_runner_daemon_exports));
14234
+ main3({ env, once }).catch((err) => {
13213
14235
  console.error("[vo-mcp runner] fatal:", err);
13214
14236
  process.exit(1);
13215
14237
  });