@algosuite/vo-mcp 0.2.0-beta.21 → 0.2.0-beta.22

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.
@@ -2241,6 +2241,37 @@ var init_spend_cap_shim = __esm({
2241
2241
  }
2242
2242
  });
2243
2243
 
2244
+ // ../../scripts/virtual-office/code-runner/installation-token.mjs
2245
+ async function fetchInstallationToken({ req, required = false, readOnly = false, repo = null }) {
2246
+ const fail = (reason) => {
2247
+ if (required) throw new Error(`installation-token required: ${reason}`);
2248
+ return null;
2249
+ };
2250
+ try {
2251
+ const res = await req(
2252
+ "POST",
2253
+ "/api/v1/github/installation-token",
2254
+ readOnly ? { scope: "read", ...repo ? { repo } : {} } : {},
2255
+ readOnly ? { timeoutMs: READ_TOKEN_TIMEOUT_MS } : {}
2256
+ );
2257
+ if (!res.ok) return fail(`HTTP ${res.status}`);
2258
+ const json = await res.json();
2259
+ if (!json || !json.token) return fail("missing token");
2260
+ if (readOnly && json.scope !== "read") return fail("control plane did not confirm a read-only grant");
2261
+ return { token: json.token, expiresAt: json.expires_at || null };
2262
+ } catch (err) {
2263
+ if (required) throw err;
2264
+ return null;
2265
+ }
2266
+ }
2267
+ var READ_TOKEN_TIMEOUT_MS;
2268
+ var init_installation_token = __esm({
2269
+ "../../scripts/virtual-office/code-runner/installation-token.mjs"() {
2270
+ "use strict";
2271
+ READ_TOKEN_TIMEOUT_MS = 15e3;
2272
+ }
2273
+ });
2274
+
2244
2275
  // src/runner/control-plane-auth-stub.mjs
2245
2276
  var control_plane_auth_stub_exports = {};
