@algosuite/vo-mcp 0.2.0-beta.30 → 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.
@@ -2582,6 +2582,11 @@ var init_control_plane_auth_stub = __esm({
2582
2582
  });
2583
2583
 
2584
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
+ });
2585
2590
  async function resolveBearer(env2) {
2586
2591
  const adminToken = env2.VO_CONTROL_PLANE_ADMIN_TOKEN;
2587
2592
  if (adminToken) return adminToken;
@@ -2614,11 +2619,11 @@ function createControlPlaneClient({
2614
2619
  const resolvedBaseUrl = baseUrl ?? env2.VO_CONTROL_PLANE_URL ?? "";
2615
2620
  if (!resolvedBaseUrl) throw new Error("VO_CONTROL_PLANE_URL is required for the code-runner daemon");
2616
2621
  const root = resolvedBaseUrl.replace(/\/+$/, "");
2617
- async function req(method, path20, body, { timeoutMs } = {}) {
2622
+ async function req(method, path23, body, { timeoutMs } = {}) {
2618
2623
  const bearer = await resolveBearer(env2);
2619
2624
  const controller = timeoutMs ? new AbortController() : null;
2620
2625
  let timeoutId;
2621
- const request = Promise.resolve(fetchImpl(`${root}${path20}`, {
2626
+ const request = Promise.resolve(fetchImpl(`${root}${path23}`, {
2622
2627
  method,
2623
2628
  headers: {
2624
2629
  "content-type": "application/json",
@@ -2631,7 +2636,7 @@ function createControlPlaneClient({
2631
2636
  const timeout = new Promise((_, reject) => {
2632
2637
  timeoutId = setTimeout(() => {
2633
2638
  controller.abort();
2634
- reject(new Error(`control-plane ${path20} timed out after ${timeoutMs}ms`));
2639
+ reject(new Error(`control-plane ${path23} timed out after ${timeoutMs}ms`));
2635
2640
  }, timeoutMs);
2636
2641
  });
2637
2642
  try {
@@ -2640,7 +2645,7 @@ function createControlPlaneClient({
2640
2645
  clearTimeout(timeoutId);
2641
2646
  }
2642
2647
  }
2643
- const taskReq = (method, path20, body, options = {}) => req(method, path20, body, { timeoutMs: taskRequestTimeoutMs, ...options });
2648
+ const taskReq = (method, path23, body, options = {}) => req(method, path23, body, { timeoutMs: taskRequestTimeoutMs, ...options });
2644
2649
  return {
2645
2650
  ...makeAutonomousDispatchAdmissionClient(
2646
2651
  req,
@@ -2763,8 +2768,8 @@ function createControlPlaneClient({
2763
2768
  return listAllPrOpenedTasks(taskReq);
2764
2769
  },
2765
2770
  async downloadTaskAttachment(taskId, attachmentId) {
2766
- const path20 = `/api/v1/code-task/${encodeURIComponent(taskId)}/attachment/${encodeURIComponent(attachmentId)}`;
2767
- const res = await taskReq("GET", path20);
2771
+ const path23 = `/api/v1/code-task/${encodeURIComponent(taskId)}/attachment/${encodeURIComponent(attachmentId)}`;
2772
+ const res = await taskReq("GET", path23);
2768
2773
  if (res.status === 401) cachedFirebaseToken = null;
2769
2774
  if (!res.ok) throw new Error(`attachment download failed: HTTP ${res.status}`);
2770
2775
  return Buffer.from(await res.arrayBuffer());
@@ -4870,6 +4875,188 @@ var init_cursor_runner = __esm({
4870
4875
  }
4871
4876
  });
4872
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
+
4873
5060
  // ../../scripts/virtual-office/code-runner/local-model-runner.mjs
4874
5061
  function resolveLocalProvider(env2 = process.env) {
4875
5062
  return String(env2.VO_CODE_RUNNER_LOCAL_PROVIDER || "").trim().toLowerCase() || DEFAULT_LOCAL_PROVIDER;
@@ -4943,20 +5130,37 @@ function buildLocalArgs(opts = {}, env2 = process.env) {
4943
5130
  }
4944
5131
  function applyLocalAuthEnv(baseEnv = process.env, configEnv = process.env) {
4945
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
+ }
4946
5136
  const baseUrl = resolveLocalBaseUrl(configEnv);
4947
5137
  if (baseUrl && isLoopbackBaseUrl(baseUrl) && resolveLocalProvider(configEnv) === "ollama" && !String(out.OLLAMA_HOST || "").trim()) {
4948
5138
  out.OLLAMA_HOST = baseUrl.replace(/\/+$/, "");
4949
5139
  }
4950
5140
  return out;
4951
5141
  }
4952
- 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;
4953
5143
  var init_local_model_runner = __esm({
4954
5144
  "../../scripts/virtual-office/code-runner/local-model-runner.mjs"() {
4955
5145
  "use strict";
4956
5146
  init_codex_runner();
4957
5147
  init_agent_key_store();
4958
5148
  init_agent_auth_tier();
5149
+ init_ollama_agent_core();
5150
+ init_ollama_native_transport();
4959
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
+ ]);
4960
5164
  LOCAL_PROVIDERS = ["ollama", "lmstudio"];
4961
5165
  DEFAULT_LOCAL_PROVIDER = "ollama";
4962
5166
  LOCAL_PROBE_URLS = {
@@ -4977,19 +5181,25 @@ var init_local_model_runner = __esm({
4977
5181
  this.env = env2;
4978
5182
  this.fetchImpl = fetchImpl;
4979
5183
  }
4980
- /** 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
+ */
4981
5189
  get binary() {
4982
- return this.resolveBinary();
5190
+ return resolveLocalTransport(this.env) === "native" ? process.execPath : this.resolveBinary();
4983
5191
  }
4984
5192
  buildArgs(opts = {}) {
4985
- return buildLocalArgs(opts, this.env);
5193
+ return resolveLocalTransport(this.env) === "native" ? buildLocalNativeArgs(opts, this.env) : buildLocalArgs(opts, this.env);
4986
5194
  }
4987
5195
  /**
4988
- * Codex JSONL events map identically, except a sovereign local model has no
4989
- * vendor bill. Codex omits total_cost_usd for OSS runs; turn that known fact
4990
- * 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.)
4991
5200
  */
4992
5201
  parseEvent(line) {
5202
+ if (resolveLocalTransport(this.env) === "native") return parseOllamaAgentEvent(line);
4993
5203
  const event = parseCodexEvent(line);
4994
5204
  return event?.kind === "result" ? { ...event, costUsd: 0 } : event;
4995
5205
  }
@@ -5488,9 +5698,9 @@ var init_rate_limit_resume_state = __esm({
5488
5698
 
5489
5699
  // ../../scripts/virtual-office/code-runner/rate-limit-resume.mjs
5490
5700
  import { homedir as homedir3 } from "node:os";
5491
- import { join as join2 } from "node:path";
5701
+ import { join as join3 } from "node:path";
5492
5702
  function resumeQueuePath() {
5493
- return join2(homedir3(), ".claude", "resume-queue.jsonl");
5703
+ return join3(homedir3(), ".claude", "resume-queue.jsonl");
5494
5704
  }
5495
5705
  function buildResumeEntry({ task = {}, resumeAfter = null, summary = "", at } = {}) {
5496
5706
  return {
@@ -5692,7 +5902,7 @@ var init_auto_merge = __esm({
5692
5902
  // ../../scripts/virtual-office/code-runner/pr-overlap-gate.mjs
5693
5903
  import { spawnSync as spawnSync8 } from "node:child_process";
5694
5904
  import { existsSync as existsSync6 } from "node:fs";
5695
- import { fileURLToPath } from "node:url";
5905
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
5696
5906
  function stripCredentials(env2 = process.env) {
5697
5907
  const safe = { ...env2 };
5698
5908
  for (const key of CREDENTIAL_ENV_KEYS) delete safe[key];
@@ -5718,7 +5928,7 @@ var init_pr_overlap_gate = __esm({
5718
5928
  TRUSTED_OVERLAP_CANDIDATES = [
5719
5929
  new URL("../../ci/check-local-pr-overlap.mjs", import.meta.url),
5720
5930
  new URL("./ci/check-local-pr-overlap.js", import.meta.url)
5721
- ].map((candidate) => fileURLToPath(candidate));
5931
+ ].map((candidate) => fileURLToPath2(candidate));
5722
5932
  CREDENTIAL_ENV_KEYS = Object.freeze([
5723
5933
  "GH_TOKEN",
5724
5934
  "GITHUB_TOKEN",
@@ -5771,14 +5981,14 @@ function parsePorcelainZ(out) {
5771
5981
  for (let i = 0; i < tokens.length; i += 1) {
5772
5982
  const token2 = tokens[i];
5773
5983
  if (!token2) continue;
5774
- const path20 = token2.slice(3);
5775
- if (path20) files.push(path20);
5984
+ const path23 = token2.slice(3);
5985
+ if (path23) files.push(path23);
5776
5986
  if (token2[0] === "R" || token2[0] === "C") i += 1;
5777
5987
  }
5778
5988
  return files;
5779
5989
  }
5780
- function isAgentScratch(path20) {
5781
- const normalized = String(path20 || "");
5990
+ function isAgentScratch(path23) {
5991
+ const normalized = String(path23 || "");
5782
5992
  return SCRATCH_PATTERNS.some((pattern) => pattern.test(normalized));
5783
5993
  }
5784
5994
  var SCRATCH_PATTERNS;
@@ -5845,6 +6055,540 @@ var init_publish = __esm({
5845
6055
  }
5846
6056
  });
5847
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
+
5848
6592
  // ../../scripts/virtual-office/code-runner/process-runner.mjs
5849
6593
  import { spawn as spawn3 } from "node:child_process";
5850
6594
  function buildStepLabel(cmd, args = []) {
@@ -6338,8 +7082,8 @@ var init_publish_async = __esm({
6338
7082
 
6339
7083
  // ../../scripts/virtual-office/code-runner/skill-catalog.mjs
6340
7084
  import { readdirSync as readdirSync2, readFileSync as readFileSync3, statSync } from "node:fs";
6341
- import { dirname as dirname2, join as join3 } from "node:path";
6342
- 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";
6343
7087
  function parseFrontmatterNameDescription(raw) {
6344
7088
  const text = String(raw).replace(/\r\n/g, "\n");
6345
7089
  if (!text.startsWith("---\n")) return null;
@@ -6358,15 +7102,15 @@ function parseFrontmatterNameDescription(raw) {
6358
7102
  return name && description ? { name, description } : null;
6359
7103
  }
6360
7104
  function resolveDefaultRepoRoot() {
6361
- const starts = [dirname2(fileURLToPath2(import.meta.url)), process.cwd()];
7105
+ const starts = [dirname3(fileURLToPath4(import.meta.url)), process.cwd()];
6362
7106
  for (const start of starts) {
6363
7107
  let dir = start;
6364
7108
  for (let i = 0; i < 8; i += 1) {
6365
7109
  try {
6366
- if (statSync(join3(dir, ".claude", "skills")).isDirectory()) return dir;
7110
+ if (statSync(join4(dir, ".claude", "skills")).isDirectory()) return dir;
6367
7111
  } catch {
6368
7112
  }
6369
- const parent = dirname2(dir);
7113
+ const parent = dirname3(dir);
6370
7114
  if (parent === dir) break;
6371
7115
  dir = parent;
6372
7116
  }
@@ -6375,14 +7119,14 @@ function resolveDefaultRepoRoot() {
6375
7119
  }
6376
7120
  function loadSkillCatalog({ repoRoot: repoRoot2 = resolveDefaultRepoRoot() } = {}) {
6377
7121
  try {
6378
- const skillsDir = join3(repoRoot2, ".claude", "skills");
7122
+ const skillsDir = join4(repoRoot2, ".claude", "skills");
6379
7123
  const catalog = [];
6380
7124
  for (const entry of readdirSync2(skillsDir)) {
6381
- const dir = join3(skillsDir, entry);
7125
+ const dir = join4(skillsDir, entry);
6382
7126
  try {
6383
7127
  if (!statSync(dir).isDirectory()) continue;
6384
7128
  const parsed = parseFrontmatterNameDescription(
6385
- readFileSync3(join3(dir, "SKILL.md"), "utf8")
7129
+ readFileSync3(join4(dir, "SKILL.md"), "utf8")
6386
7130
  );
6387
7131
  if (parsed) catalog.push(parsed);
6388
7132
  } catch {
@@ -6614,7 +7358,7 @@ var init_task_prompt = __esm({
6614
7358
  import { createHash as createHash3, randomUUID as randomUUID2 } from "node:crypto";
6615
7359
  import { chmod, mkdir, mkdtemp, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
6616
7360
  import os2 from "node:os";
6617
- import path14 from "node:path";
7361
+ import path17 from "node:path";
6618
7362
  function safeTaskToken(taskId) {
6619
7363
  return String(taskId || "task").replace(/[^0-9A-Za-z_-]/gu, "_").slice(0, 48) || "task";
6620
7364
  }
@@ -6624,25 +7368,25 @@ function sanitizeTaskAttachmentName(name, index = 0) {
6624
7368
  return `${String(index + 1).padStart(2, "0")}-${normalized}`;
6625
7369
  }
6626
7370
  function assertGeneratedDirectory(directory, tempRoot) {
6627
- const resolvedDirectory = path14.resolve(directory);
6628
- const resolvedRoot = path14.resolve(tempRoot);
6629
- if (path14.dirname(resolvedDirectory) !== resolvedRoot || !path14.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)) {
6630
7374
  throw new Error("refusing to clean an unverified task-attachment directory");
6631
7375
  }
6632
7376
  return resolvedDirectory;
6633
7377
  }
6634
7378
  async function createAttachmentDirectory(taskId, tempRoot) {
6635
- const root = path14.resolve(tempRoot);
7379
+ const root = path17.resolve(tempRoot);
6636
7380
  await mkdir(root, { recursive: true });
6637
- const directory = await mkdtemp(path14.join(root, `${DIRECTORY_PREFIX}${safeTaskToken(taskId)}-`));
6638
- const marker = JSON.stringify({ owner: MARKER_OWNER, token: randomUUID2(), directory: path14.basename(directory), created_at: (/* @__PURE__ */ new Date()).toISOString() });
6639
- await writeFile(path14.join(directory, MARKER_FILE), marker, { encoding: "utf8", mode: 384 });
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 });
6640
7384
  return { directory, marker, tempRoot: root };
6641
7385
  }
6642
7386
  async function cleanupGeneratedDirectory(state) {
6643
7387
  if (!state || state.cleaned) return;
6644
7388
  const directory = assertGeneratedDirectory(state.directory, state.tempRoot);
6645
- const marker = await readFile(path14.join(directory, MARKER_FILE), "utf8").catch(() => "");
7389
+ const marker = await readFile(path17.join(directory, MARKER_FILE), "utf8").catch(() => "");
6646
7390
  if (marker !== state.marker) throw new Error("refusing to clean a task-attachment directory without its exact marker");
6647
7391
  await rm(directory, { recursive: true, force: true });
6648
7392
  state.cleaned = true;
@@ -6661,7 +7405,7 @@ async function sweepStaleTaskAttachmentDirectories({
6661
7405
  now = Date.now(),
6662
7406
  maxAgeMs = DEFAULT_STALE_AGE_MS
6663
7407
  } = {}) {
6664
- const root = path14.resolve(tempRoot);
7408
+ const root = path17.resolve(tempRoot);
6665
7409
  if (!Number.isFinite(maxAgeMs) || maxAgeMs <= 0) throw new Error("stale attachment age must be positive");
6666
7410
  const entries = await readdir(root, { withFileTypes: true }).catch((error) => {
6667
7411
  if (error?.code === "ENOENT") return [];
@@ -6670,8 +7414,8 @@ async function sweepStaleTaskAttachmentDirectories({
6670
7414
  let removed = 0;
6671
7415
  for (const entry of entries) {
6672
7416
  if (!entry.isDirectory() || !entry.name.startsWith(DIRECTORY_PREFIX)) continue;
6673
- const directory = assertGeneratedDirectory(path14.join(root, entry.name), root);
6674
- const markerRaw = await readFile(path14.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(() => "");
6675
7419
  const marker = parseOwnedMarker(markerRaw, entry.name);
6676
7420
  if (!marker) continue;
6677
7421
  const directoryStat = await stat(directory);
@@ -6714,10 +7458,10 @@ async function materializeTaskAttachments(client, task, { tempRoot = os2.tmpdir(
6714
7458
  const sha256 = createHash3("sha256").update(content).digest("hex");
6715
7459
  if (sha256 !== ref.sha256) throw new Error(`attachment ${ref.attachment_id} sha256 mismatch`);
6716
7460
  const name = sanitizeTaskAttachmentName(ref.name, index);
6717
- const filePath = path14.join(state.directory, name);
7461
+ const filePath = path17.join(state.directory, name);
6718
7462
  await writeFile(filePath, content, { flag: "wx", mode: 384 });
6719
7463
  await chmod(filePath, 384);
6720
- files.push({ attachmentId: ref.attachment_id, name, mime: ref.mime, sizeBytes: ref.size_bytes, sha256, path: path14.resolve(filePath) });
7464
+ files.push({ attachmentId: ref.attachment_id, name, mime: ref.mime, sizeBytes: ref.size_bytes, sha256, path: path17.resolve(filePath) });
6721
7465
  }
6722
7466
  return { directory: state.directory, files, manifestMarkdown: buildManifest(files), cleanup: () => cleanupGeneratedDirectory(state) };
6723
7467
  } catch (error) {
@@ -6740,7 +7484,7 @@ var init_task_attachments = __esm({
6740
7484
 
6741
7485
  // ../../scripts/virtual-office/code-runner/session-spool-forwarder.mjs
6742
7486
  import { homedir as homedir4 } from "node:os";
6743
- import { join as join4 } from "node:path";
7487
+ import { join as join5 } from "node:path";
6744
7488
  import { readdir as readdir2, readFile as readFile2, unlink, writeFile as writeFile2 } from "node:fs/promises";
6745
7489
  import { createHash as createHash4 } from "node:crypto";
6746
7490
  function deriveUuid(seed) {
@@ -6772,18 +7516,18 @@ async function readSpool(spoolDir = SPOOL_DIR) {
6772
7516
  for (const f of files) {
6773
7517
  if (!f.endsWith(".json")) continue;
6774
7518
  try {
6775
- const record = JSON.parse(await readFile2(join4(spoolDir, f), "utf8"));
7519
+ const record = JSON.parse(await readFile2(join5(spoolDir, f), "utf8"));
6776
7520
  if (record && typeof record.session_key === "string") {
6777
- out.push({ full: join4(spoolDir, f), record });
7521
+ out.push({ full: join5(spoolDir, f), record });
6778
7522
  }
6779
7523
  } catch {
6780
7524
  }
6781
7525
  }
6782
7526
  return out;
6783
7527
  }
6784
- async function readCloudMap(path20) {
7528
+ async function readCloudMap(path23) {
6785
7529
  try {
6786
- return JSON.parse(await readFile2(path20, "utf8"));
7530
+ return JSON.parse(await readFile2(path23, "utf8"));
6787
7531
  } catch {
6788
7532
  return {};
6789
7533
  }
@@ -6856,8 +7600,8 @@ var SPOOL_DIR, CLOUD_MAP_FILE, STALE_MS, ACTIVE_SILENCE_MS;
6856
7600
  var init_session_spool_forwarder = __esm({
6857
7601
  "../../scripts/virtual-office/code-runner/session-spool-forwarder.mjs"() {
6858
7602
  "use strict";
6859
- SPOOL_DIR = join4(homedir4(), ".vo", "session-spool");
6860
- CLOUD_MAP_FILE = join4(homedir4(), ".vo", "session-cloud-map.json");
7603
+ SPOOL_DIR = join5(homedir4(), ".vo", "session-spool");
7604
+ CLOUD_MAP_FILE = join5(homedir4(), ".vo", "session-cloud-map.json");
6861
7605
  STALE_MS = 60 * 60 * 1e3;
6862
7606
  ACTIVE_SILENCE_MS = 10 * 60 * 1e3;
6863
7607
  }
@@ -6928,7 +7672,7 @@ var init_rate_limit_resume_scheduler_core = __esm({
6928
7672
  });
6929
7673
 
6930
7674
  // ../../scripts/virtual-office/code-runner/rate-limit-resume-scheduler.mjs
6931
- import { dirname as dirname3, join as join5, resolve } from "node:path";
7675
+ import { dirname as dirname4, join as join6, resolve } from "node:path";
6932
7676
  function defaultLog(message) {
6933
7677
  console.log(`[rate-limit-scheduler ${(/* @__PURE__ */ new Date()).toISOString()}] ${message}`);
6934
7678
  }
@@ -7010,7 +7754,7 @@ async function runLockedScheduler({
7010
7754
  async function runScheduler({
7011
7755
  env: env2 = process.env,
7012
7756
  queuePath = resumeQueuePath(),
7013
- attemptsPath = join5(dirname3(queuePath), "resume-attempts.json"),
7757
+ attemptsPath = join6(dirname4(queuePath), "resume-attempts.json"),
7014
7758
  client,
7015
7759
  now,
7016
7760
  log: log2 = defaultLog
@@ -7302,7 +8046,7 @@ var init_runner_capacity = __esm({
7302
8046
  });
7303
8047
 
7304
8048
  // ../../scripts/virtual-office/code-runner/agent-auth-probe-process.mjs
7305
- import { fileURLToPath as fileURLToPath3 } from "node:url";
8049
+ import { fileURLToPath as fileURLToPath5 } from "node:url";
7306
8050
  async function probeAgentInChild(agent, timeoutMs) {
7307
8051
  const stdout = await runProcess2(process.execPath, [probeCli, agent], {
7308
8052
  timeout: timeoutMs,
@@ -7315,7 +8059,7 @@ var init_agent_auth_probe_process = __esm({
7315
8059
  "../../scripts/virtual-office/code-runner/agent-auth-probe-process.mjs"() {
7316
8060
  "use strict";
7317
8061
  init_process_runner2();
7318
- 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));
7319
8063
  }
7320
8064
  });
7321
8065
 
@@ -7540,7 +8284,7 @@ var init_local_model_remote_config = __esm({
7540
8284
 
7541
8285
  // ../../scripts/virtual-office/code-runner/account-usage/shared.mjs
7542
8286
  import crypto from "node:crypto";
7543
- import fs7 from "node:fs";
8287
+ import fs9 from "node:fs";
7544
8288
  function accountKey(agent, rawId) {
7545
8289
  const id = typeof rawId === "string" ? rawId.trim() : "";
7546
8290
  if (!id) return null;
@@ -7604,7 +8348,7 @@ var init_shared = __esm({
7604
8348
  };
7605
8349
  readJson = (p) => {
7606
8350
  try {
7607
- return JSON.parse(fs7.readFileSync(p, "utf8"));
8351
+ return JSON.parse(fs9.readFileSync(p, "utf8"));
7608
8352
  } catch {
7609
8353
  return null;
7610
8354
  }
@@ -7614,9 +8358,9 @@ var init_shared = __esm({
7614
8358
  });
7615
8359
 
7616
8360
  // ../../scripts/virtual-office/code-runner/account-usage/claude.mjs
7617
- import fs8 from "node:fs";
8361
+ import fs10 from "node:fs";
7618
8362
  import os3 from "node:os";
7619
- import path15 from "node:path";
8363
+ import path18 from "node:path";
7620
8364
  function fileCaptureTime(filePath, explicit, statFn) {
7621
8365
  if (typeof explicit === "string" && explicit) return explicit;
7622
8366
  try {
@@ -7630,7 +8374,7 @@ function usageBaseUrl(env2 = process.env) {
7630
8374
  return String(raw).replace(/\/+$/, "");
7631
8375
  }
7632
8376
  function readOAuthToken({ homeDir = os3.homedir(), read = readJson, now = Date.now() } = {}) {
7633
- const creds = read(path15.join(homeDir, ".claude", ".credentials.json"));
8377
+ const creds = read(path18.join(homeDir, ".claude", ".credentials.json"));
7634
8378
  const oauth = creds && typeof creds === "object" ? creds.claudeAiOauth : null;
7635
8379
  if (!oauth || typeof oauth !== "object") return null;
7636
8380
  const token2 = typeof oauth.accessToken === "string" ? oauth.accessToken.trim() : "";
@@ -7640,7 +8384,7 @@ function readOAuthToken({ homeDir = os3.homedir(), read = readJson, now = Date.n
7640
8384
  return token2;
7641
8385
  }
7642
8386
  function readAccountId({ homeDir = os3.homedir(), read = readJson } = {}) {
7643
- const cfg = read(path15.join(homeDir, ".claude.json"));
8387
+ const cfg = read(path18.join(homeDir, ".claude.json"));
7644
8388
  const account = cfg && typeof cfg === "object" ? cfg.oauthAccount : null;
7645
8389
  return account && typeof account.accountUuid === "string" ? account.accountUuid : null;
7646
8390
  }
@@ -7733,7 +8477,7 @@ async function readClaudeOAuthUsage({
7733
8477
  function readClaudeFileUsage({
7734
8478
  homeDir = os3.homedir(),
7735
8479
  read: rawRead = readJson,
7736
- statFn = fs8.statSync,
8480
+ statFn = fs10.statSync,
7737
8481
  now = () => Date.now()
7738
8482
  } = {}) {
7739
8483
  const read = (p) => {
@@ -7750,7 +8494,7 @@ function readClaudeFileUsage({
7750
8494
  if (age === null || age > MAX_FILE_AGE_MS) return null;
7751
8495
  return row;
7752
8496
  };
7753
- const statusPath = path15.join(homeDir, ".claude", "claude-usage.json");
8497
+ const statusPath = path18.join(homeDir, ".claude", "claude-usage.json");
7754
8498
  const status = read(statusPath);
7755
8499
  if (status && (status.seven_day || status.five_hour)) {
7756
8500
  const row = fresh(makeUsageRow({
@@ -7765,7 +8509,7 @@ function readClaudeFileUsage({
7765
8509
  }));
7766
8510
  if (row) return row;
7767
8511
  }
7768
- const weeklyPath = path15.join(homeDir, ".claude", "claude-weekly-usage.json");
8512
+ const weeklyPath = path18.join(homeDir, ".claude", "claude-weekly-usage.json");
7769
8513
  const weekly = read(weeklyPath);
7770
8514
  if (weekly) {
7771
8515
  const row = fresh(makeUsageRow({
@@ -8206,7 +8950,7 @@ var init_watcher_coordination = __esm({
8206
8950
  // ../../scripts/virtual-office/code-runner/watcher-state.mjs
8207
8951
  import { randomUUID as randomUUID3 } from "node:crypto";
8208
8952
  import { mkdir as mkdir2, open, readFile as readFile3, rename, unlink as unlink2 } from "node:fs/promises";
8209
- import { dirname as dirname4 } from "node:path";
8953
+ import { dirname as dirname5 } from "node:path";
8210
8954
  async function readWatcherState(stateFile) {
8211
8955
  let raw;
8212
8956
  try {
@@ -8222,7 +8966,7 @@ async function readWatcherState(stateFile) {
8222
8966
  return parsed;
8223
8967
  }
8224
8968
  async function writeWatcherState(stateFile, state) {
8225
- const directory = dirname4(stateFile);
8969
+ const directory = dirname5(stateFile);
8226
8970
  await mkdir2(directory, { recursive: true });
8227
8971
  const temp = `${stateFile}.${process.pid}.${randomUUID3()}.tmp`;
8228
8972
  let handle;
@@ -8468,7 +9212,7 @@ var init_enqueue_autonomous_code_task = __esm({
8468
9212
 
8469
9213
  // ../../scripts/virtual-office/code-runner/pr-watcher.mjs
8470
9214
  import { homedir as homedir5 } from "node:os";
8471
- import { join as join6 } from "node:path";
9215
+ import { join as join7 } from "node:path";
8472
9216
  function parsePrCiStatus(view) {
8473
9217
  const state = (view && typeof view.state === "string" ? view.state : "UNKNOWN").toUpperCase();
8474
9218
  const rollup = view && Array.isArray(view.statusCheckRollup) ? view.statusCheckRollup : [];
@@ -8811,7 +9555,7 @@ var init_pr_watcher = __esm({
8811
9555
  init_watcher_state();
8812
9556
  init_superseded_pr_source();
8813
9557
  init_ci_fix_prompt();
8814
- DEFAULT_STATE_FILE = join6(homedir5(), ".vo", "dispatched-prs.json");
9558
+ DEFAULT_STATE_FILE = join7(homedir5(), ".vo", "dispatched-prs.json");
8815
9559
  FAIL_CONCLUSIONS = /* @__PURE__ */ new Set([
8816
9560
  "FAILURE",
8817
9561
  "TIMED_OUT",
@@ -9041,9 +9785,9 @@ function buildControlHandler({ getStatus, requestStop, allowedOrigin }) {
9041
9785
  res.end();
9042
9786
  return;
9043
9787
  }
9044
- const path20 = String(req.url || "").split("?")[0];
9788
+ const path23 = String(req.url || "").split("?")[0];
9045
9789
  res.setHeader("content-type", "application/json");
9046
- if (req.method === "GET" && path20 === "/status") {
9790
+ if (req.method === "GET" && path23 === "/status") {
9047
9791
  let status;
9048
9792
  try {
9049
9793
  status = getStatus();
@@ -9054,7 +9798,7 @@ function buildControlHandler({ getStatus, requestStop, allowedOrigin }) {
9054
9798
  res.end(JSON.stringify({ ok: true, ...status }));
9055
9799
  return;
9056
9800
  }
9057
- if (req.method === "POST" && path20 === "/stop") {
9801
+ if (req.method === "POST" && path23 === "/stop") {
9058
9802
  if (!isControlOriginAllowed(req.headers.origin, allowedOrigin) || !req.headers["x-vo-control"]) {
9059
9803
  res.statusCode = 403;
9060
9804
  res.end(JSON.stringify({ ok: false, error: "forbidden" }));
@@ -9217,18 +9961,35 @@ var init_effort_mode_config = __esm({
9217
9961
  });
9218
9962
 
9219
9963
  // ../../scripts/virtual-office/model-registry.mjs
9220
- import fs9 from "node:fs";
9221
- import path16 from "node:path";
9222
- 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
+ }
9223
9984
  function uniqueModels(models = []) {
9224
9985
  return [...new Set(models.map((model) => String(model || "").trim()).filter(Boolean))];
9225
9986
  }
9226
9987
  function normalizeProvider(value = "") {
9227
- const lower = String(value || "").trim().toLowerCase();
9228
- if (lower.includes("anthropic")) return "anthropic";
9229
- if (lower.includes("openai")) return "openai";
9230
- if (lower.includes("google") || lower.includes("gemini")) return "google";
9231
- 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;
9232
9993
  }
9233
9994
  function stripProviderPrefix(id = "") {
9234
9995
  const raw = String(id || "").trim();
@@ -9270,15 +10031,15 @@ function normalizeCatalogModel(model = {}) {
9270
10031
  };
9271
10032
  }
9272
10033
  function parseVersionScore(id = "") {
9273
- const lower = String(id || "").toLowerCase();
9274
- 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);
9275
10036
  let score = 0;
9276
10037
  for (let i = 0; i < numbers.length; i++) score += numbers[i] / Math.pow(1e3, i);
9277
- if (/opus|pro|flagship/.test(lower)) score += 10;
9278
- if (/sonnet/.test(lower)) score += 5;
9279
- if (/preview|latest/.test(lower)) score += 0.25;
9280
- if (/\[1m\]|\(1m\)/i.test(lower)) score += 1;
9281
- 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;
9282
10043
  return score;
9283
10044
  }
9284
10045
  function familyMatches(model, family) {
@@ -9330,9 +10091,9 @@ async function fetchGoogleModels(fetchImpl, env2 = process.env) {
9330
10091
  return (data?.models || []).map((model) => normalizeCatalogModel({ ...model, provider: "google", source: "google" })).filter(Boolean);
9331
10092
  }
9332
10093
  function readCache(cacheFile = DEFAULT_CACHE_FILE, nowMs = Date.now(), ttlMs = DEFAULT_TTL_MS3) {
9333
- if (!fs9.existsSync(cacheFile)) return null;
10094
+ if (!fs11.existsSync(cacheFile)) return null;
9334
10095
  try {
9335
- const parsed = JSON.parse(fs9.readFileSync(cacheFile, "utf-8"));
10096
+ const parsed = JSON.parse(fs11.readFileSync(cacheFile, "utf-8"));
9336
10097
  if (nowMs - Number(parsed.checkedAtMs || 0) > ttlMs) return null;
9337
10098
  if (!Array.isArray(parsed.models)) return null;
9338
10099
  return parsed;
@@ -9341,8 +10102,8 @@ function readCache(cacheFile = DEFAULT_CACHE_FILE, nowMs = Date.now(), ttlMs = D
9341
10102
  }
9342
10103
  }
9343
10104
  function writeCache(cacheFile = DEFAULT_CACHE_FILE, payload) {
9344
- fs9.mkdirSync(path16.dirname(cacheFile), { recursive: true });
9345
- fs9.writeFileSync(cacheFile, JSON.stringify(payload, null, 2));
10105
+ fs11.mkdirSync(path19.dirname(cacheFile), { recursive: true });
10106
+ fs11.writeFileSync(cacheFile, JSON.stringify(payload, null, 2));
9346
10107
  }
9347
10108
  async function fetchRegistryCatalog({
9348
10109
  fetchImpl = fetch,
@@ -9395,14 +10156,17 @@ async function resolveModelFamily(family, options = {}) {
9395
10156
  const resolved = selectBestFamilyModel(catalog.models || [], family);
9396
10157
  return resolved || def.fallbacks[0];
9397
10158
  }
9398
- 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;
9399
10160
  var init_model_registry = __esm({
9400
10161
  "../../scripts/virtual-office/model-registry.mjs"() {
9401
10162
  "use strict";
9402
- __dirname = path16.dirname(fileURLToPath4(import.meta.url));
9403
- ROOT = path16.resolve(__dirname, "..", "..");
9404
- DEFAULT_CACHE_DIR = path16.join(ROOT, ".virtual-office-cache", "model-registry");
9405
- DEFAULT_CACHE_FILE = path16.join(DEFAULT_CACHE_DIR, "catalog.json");
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");
9406
10170
  DEFAULT_TTL_MS3 = 60 * 60 * 1e3;
9407
10171
  ANTHROPIC_API_VERSION = "2023-06-01";
9408
10172
  FAMILY_DEFINITIONS = {
@@ -9507,17 +10271,17 @@ function modelCompatibleWithAgent(agent, model) {
9507
10271
  if (!model) return true;
9508
10272
  return AGENT_MODEL_COMPATIBILITY[agent]?.(model) ?? false;
9509
10273
  }
9510
- function classifyTier(prompt) {
10274
+ function classifyTier2(prompt) {
9511
10275
  const text = String(prompt || "").trim();
9512
10276
  if (!text) return "mid";
9513
- const lower = text.toLowerCase();
10277
+ const lower2 = text.toLowerCase();
9514
10278
  if (/lint|format|typo|missing import|update deps|chore|maintenance|runner|daemon/.test(
9515
- lower
10279
+ lower2
9516
10280
  )) {
9517
10281
  return "cheap";
9518
10282
  }
9519
10283
  if (/generate roadmap|new feature|implement .* feature|major refactor|strategic/.test(
9520
- lower
10284
+ lower2
9521
10285
  ) || text.length > 1500) {
9522
10286
  return "best";
9523
10287
  }
@@ -9538,7 +10302,7 @@ async function resolveModelForTier(tier, { agent = DEFAULT_AGENT2, resolveModelF
9538
10302
  return fallbacks[effectiveTier];
9539
10303
  }
9540
10304
  async function resolveTaskModel(task, { agent = DEFAULT_AGENT2 } = {}) {
9541
- 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);
9542
10306
  const pinned = typeof task.model === "string" ? task.model.trim() : "";
9543
10307
  if (pinned && modelCompatibleWithAgent(normalizeAgent(agent), pinned)) {
9544
10308
  return { tier, model: pinned };
@@ -10011,7 +10775,7 @@ var init_classify_task = __esm({
10011
10775
  // ../../scripts/virtual-office/code-runner/auto-router/effort-policy.mjs
10012
10776
  import { readFileSync as readFileSync4 } from "node:fs";
10013
10777
  import { homedir as homedir6 } from "node:os";
10014
- import { join as join7 } from "node:path";
10778
+ import { join as join8 } from "node:path";
10015
10779
  function difficultyToRung(difficulty, thresholds) {
10016
10780
  const b = thresholds.rungBounds;
10017
10781
  if (difficulty >= b.R5) return "R5";
@@ -10036,9 +10800,9 @@ function operatorTierToRung(tier, difficulty, thresholds) {
10036
10800
  if (tier === "best" && difficulty >= thresholds.rungBounds.R5) return "R5";
10037
10801
  return base;
10038
10802
  }
10039
- function readCodexModelsCache({ path: path20 = DEFAULT_CODEX_MODELS_CACHE, read = readFileSync4 } = {}) {
10803
+ function readCodexModelsCache({ path: path23 = DEFAULT_CODEX_MODELS_CACHE, read = readFileSync4 } = {}) {
10040
10804
  try {
10041
- const parsed = JSON.parse(read(path20, "utf8"));
10805
+ const parsed = JSON.parse(read(path23, "utf8"));
10042
10806
  return Array.isArray(parsed?.models) ? parsed : null;
10043
10807
  } catch {
10044
10808
  return null;
@@ -10088,7 +10852,7 @@ var init_effort_policy = __esm({
10088
10852
  init_meta_model_catalog();
10089
10853
  RUNG_ORDER = ["R1", "R2", "R3", "R4", "R5"];
10090
10854
  rungIndex = (rung) => RUNG_ORDER.indexOf(rung);
10091
- DEFAULT_CODEX_MODELS_CACHE = join7(homedir6(), ".codex", "models_cache.json");
10855
+ DEFAULT_CODEX_MODELS_CACHE = join8(homedir6(), ".codex", "models_cache.json");
10092
10856
  }
10093
10857
  });
10094
10858
 
@@ -10220,16 +10984,16 @@ var init_role_cost_shadow = __esm({
10220
10984
  // ../../scripts/virtual-office/code-runner/auto-router/auto-router.mjs
10221
10985
  import { readFileSync as readFileSync5, appendFileSync, mkdirSync as mkdirSync4 } from "node:fs";
10222
10986
  import { homedir as homedir7 } from "node:os";
10223
- import { join as join8, dirname as dirname5 } from "node:path";
10224
- import { fileURLToPath as fileURLToPath5 } from "node:url";
10987
+ import { join as join9, dirname as dirname6 } from "node:path";
10988
+ import { fileURLToPath as fileURLToPath7 } from "node:url";
10225
10989
  function getAutoRouterMode(env2 = process.env) {
10226
10990
  const raw = String(env2.VO_CODE_RUNNER_AUTO_ROUTER || "").trim().toLowerCase();
10227
10991
  return MODES.has(raw) ? raw : "off";
10228
10992
  }
10229
10993
  function loadThresholds() {
10230
10994
  if (!cachedThresholds) {
10231
- const here = dirname5(fileURLToPath5(import.meta.url));
10232
- 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"));
10233
10997
  }
10234
10998
  return cachedThresholds;
10235
10999
  }
@@ -10295,15 +11059,15 @@ function formatDecisionReason(decision, maxLen = 480) {
10295
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("; ")}`;
10296
11060
  return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
10297
11061
  }
10298
- function appendDecisionFallback(decision, { path: path20 = DECISION_FALLBACK_PATH, append = appendFileSync, mkdir: mkdir4 = mkdirSync4, task, thresholds, roleCostInputs } = {}) {
11062
+ function appendDecisionFallback(decision, { path: path23 = DECISION_FALLBACK_PATH, append = appendFileSync, mkdir: mkdir4 = mkdirSync4, task, thresholds, roleCostInputs } = {}) {
10299
11063
  try {
10300
- mkdir4(dirname5(path20), { recursive: true });
10301
- append(path20, `${JSON.stringify(decision)}
11064
+ mkdir4(dirname6(path23), { recursive: true });
11065
+ append(path23, `${JSON.stringify(decision)}
10302
11066
  `, "utf8");
10303
11067
  if (isRouterDecision(decision)) {
10304
11068
  try {
10305
11069
  const records = buildShadowRecords({ decision, task, thresholds: thresholds || loadThresholds(), roleCostInputs });
10306
- for (const record of records) append(path20, `${JSON.stringify(record)}
11070
+ for (const record of records) append(path23, `${JSON.stringify(record)}
10307
11071
  `, "utf8");
10308
11072
  } catch {
10309
11073
  }
@@ -10321,7 +11085,7 @@ var init_auto_router = __esm({
10321
11085
  init_effort_policy();
10322
11086
  init_role_cost_shadow();
10323
11087
  ROUTER_VERSION = "0.1.0";
10324
- DECISION_FALLBACK_PATH = join8(homedir7(), ".claude", "vo-auto-router-decisions.jsonl");
11088
+ DECISION_FALLBACK_PATH = join9(homedir7(), ".claude", "vo-auto-router-decisions.jsonl");
10325
11089
  MODES = /* @__PURE__ */ new Set(["off", "shadow", "on"]);
10326
11090
  cachedThresholds = null;
10327
11091
  isRouterDecision = (d) => Boolean(d && typeof d === "object" && typeof d.taskClass === "string" && typeof d.confidence === "number");
@@ -11294,9 +12058,9 @@ var init_inference_task_runner = __esm({
11294
12058
  });
11295
12059
 
11296
12060
  // ../../scripts/virtual-office/code-runner/isolation-audit.mjs
11297
- import fs10 from "node:fs";
12061
+ import fs12 from "node:fs";
11298
12062
  import fsp11 from "node:fs/promises";
11299
- import path17 from "node:path";
12063
+ import path20 from "node:path";
11300
12064
  async function defaultRun(command, args, cwd, options = {}) {
11301
12065
  return runProcess2(command, args, { cwd, timeout: 6e4, ...options });
11302
12066
  }
@@ -11309,7 +12073,7 @@ async function canonicalRootForWorktree(worktreeDir, run) {
11309
12073
  "--path-format=absolute",
11310
12074
  "--git-common-dir"
11311
12075
  ])).trim();
11312
- const root = path17.dirname(commonDir);
12076
+ const root = path20.dirname(commonDir);
11313
12077
  return samePath2(root, worktreeDir) ? null : root;
11314
12078
  }
11315
12079
  async function snapshot(root, run) {
@@ -11351,21 +12115,21 @@ async function changedPaths(root, run) {
11351
12115
  }
11352
12116
  async function quarantineCanonicalWrites({ baseline, worktreeDir, taskId, run, now }) {
11353
12117
  const paths = await changedPaths(baseline.root, run);
11354
- const quarantineDir = path17.join(
11355
- path17.dirname(worktreeDir),
12118
+ const quarantineDir = path20.join(
12119
+ path20.dirname(worktreeDir),
11356
12120
  ".canonical-recovery",
11357
12121
  `${String(taskId || "unknown").replace(/[^a-z0-9-]/gi, "-")}-${now().toISOString().replace(/[:.]/g, "-")}`
11358
12122
  );
11359
12123
  await fsp11.mkdir(quarantineDir, { recursive: true });
11360
12124
  const patch = await git2(run, baseline.root, ["diff", "--binary", "HEAD"], { raw: true });
11361
- await fsp11.writeFile(path17.join(quarantineDir, "tracked.patch"), patch, "utf8");
12125
+ await fsp11.writeFile(path20.join(quarantineDir, "tracked.patch"), patch, "utf8");
11362
12126
  for (const relative of paths.untracked) {
11363
- const source = path17.join(baseline.root, relative);
11364
- const target = path17.join(quarantineDir, "untracked", relative);
11365
- await fsp11.mkdir(path17.dirname(target), { recursive: true });
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 });
11366
12130
  await fsp11.copyFile(source, target);
11367
12131
  }
11368
- await fsp11.writeFile(path17.join(quarantineDir, "manifest.json"), `${JSON.stringify({
12132
+ await fsp11.writeFile(path20.join(quarantineDir, "manifest.json"), `${JSON.stringify({
11369
12133
  taskId,
11370
12134
  canonicalRoot: baseline.root,
11371
12135
  canonicalHead: baseline.head,
@@ -11387,9 +12151,9 @@ async function restoreExactCanonicalPaths(baseline, evidence, run) {
11387
12151
  ]);
11388
12152
  }
11389
12153
  for (const relative of evidence.untracked) {
11390
- const target = path17.resolve(baseline.root, relative);
11391
- const prefix = `${path17.resolve(baseline.root)}${path17.sep}`;
11392
- if (!target.startsWith(prefix) || !fs10.existsSync(target)) continue;
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;
11393
12157
  await fsp11.rm(target, { force: true });
11394
12158
  }
11395
12159
  }
@@ -11425,7 +12189,7 @@ var init_isolation_audit = __esm({
11425
12189
  init_process_runner2();
11426
12190
  splitZ2 = (value) => String(value || "").split("\0").map((item) => item.trim()).filter(Boolean);
11427
12191
  samePath2 = (left, right) => {
11428
- const [a, b] = [left, right].map((value) => path17.resolve(value));
12192
+ const [a, b] = [left, right].map((value) => path20.resolve(value));
11429
12193
  return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
11430
12194
  };
11431
12195
  }
@@ -11785,7 +12549,7 @@ var init_publication_outcome = __esm({
11785
12549
 
11786
12550
  // ../../scripts/virtual-office/code-runner/committed-scratch-cleanup.mjs
11787
12551
  import fsp12 from "node:fs/promises";
11788
- import path18 from "node:path";
12552
+ import path21 from "node:path";
11789
12553
  function defaultRun2(command, args, cwd, options = {}) {
11790
12554
  return runProcess2(command, args, { cwd, ...options });
11791
12555
  }
@@ -11793,13 +12557,13 @@ async function resolveSafeScratchTarget(worktreeDir, file) {
11793
12557
  if (!isAgentScratch(file)) {
11794
12558
  throw new Error(`refusing to remove non-scratch publication path: ${file}`);
11795
12559
  }
11796
- const root = path18.resolve(worktreeDir);
11797
- const target = path18.resolve(root, file);
11798
- const relative = path18.relative(root, target);
11799
- if (!relative || relative.startsWith(`..${path18.sep}`) || path18.isAbsolute(relative)) {
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)) {
11800
12564
  throw new Error(`refusing to remove publication scratch outside worktree: ${file}`);
11801
12565
  }
11802
- for (let cursor = target; cursor !== root; cursor = path18.dirname(cursor)) {
12566
+ for (let cursor = target; cursor !== root; cursor = path21.dirname(cursor)) {
11803
12567
  try {
11804
12568
  if ((await fsp12.lstat(cursor)).isSymbolicLink()) {
11805
12569
  throw new Error(`refusing to follow symlink while removing publication scratch: ${file}`);
@@ -11919,9 +12683,9 @@ var init_publication_scope = __esm({
11919
12683
  });
11920
12684
 
11921
12685
  // ../../scripts/virtual-office/code-runner/recovery-ledger.mjs
11922
- import fs11 from "node:fs";
12686
+ import fs13 from "node:fs";
11923
12687
  import fsp13 from "node:fs/promises";
11924
- import path19 from "node:path";
12688
+ import path22 from "node:path";
11925
12689
  function recoveryTaskId(prompt) {
11926
12690
  const match = String(prompt || "").match(/VO_RECOVERY_FROM_CODE_TASK:\s*([0-9a-f-]{36})/i);
11927
12691
  return match ? match[1].toLowerCase() : null;
@@ -11935,10 +12699,10 @@ function cloneLeaf(repo) {
11935
12699
  function recoveryLedgerCandidates(repo, clonesRoot2) {
11936
12700
  const leaf = cloneLeaf(repo);
11937
12701
  if (!leaf || !clonesRoot2) return [];
11938
- const canonical = path19.join(clonesRoot2, leaf);
12702
+ const canonical = path22.join(clonesRoot2, leaf);
11939
12703
  return [
11940
- path19.join(clonesRoot2, ".agent-worktrees", leaf, "recovery-ledger.jsonl"),
11941
- path19.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")
11942
12706
  ];
11943
12707
  }
11944
12708
  async function readLedger(file, readFile5) {
@@ -11957,7 +12721,7 @@ async function readLedger(file, readFile5) {
11957
12721
  async function findPreservedRecovery(task, {
11958
12722
  clonesRoot: clonesRoot2 = process.env.VO_CODE_RUNNER_CLONES_ROOT || "",
11959
12723
  readFile: readFile5 = fsp13.readFile,
11960
- exists = fs11.existsSync
12724
+ exists = fs13.existsSync
11961
12725
  } = {}) {
11962
12726
  const resumedFrom = /^[0-9a-f-]{36}$/iu.test(String(task.resumed_from || "")) ? String(task.resumed_from).toLowerCase() : null;
11963
12727
  const originalTaskId = recoveryTaskId(task.prompt) ?? resumedFrom;
@@ -12272,7 +13036,7 @@ var init_cancellation_probe = __esm({
12272
13036
 
12273
13037
  // ../../scripts/virtual-office/code-runner/detached-economics-spool.mjs
12274
13038
  import { homedir as homedir8 } from "node:os";
12275
- import { dirname as dirname6, join as join9 } from "node:path";
13039
+ import { dirname as dirname7, join as join10 } from "node:path";
12276
13040
  import { mkdir as mkdir3, readFile as readFile4, rename as rename2, writeFile as writeFile3 } from "node:fs/promises";
12277
13041
  function withLock(operation) {
12278
13042
  const result = serialized.then(operation, operation);
@@ -12290,7 +13054,7 @@ async function readEntries(file) {
12290
13054
  }
12291
13055
  }
12292
13056
  async function writeEntries(file, entries) {
12293
- await mkdir3(dirname6(file), { recursive: true });
13057
+ await mkdir3(dirname7(file), { recursive: true });
12294
13058
  const temp = `${file}.${process.pid}.tmp`;
12295
13059
  await writeFile3(temp, `${JSON.stringify(entries)}
12296
13060
  `, "utf8");
@@ -12336,13 +13100,13 @@ var DEFAULT_FILE, serialized;
12336
13100
  var init_detached_economics_spool = __esm({
12337
13101
  "../../scripts/virtual-office/code-runner/detached-economics-spool.mjs"() {
12338
13102
  "use strict";
12339
- DEFAULT_FILE = join9(homedir8(), ".vo", "detached-run-economics.json");
13103
+ DEFAULT_FILE = join10(homedir8(), ".vo", "detached-run-economics.json");
12340
13104
  serialized = Promise.resolve();
12341
13105
  }
12342
13106
  });
12343
13107
 
12344
13108
  // ../../scripts/virtual-office/code-runner/killed-run-outcome.mjs
12345
- import { randomUUID as randomUUID5 } from "node:crypto";
13109
+ import { randomUUID as randomUUID6 } from "node:crypto";
12346
13110
  async function handleKilledRun({
12347
13111
  client,
12348
13112
  id,
@@ -12379,7 +13143,7 @@ async function handleKilledRun({
12379
13143
  };
12380
13144
  }
12381
13145
  if (reason === "claim_authority_changed") {
12382
- const occurrenceId = randomUUID5();
13146
+ const occurrenceId = randomUUID6();
12383
13147
  const economics = {
12384
13148
  occurrence_id: occurrenceId,
12385
13149
  runner_id: runnerId,
@@ -12479,13 +13243,13 @@ var init_runner_runtime_limits = __esm({
12479
13243
  });
12480
13244
 
12481
13245
  // ../../scripts/virtual-office/code-runner/daemon-config.mjs
12482
- import os4 from "node:os";
13246
+ import os5 from "node:os";
12483
13247
  function loadCodeRunnerConfig(env2 = process.env, { log: log2 = () => {
12484
13248
  } } = {}) {
12485
13249
  const servedOperators = parseList(env2.VO_CODE_RUNNER_OPERATOR_IDS);
12486
13250
  const allowAmbientGithub = env2.VO_CODE_RUNNER_ALLOW_AMBIENT_GH === "1";
12487
13251
  return {
12488
- runnerId: env2.VO_CODE_RUNNER_ID || `vo-code-runner-${os4.hostname()}`,
13252
+ runnerId: env2.VO_CODE_RUNNER_ID || `vo-code-runner-${os5.hostname()}`,
12489
13253
  ...resolveRunner(env2, { warn: (message) => log2(`agent-select: ${message}`) }),
12490
13254
  permissionMode: env2.VO_CODE_RUNNER_PERMISSION_MODE || "acceptEdits",
12491
13255
  maxConcurrency: Math.max(1, Number(env2.VO_CODE_TASK_MAX_CONCURRENCY || 2) || 2),
@@ -12495,7 +13259,7 @@ function loadCodeRunnerConfig(env2 = process.env, { log: log2 = () => {
12495
13259
  requireGithubAppAuth: !allowAmbientGithub && servedOperators.length > 0,
12496
13260
  allowAmbientGithub,
12497
13261
  sessionForwardSec: Math.max(0, Number(env2.VO_SESSION_FORWARD_SEC ?? 30) || 0),
12498
- 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()}`,
12499
13263
  cancelPollMs: Math.max(1e3, Number(env2.VO_CODE_RUNNER_CANCEL_POLL_MS || 2500) || 2500),
12500
13264
  maxWallClockMs: resolveMaxWallClockMs(env2.VO_CODE_RUNNER_MAX_WALL_CLOCK_MS),
12501
13265
  watchEnabled: env2.VO_CODE_RUNNER_WATCH !== "0",
@@ -12674,10 +13438,10 @@ var init_task_worktree_preparation = __esm({
12674
13438
  // ../../scripts/virtual-office/code-runner-daemon.mjs
12675
13439
  var code_runner_daemon_exports = {};
12676
13440
  __export(code_runner_daemon_exports, {
12677
- main: () => main
13441
+ main: () => main2
12678
13442
  });
12679
- import { randomUUID as randomUUID6 } from "node:crypto";
12680
- import { fileURLToPath as fileURLToPath6 } from "node:url";
13443
+ import { randomUUID as randomUUID7 } from "node:crypto";
13444
+ import { fileURLToPath as fileURLToPath8 } from "node:url";
12681
13445
  function log(msg) {
12682
13446
  console.log(`[code-runner ${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}`);
12683
13447
  }
@@ -12819,16 +13583,12 @@ async function processOneTask(client, task, cfg, runnerInstanceId, swarmAdmissio
12819
13583
  return;
12820
13584
  }
12821
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;
12822
13588
  const publicationTarget = await resolvePublicationTarget({ task, continuationRestore, worktreeDir: wt.worktreeDir, githubToken, allowAmbientGithubFallback: cfg.allowAmbientGithub });
12823
13589
  const localBranch = await resolveOrCreateBranchAsync(wt.worktreeDir, "vo/code-task");
12824
13590
  const publicationBranch = publicationTarget.targetBranch || localBranch;
12825
- if (!await recordPublicationIntent({
12826
- client,
12827
- id,
12828
- branch: publicationBranch,
12829
- safeProgress,
12830
- log
12831
- })) {
13591
+ if (!await recordPublicationIntent({ client, id, branch: publicationBranch, safeProgress, log })) {
12832
13592
  await reportCancelledRun({ client, id, run, safeProgress, log });
12833
13593
  return;
12834
13594
  }
@@ -12903,10 +13663,10 @@ Closes #${publicationTarget.supersedesPrNumber}` : "";
12903
13663
  }
12904
13664
  }
12905
13665
  }
12906
- async function main({ env: env2 = process.env, once: once2 = false } = {}) {
13666
+ async function main2({ env: env2 = process.env, once: once2 = false } = {}) {
12907
13667
  const cfg = loadCodeRunnerConfig(env2, { log });
12908
13668
  await sweepStaleTaskAttachmentDirectories().catch((error) => log(`stale attachment cleanup failed: ${error.message}`));
12909
- const runnerInstanceId = randomUUID6();
13669
+ const runnerInstanceId = randomUUID7();
12910
13670
  const client = createControlPlaneClient({
12911
13671
  env: env2,
12912
13672
  runnerId: cfg.runnerId,
@@ -13049,7 +13809,7 @@ async function main({ env: env2 = process.env, once: once2 = false } = {}) {
13049
13809
  if (controlServer) controlServer.close();
13050
13810
  log("stopped");
13051
13811
  }
13052
- var RATE_LIMIT_RESUME_ENABLED, sleep2, safeProgress, invokedDirectly;
13812
+ var RATE_LIMIT_RESUME_ENABLED, sleep2, safeProgress, invokedDirectly2;
13053
13813
  var init_code_runner_daemon = __esm({
13054
13814
  "../../scripts/virtual-office/code-runner-daemon.mjs"() {
13055
13815
  "use strict";
@@ -13060,6 +13820,8 @@ var init_code_runner_daemon = __esm({
13060
13820
  init_resolve_runner();
13061
13821
  init_rate_limit_resume();
13062
13822
  init_publish();
13823
+ init_test_gen_gate();
13824
+ init_completion_gate();
13063
13825
  init_orphan_agent_reaper();
13064
13826
  init_publish_async();
13065
13827
  init_task_prompt();
@@ -13097,11 +13859,11 @@ var init_code_runner_daemon = __esm({
13097
13859
  RATE_LIMIT_RESUME_ENABLED = process.env.VO_RATE_LIMIT_RESUME !== "0";
13098
13860
  sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
13099
13861
  safeProgress = makeSafeProgress(log);
13100
- 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).
13101
13863
  import.meta.url.endsWith("code-runner-daemon.mjs");
13102
- if (invokedDirectly) {
13864
+ if (invokedDirectly2) {
13103
13865
  const once2 = process.argv.includes("--once");
13104
- main({ once: once2 }).catch((err) => {
13866
+ main2({ once: once2 }).catch((err) => {
13105
13867
  console.error("[code-runner] fatal:", err);
13106
13868
  process.exit(1);
13107
13869
  });
@@ -13468,8 +14230,8 @@ var env = {
13468
14230
  ...pairedOperatorId ? { VO_CODE_RUNNER_OPERATOR_IDS: pairedOperatorId } : {}
13469
14231
  };
13470
14232
  var once = process.argv.includes("--once");
13471
- var { main: main2 } = await Promise.resolve().then(() => (init_code_runner_daemon(), code_runner_daemon_exports));
13472
- 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) => {
13473
14235
  console.error("[vo-mcp runner] fatal:", err);
13474
14236
  process.exit(1);
13475
14237
  });