2246
2277
  __export(control_plane_auth_stub_exports, {
@@ -2564,37 +2595,9 @@ function createControlPlaneClient({
2564
2595
  const json = await res.json();
2565
2596
  return json?.action || null;
2566
2597
  },
2567
- /**
2568
- * Mint a short-lived (~1h), repo-scoped GitHub App installation token for
2569
- * THIS runner's operator (M3). The control-plane keys the mint on the
2570
- * authenticated operator (ctx.operator_id), so the token covers only that
2571
- * operator's installation.
2572
- *
2573
- * Returns { token, expiresAt } on success. In legacy/admin mode it returns
2574
- * null on a miss so the caller may use ambient `gh`. In scoped-operator mode
2575
- * callers pass `{ required: true }`, which fails closed instead of letting a
2576
- * missing/mis-scoped installation fall through to the runner machine's `gh`.
2577
- *
2578
- * The minted token is only usable for push + PR if the GitHub App grants
2579
- * BOTH `Contents: write` (git push) AND `Pull requests: write` (gh pr
2580
- * create) — see docs/vo/github-app-setup-2026-06-18.md. A token missing
2581
- * either scope fails at push (→ ambient fallback) or at `gh pr create`.
2582
- */
2583
- async getInstallationToken({ required = false } = {}) {
2584
- const fail = (reason) => {
2585
- if (required) throw new Error(`installation-token required: ${reason}`);
2586
- return null;
2587
- };
2588
- try {
2589
- const res = await req("POST", "/api/v1/github/installation-token", {});
2590
- if (!res.ok) return fail(`HTTP ${res.status}`);
2591
- const json = await res.json();
2592
- if (!json || !json.token) return fail("missing token");
2593
- return { token: json.token, expiresAt: json.expires_at || null };
2594
- } catch (err) {
2595
- if (required) throw err;
2596
- return null;
2597
- }
2598
+ /** Mint a GitHub App installation token — see installation-token.mjs. */
2599
+ async getInstallationToken({ required = false, readOnly = false, repo = null } = {}) {
2600
+ return fetchInstallationToken({ req, required, readOnly, repo });
2598
2601
  },
2599
2602
  /**
2600
2603
  * Read the operator's dispatch-mode config (Fast→Ultracode effort setting).
@@ -2617,6 +2620,7 @@ var cachedFirebaseToken;
2617
2620
  var init_control_plane_client = __esm({
2618
2621
  "../../scripts/virtual-office/code-runner/control-plane-client.mjs"() {
2619
2622
  "use strict";
2623
+ init_installation_token();
2620
2624
  cachedFirebaseToken = null;
2621
2625
  }
2622
2626
  });
@@ -3844,6 +3848,11 @@ function itemText(item) {
3844
3848
  }
3845
3849
  return "";
3846
3850
  }
3851
+ function parseCodexVersion(stdout) {
3852
+ if (typeof stdout !== "string") return null;
3853
+ const match = stdout.match(/\b(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)\b/u);
3854
+ return match ? match[1] : null;
3855
+ }
3847
3856
  function parseCodexEvent(line) {
3848
3857
  const trimmed = String(line || "").trim();
3849
3858
  if (!trimmed) return null;
@@ -3938,7 +3947,7 @@ var init_codex_runner = __esm({
3938
3947
  ...this.getSpawnOptions({ bin }),
3939
3948
  windowsHide: true,
3940
3949
  timeout: 3e3,
3941
- stdio: "ignore"
3950
+ encoding: "utf8"
3942
3951
  });
3943
3952
  if (version.error) {
3944
3953
  return { installed: false, authenticated: false, message: `codex not found on PATH: ${version.error.message}` };
@@ -3946,6 +3955,8 @@ var init_codex_runner = __esm({
3946
3955
  if (version.status !== 0) {
3947
3956
  return { installed: true, authenticated: false, message: "codex exists but --version failed (auth unclear)" };
3948
3957
  }
3958
+ const cliVersion = parseCodexVersion(version.stdout);
3959
+ const versionField = cliVersion ? { version: cliVersion } : {};
3949
3960
  const login = this.spawn(bin, ["login", "status"], {
3950
3961
  ...this.getSpawnOptions({ bin }),
3951
3962
  windowsHide: true,
@@ -3960,18 +3971,21 @@ ${login.stderr || ""}`.trim();
3960
3971
  return {
3961
3972
  installed: true,
3962
3973
  authenticated: true,
3974
+ ...versionField,
3963
3975
  message: "codex API key available (no persisted ChatGPT login)"
3964
3976
  };
3965
3977
  }
3966
3978
  return {
3967
3979
  installed: true,
3968
3980
  authenticated: false,
3981
+ ...versionField,
3969
3982
  message: output || login.error?.message || "codex is installed but not logged in"
3970
3983
  };
3971
3984
  }
3972
3985
  return {
3973
3986
  installed: true,
3974
3987
  authenticated: true,
3988
+ ...versionField,
3975
3989
  message: output || "codex login status succeeded"
3976
3990
  };
3977
3991
  } catch (err) {
@@ -6422,7 +6436,12 @@ async function collectAgentAvailability({
6422
6436
  const probes = agents.map(async (agent) => {
6423
6437
  try {
6424
6438
  const r = await runnerFor(agent).checkAuth();
6425
- return { agent, installed: Boolean(r?.installed), authenticated: Boolean(r?.authenticated) };
6439
+ return {
6440
+ agent,
6441
+ installed: Boolean(r?.installed),
6442
+ authenticated: Boolean(r?.authenticated),
6443
+ ...typeof r?.version === "string" && r.version ? { version: r.version } : {}
6444
+ };
6426
6445
  } catch {
6427
6446
  return { agent, installed: false, authenticated: false };
6428
6447
  }
@@ -8823,11 +8842,41 @@ var init_reconnect_backoff = __esm({
8823
8842
  }
8824
8843
  });
8825
8844
 
8845
+ // ../../scripts/virtual-office/code-runner/redact-tokens.mjs
8846
+ function redactSecrets(text) {
8847
+ if (typeof text !== "string" || text.length === 0) return text;
8848
+ let out = text;
8849
+ for (const [re, mask] of TOKEN_PATTERNS) out = out.replace(re, mask);
8850
+ return out;
8851
+ }
8852
+ function redactPatch(patch) {
8853
+ if (!patch || typeof patch !== "object") return patch;
8854
+ const out = Array.isArray(patch) ? [...patch] : { ...patch };
8855
+ for (const [k, v] of Object.entries(out)) {
8856
+ if (typeof v === "string") out[k] = redactSecrets(v);
8857
+ else if (v && typeof v === "object") out[k] = redactPatch(v);
8858
+ }
8859
+ return out;
8860
+ }
8861
+ var TOKEN_PATTERNS;
8862
+ var init_redact_tokens = __esm({
8863
+ "../../scripts/virtual-office/code-runner/redact-tokens.mjs"() {
8864
+ "use strict";
8865
+ TOKEN_PATTERNS = [
8866
+ [/\b(?:gh[oprsu]|vocred|npm)_[A-Za-z0-9._-]{10,}\b/gu, "[REDACTED]"],
8867
+ [/\bgithub_pat_[A-Za-z0-9_]{10,}\b/gu, "[REDACTED]"],
8868
+ // Keep the scheme so the line still reads as an auth header, matching
8869
+ // sanitizeMaintenanceDiagnostic's behaviour.
8870
+ [/\bBearer\s+[A-Za-z0-9._~+/-]{10,}=*/giu, "Bearer [REDACTED]"]
8871
+ ];
8872
+ }
8873
+ });
8874
+
8826
8875
  // ../../scripts/virtual-office/code-runner/task-helpers.mjs
8827
8876
  function makeSafeProgress(log3) {
8828
8877
  return async (client, id, patch) => {
8829
8878
  try {
8830
- const r = await client.postProgress(id, patch);
8879
+ const r = await client.postProgress(id, redactPatch(patch));
8831
8880
  if (r && r.terminal) log3(`task ${id} is terminal server-side; stopping updates`);
8832
8881
  return r;
8833
8882
  } catch (err) {
@@ -8853,20 +8902,34 @@ function buildPrBody(task, run, files, { armAutoMerge = false } = {}) {
8853
8902
  "### Prompt",
8854
8903
  "",
8855
8904
  "```",
8856
- String(task.prompt).slice(0, 2e3),
8905
+ redactSecrets(String(task.prompt)).slice(0, 2e3),
8857
8906
  "```",
8858
8907
  "",
8859
8908
  "### Agent summary",
8860
8909
  "",
8861
- String(run.summary || "").slice(0, 2e3),
8910
+ redactSecrets(String(run.summary || "")).slice(0, 2e3),
8862
8911
  "",
8863
8912
  "---",
8864
8913
  armAutoMerge ? "_Opened by the AlgoHQ code-runner daemon. After CI passes, the watcher must obtain a durable consensus receipt and merge the exact verified SHA._" : "_Opened by the AlgoHQ code-runner daemon. This PR awaits the verify-before-act gate / operator review \u2014 it is NOT auto-merged._"
8865
8914
  ].filter((l) => l !== "").join("\n");
8866
8915
  }
8916
+ async function mintRunnerGithubTokens({ client, taskId, log: log3, repo = null, requirePublish = false }) {
8917
+ const publishToken = (await client.getInstallationToken({ required: requirePublish }))?.token ?? null;
8918
+ let agentReadToken = null;
8919
+ let reason = null;
8920
+ try {
8921
+ agentReadToken = (await client.getInstallationToken({ readOnly: true, repo }))?.token ?? null;
8922
+ if (!agentReadToken) reason = "control plane returned no confirmed read-only grant";
8923
+ } catch (err) {
8924
+ reason = err?.message ?? String(err);
8925
+ }
8926
+ if (!agentReadToken) log3(`task ${taskId}: no GitHub read access for the agent (${reason}); it cannot read a private repo`);
8927
+ return { publishToken, agentReadToken };
8928
+ }
8867
8929
  var init_task_helpers = __esm({
8868
8930
  "../../scripts/virtual-office/code-runner/task-helpers.mjs"() {
8869
8931
  "use strict";
8932
+ init_redact_tokens();
8870
8933
  }
8871
8934
  });
8872
8935
 
@@ -8882,8 +8945,12 @@ function safeBaseEnv(env2 = {}) {
8882
8945
  }
8883
8946
  return result;
8884
8947
  }
8885
- function buildAgentProcessEnv(env2, { agent = "agent", runnerId = "vo-runner", taskId = "task" } = {}) {
8948
+ function buildAgentProcessEnv(env2, { agent = "agent", runnerId = "vo-runner", taskId = "task", githubReadToken = null } = {}) {
8886
8949
  const base = safeBaseEnv(env2);
8950
+ if (typeof githubReadToken === "string" && githubReadToken) {
8951
+ base.GH_TOKEN = githubReadToken;
8952
+ base.GITHUB_TOKEN = githubReadToken;
8953
+ }
8887
8954
  if (String(env2?.AGENT_ID || "").trim()) return { ...base, AGENT_ID: env2.AGENT_ID };
8888
8955
  const generated = [
8889
8956
  "vo",
@@ -9574,7 +9641,7 @@ async function processOneTask(client, task, cfg) {
9574
9641
  const wt = await Promise.resolve(createFixWorktree("code-task", { source: id.slice(0, 8), repo: task.repo }));
9575
9642
  worktreeName = wt.worktreeName;
9576
9643
  if (!worktreeName || !wt.worktreeDir) throw new Error("worktree isolation failure \u2014 refusing to run in the main tree");
9577
- const githubToken = (await client.getInstallationToken({ required: cfg.requireGithubAppAuth }))?.token ?? null;
9644
+ const { publishToken: githubToken, agentReadToken: agentGithubReadToken } = await mintRunnerGithubTokens({ client, taskId: id, log: log2, repo: task.repo, requirePublish: cfg.requireGithubAppAuth });
9578
9645
  const parentTask = task.resumed_from ? await client.getTask(task.resumed_from).catch(() => null) : null;
9579
9646
  const continuationRestore = await prepareContinuationBranch(wt.worktreeDir, {
9580
9647
  task,
@@ -9609,7 +9676,7 @@ async function processOneTask(client, task, cfg) {
9609
9676
  model,
9610
9677
  effort: effectiveEffort,
9611
9678
  maxBudgetUsd: effectiveMaxBudgetUsd,
9612
- env: buildAgentProcessEnv(process.env, { agent: cfg.agent, runnerId: cfg.runnerId, taskId: id }),
9679
+ env: buildAgentProcessEnv(process.env, { agent: cfg.agent, runnerId: cfg.runnerId, taskId: id, githubReadToken: agentGithubReadToken }),
9613
9680
  onProgress: (text) => {
9614
9681
  void safeProgress(client, id, runnerStagePatch("agent_working", text));
9615
9682
  },