@algosuite/vo-mcp 0.2.0-beta.56 → 0.2.0-beta.57

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.
@@ -675,12 +675,15 @@ function installWindowsAutostart(runnerCommand, log2, env2) {
675
675
  log2(`\u26A0 Could not remove legacy launcher ${legacyCmdPath} (${error instanceof Error ? error.message : String(error)}); remove it manually to avoid a double start`);
676
676
  }
677
677
  }
678
- const hiddenCommand = `cmd /c ${runnerCommand} >> "%USERPROFILE%\\.claude\\vo-runner.log" 2>&1`.replace(/"/g, '""');
678
+ const runnerConsoleCommand = `cmd /c ${runnerCommand} >> "%USERPROFILE%\\.claude\\vo-runner.log" 2>&1`.replace(/"/g, '""');
679
679
  const launcherContent = `' Auto-start launcher for vo-mcp runner
680
680
  ' Created by vo-mcp autostart installer
681
681
  ' Keepalive supervisor: restarts the runner if it exits (parity with launchd
682
682
  ' KeepAlive on macOS and systemd Restart=on-failure on Linux).
683
- ' To stop: create %USERPROFILE%\\.claude\\vo-runner.stop, or end wscript.exe.
683
+ ' Runs the runner in a MINIMIZED (style 7), never a hidden (style 0) window:
684
+ ' hidden script-host exec trips Defender's PowhidSubExec.B heuristic and gets
685
+ ' blocked at logon. To stop: create %USERPROFILE%\\.claude\\vo-runner.stop, or
686
+ ' end wscript.exe.
684
687
  Dim sh, fso, stopFile, backoff, startedAt, ranMs
685
688
  Set sh = CreateObject("WScript.Shell")
686
689
  Set fso = CreateObject("Scripting.FileSystemObject")
@@ -694,7 +697,7 @@ Do
694
697
  WScript.Quit 0
695
698
  End If
696
699
  startedAt = Timer
697
- sh.Run "${hiddenCommand}", 0, True
700
+ sh.Run "${runnerConsoleCommand}", 7, True
698
701
  ranMs = (Timer - startedAt) * 1000
699
702
  If ranMs < 0 Then ranMs = ${WINDOWS_HEALTHY_RUN_MS}
700
703
  If ranMs >= ${WINDOWS_HEALTHY_RUN_MS} Then
@@ -1999,12 +2002,12 @@ var init_pnpm_materialize = __esm({
1999
2002
  });
2000
2003
 
2001
2004
  // src/runner/pnpm-hydration.mjs
2002
- import { createHash } from "node:crypto";
2005
+ import { createHash as createHash2 } from "node:crypto";
2003
2006
  import fs3 from "node:fs";
2004
2007
  import fsp5 from "node:fs/promises";
2005
2008
  import path5 from "node:path";
2006
2009
  function hashText(text) {
2007
- return createHash("sha256").update(String(text)).digest("hex");
2010
+ return createHash2("sha256").update(String(text)).digest("hex");
2008
2011
  }
2009
2012
  function statePath(root) {
2010
2013
  return path5.join(root, ".agent-worktrees", "runner-pnpm-hydration.json");
@@ -2314,7 +2317,7 @@ var init_pnpm_hydration = __esm({
2314
2317
  });
2315
2318
 
2316
2319
  // src/runner/worktree-paths.mjs
2317
- import { createHash as createHash2 } from "node:crypto";
2320
+ import { createHash as createHash3 } from "node:crypto";
2318
2321
  import path6 from "node:path";
2319
2322
  function samePath2(left, right) {
2320
2323
  const a = path6.resolve(left);
@@ -2332,7 +2335,7 @@ function worktreePoolForRoot(root, { clonesRootDir = process.env.VO_CODE_RUNNER_
2332
2335
  return path6.join(canonicalRoot, ".agent-worktrees");
2333
2336
  }
2334
2337
  function worktreeDirForName(root, worktreeName, options = {}) {
2335
- const leaf = createHash2("sha256").update(String(worktreeName)).digest("hex").slice(0, 16);
2338
+ const leaf = createHash3("sha256").update(String(worktreeName)).digest("hex").slice(0, 16);
2336
2339
  return path6.join(worktreePoolForRoot(root, options), leaf);
2337
2340
  }
2338
2341
  function recoveryLedgerPathForRoot(root, options = {}) {
@@ -8666,7 +8669,7 @@ var init_task_prompt = __esm({
8666
8669
  });
8667
8670
 
8668
8671
  // ../../scripts/virtual-office/code-runner/task-attachments.mjs
8669
- import { createHash as createHash3, randomUUID as randomUUID2 } from "node:crypto";
8672
+ import { createHash as createHash4, randomUUID as randomUUID3 } from "node:crypto";
8670
8673
  import { chmod, mkdir, mkdtemp, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
8671
8674
  import os2 from "node:os";
8672
8675
  import path16 from "node:path";
@@ -8690,7 +8693,7 @@ async function createAttachmentDirectory(taskId, tempRoot) {
8690
8693
  const root = path16.resolve(tempRoot);
8691
8694
  await mkdir(root, { recursive: true });
8692
8695
  const directory = await mkdtemp(path16.join(root, `${DIRECTORY_PREFIX}${safeTaskToken(taskId)}-`));
8693
- const marker = JSON.stringify({ owner: MARKER_OWNER, token: randomUUID2(), directory: path16.basename(directory), created_at: (/* @__PURE__ */ new Date()).toISOString() });
8696
+ const marker = JSON.stringify({ owner: MARKER_OWNER, token: randomUUID3(), directory: path16.basename(directory), created_at: (/* @__PURE__ */ new Date()).toISOString() });
8694
8697
  await writeFile(path16.join(directory, MARKER_FILE), marker, { encoding: "utf8", mode: 384 });
8695
8698
  return { directory, marker, tempRoot: root };
8696
8699
  }
@@ -8766,7 +8769,7 @@ async function materializeTaskAttachments(client, task, { tempRoot = os2.tmpdir(
8766
8769
  const content = await client.downloadTaskAttachment(task.code_task_id, ref.attachment_id);
8767
8770
  if (!Buffer.isBuffer(content)) throw new Error(`attachment ${ref.attachment_id} did not return binary content`);
8768
8771
  if (content.byteLength !== ref.size_bytes) throw new Error(`attachment ${ref.attachment_id} size mismatch`);
8769
- const sha256 = createHash3("sha256").update(content).digest("hex");
8772
+ const sha256 = createHash4("sha256").update(content).digest("hex");
8770
8773
  if (sha256 !== ref.sha256) throw new Error(`attachment ${ref.attachment_id} sha256 mismatch`);
8771
8774
  const name = sanitizeTaskAttachmentName(ref.name, index);
8772
8775
  const filePath = path16.join(state.directory, name);
@@ -8797,9 +8800,9 @@ var init_task_attachments = __esm({
8797
8800
  import { homedir as homedir7 } from "node:os";
8798
8801
  import { join as join11 } from "node:path";
8799
8802
  import { readdir as readdir2, readFile as readFile2, unlink, writeFile as writeFile2 } from "node:fs/promises";
8800
- import { createHash as createHash4 } from "node:crypto";
8803
+ import { createHash as createHash5 } from "node:crypto";
8801
8804
  function deriveUuid(seed) {
8802
- const h = createHash4("sha256").update(seed).digest("hex");
8805
+ const h = createHash5("sha256").update(seed).digest("hex");
8803
8806
  return `${h.slice(0, 8)}-${h.slice(8, 12)}-5${h.slice(13, 16)}-${(parseInt(h.slice(16, 18), 16) & 63 | 128).toString(16)}${h.slice(18, 20)}-${h.slice(20, 32)}`;
8804
8807
  }
8805
8808
  function spoolToCloud(record, ids) {
@@ -10521,7 +10524,7 @@ var init_error_message = __esm({
10521
10524
  });
10522
10525
 
10523
10526
  // ../../scripts/virtual-office/code-runner/watcher-coordination.mjs
10524
- import { createHash as createHash5 } from "node:crypto";
10527
+ import { createHash as createHash6 } from "node:crypto";
10525
10528
  function ciFixOccurrenceKey({ repo, prNumber, headSha, repairAttempt }) {
10526
10529
  const occurrence = JSON.stringify([
10527
10530
  String(repo).toLowerCase(),
@@ -10529,7 +10532,7 @@ function ciFixOccurrenceKey({ repo, prNumber, headSha, repairAttempt }) {
10529
10532
  String(headSha).toLowerCase(),
10530
10533
  Number(repairAttempt)
10531
10534
  ]);
10532
- return `ci-fix:v1:${createHash5("sha256").update(occurrence).digest("hex")}`;
10535
+ return `ci-fix:v1:${createHash6("sha256").update(occurrence).digest("hex")}`;
10533
10536
  }
10534
10537
  function coordinationRetryDue(entry, nowMs) {
10535
10538
  return !entry.nextRetryAt || nowMs >= entry.nextRetryAt;
@@ -10659,7 +10662,7 @@ var init_watcher_coordination = __esm({
10659
10662
  });
10660
10663
 
10661
10664
  // ../../scripts/virtual-office/code-runner/watcher-state.mjs
10662
- import { randomUUID as randomUUID3 } from "node:crypto";
10665
+ import { randomUUID as randomUUID4 } from "node:crypto";
10663
10666
  import { mkdir as mkdir3, open as open2, readFile as readFile4, rename, unlink as unlink2 } from "node:fs/promises";
10664
10667
  import { dirname as dirname10 } from "node:path";
10665
10668
  async function readWatcherState(stateFile) {
@@ -10679,7 +10682,7 @@ async function readWatcherState(stateFile) {
10679
10682
  async function writeWatcherState(stateFile, state) {
10680
10683
  const directory = dirname10(stateFile);
10681
10684
  await mkdir3(directory, { recursive: true });
10682
- const temp = `${stateFile}.${process.pid}.${randomUUID3()}.tmp`;
10685
+ const temp = `${stateFile}.${process.pid}.${randomUUID4()}.tmp`;
10683
10686
  let handle;
10684
10687
  try {
10685
10688
  handle = await open2(temp, "wx");
@@ -11157,7 +11160,7 @@ var init_pr_watcher_github = __esm({
11157
11160
  });
11158
11161
 
11159
11162
  // ../../scripts/virtual-office/code-runner/enqueue-autonomous-code-task.mjs
11160
- import { randomUUID as randomUUID4 } from "node:crypto";
11163
+ import { randomUUID as randomUUID5 } from "node:crypto";
11161
11164
  function isDefiniteRefusal(err) {
11162
11165
  const status = Number(err?.status);
11163
11166
  if (Number.isFinite(status) && status >= 400 && status < 500) return true;
@@ -11176,7 +11179,7 @@ async function enqueueAutonomousCodeTask(client, task, log2 = () => {
11176
11179
  if (typeof client?.reserveAutonomousDispatchBudget !== "function" || typeof client?.releaseAutonomousDispatchBudget !== "function") {
11177
11180
  throw new Error("autonomous dispatch admission client unavailable");
11178
11181
  }
11179
- const reservationId = randomUUID4();
11182
+ const reservationId = randomUUID5();
11180
11183
  const admission = await client.reserveAutonomousDispatchBudget({
11181
11184
  requestedBudgetUsd,
11182
11185
  reservationId,
@@ -12010,7 +12013,7 @@ var init_effort_mode_config = __esm({
12010
12013
  });
12011
12014
 
12012
12015
  // ../../scripts/virtual-office/model-registry.mjs
12013
- import { randomUUID as randomUUID5 } from "node:crypto";
12016
+ import { randomUUID as randomUUID6 } from "node:crypto";
12014
12017
  import fs11 from "node:fs";
12015
12018
  import os4 from "node:os";
12016
12019
  import path18 from "node:path";
@@ -12021,7 +12024,7 @@ function userCacheRoot() {
12021
12024
  if (home) return path18.join(home, ".claude");
12022
12025
  } catch {
12023
12026
  }
12024
- return path18.join(os4.tmpdir(), `vo-model-registry-${randomUUID5()}`);
12027
+ return path18.join(os4.tmpdir(), `vo-model-registry-${randomUUID6()}`);
12025
12028
  }
12026
12029
  function resolveCacheBaseDir(env2 = process.env, moduleDir = __dirname) {
12027
12030
  if (env2.VO_MODEL_REGISTRY_CACHE_DIR) return env2.VO_MODEL_REGISTRY_CACHE_DIR;
@@ -15313,7 +15316,7 @@ var init_detached_economics_spool = __esm({
15313
15316
  });
15314
15317
 
15315
15318
  // ../../scripts/virtual-office/code-runner/killed-run-outcome.mjs
15316
- import { randomUUID as randomUUID6 } from "node:crypto";
15319
+ import { randomUUID as randomUUID7 } from "node:crypto";
15317
15320
  async function handleKilledRun({
15318
15321
  client,
15319
15322
  id,
@@ -15350,7 +15353,7 @@ async function handleKilledRun({
15350
15353
  };
15351
15354
  }
15352
15355
  if (reason === "claim_authority_changed") {
15353
- const occurrenceId = randomUUID6();
15356
+ const occurrenceId = randomUUID7();
15354
15357
  const economics = {
15355
15358
  occurrence_id: occurrenceId,
15356
15359
  runner_id: runnerId,
@@ -15650,7 +15653,7 @@ var code_runner_daemon_exports = {};
15650
15653
  __export(code_runner_daemon_exports, {
15651
15654
  main: () => main
15652
15655
  });
15653
- import { randomUUID as randomUUID7 } from "node:crypto";
15656
+ import { randomUUID as randomUUID8 } from "node:crypto";
15654
15657
  import { fileURLToPath as fileURLToPath8 } from "node:url";
15655
15658
  function log(msg) {
15656
15659
  console.log(`[code-runner ${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}`);
@@ -15883,7 +15886,7 @@ Closes #${publicationTarget.supersedesPrNumber}` : "";
15883
15886
  async function main({ env: env2 = process.env, once: once2 = false } = {}) {
15884
15887
  const cfg = loadCodeRunnerConfig(env2, { log });
15885
15888
  await sweepStaleTaskAttachmentDirectories().catch((error) => log(`stale attachment cleanup failed: ${error.message}`));
15886
- const runnerInstanceId = randomUUID7();
15889
+ const runnerInstanceId = randomUUID8();
15887
15890
  const client = createControlPlaneClient({
15888
15891
  env: env2,
15889
15892
  runnerId: cfg.runnerId,
@@ -16095,8 +16098,163 @@ var init_code_runner_daemon = __esm({
16095
16098
  import { createRequire as createRequire4 } from "node:module";
16096
16099
 
16097
16100
  // src/runner-readiness.mjs
16098
- function failed({ paired = false, operatorId = null, tenantId = null, githubReady = null, error, message }) {
16099
- return { ok: false, paired, operatorId, tenantId, githubReady, error, message };
16101
+ import { createHash, randomUUID } from "node:crypto";
16102
+ var RUNNER_GITHUB_READINESS_TIMEOUT_MS = 5e4;
16103
+ var RUNNER_IDENTITY_READINESS_TIMEOUT_MS = 1e4;
16104
+ var RUNNER_READINESS_MAX_REPOSITORIES = 4;
16105
+ var RUNNER_READINESS_MAX_TIMER_DELAY_MS = 2147e6;
16106
+ var RUNNER_READINESS_TRANSIENT_MAX_RETRY_AFTER_MS = 3e5;
16107
+ var RUNNER_READINESS_IPC_ACK_TIMEOUT_MS = 5e3;
16108
+ var RUNNER_READINESS_DEFERRAL_MESSAGE_TYPE = "vo-runner-readiness-deferred-v1";
16109
+ var RUNNER_READINESS_DEFERRAL_ACK_TYPE = "vo-runner-readiness-deferred-ack-v1";
16110
+ var RUNNER_REPOSITORY = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u;
16111
+ function runnerRepositoryScopeFromEnv(env2 = {}) {
16112
+ return String(env2.VO_CODE_RUNNER_REPOS || "").split(/[\s,]+/u).map((repo) => repo.trim()).filter(Boolean);
16113
+ }
16114
+ function normalizeRunnerRepositoryScope(repositories) {
16115
+ if (!Array.isArray(repositories) || repositories.length < 1) {
16116
+ throw new Error("VO_CODE_RUNNER_REPOS must name at least one owner/name repository");
16117
+ }
16118
+ if (repositories.length > RUNNER_READINESS_MAX_REPOSITORIES) {
16119
+ throw new Error(`VO_CODE_RUNNER_REPOS supports at most ${RUNNER_READINESS_MAX_REPOSITORIES} repositories`);
16120
+ }
16121
+ const normalized = repositories.map((repo) => {
16122
+ const [owner, name] = typeof repo === "string" ? repo.split("/") : [];
16123
+ if (typeof repo !== "string" || repo.length > 140 || !RUNNER_REPOSITORY.test(repo) || owner === "." || owner === ".." || name === "." || name === "..") {
16124
+ throw new Error("VO_CODE_RUNNER_REPOS entries must be canonical owner/name repositories");
16125
+ }
16126
+ return repo.toLowerCase();
16127
+ });
16128
+ if (new Set(normalized).size !== normalized.length) {
16129
+ throw new Error("VO_CODE_RUNNER_REPOS entries must be unique");
16130
+ }
16131
+ const owners = new Set(normalized.map((repo) => repo.split("/")[0]));
16132
+ if (owners.size !== 1) {
16133
+ throw new Error("VO_CODE_RUNNER_REPOS entries must share one owner");
16134
+ }
16135
+ return Object.freeze(normalized.sort());
16136
+ }
16137
+ function runnerRepositoryScopeDigest(repositories) {
16138
+ const scope = normalizeRunnerRepositoryScope(repositories);
16139
+ return createHash("sha256").update(JSON.stringify({
16140
+ version: 1,
16141
+ repositories: scope
16142
+ }), "utf8").digest("hex");
16143
+ }
16144
+ function runnerReadinessRetryDelayMs(readiness) {
16145
+ if (readiness?.paired !== true || readiness?.githubReady !== false) return null;
16146
+ if (Number.isFinite(readiness.retryAfterMs) && readiness.retryAfterMs > 0) {
16147
+ return Math.ceil(readiness.retryAfterMs);
16148
+ }
16149
+ return null;
16150
+ }
16151
+ function runnerReadinessFailureDisposition(readiness, delivery = null) {
16152
+ const retryAfterMs = runnerReadinessRetryDelayMs(readiness);
16153
+ if (retryAfterMs === null) {
16154
+ return Object.freeze({ action: "exit", exitCode: 1, retryAfterMs: null });
16155
+ }
16156
+ if (delivery?.attempted === true) {
16157
+ return Object.freeze({
16158
+ action: "exit",
16159
+ exitCode: delivery.delivered === true ? 75 : 1,
16160
+ retryAfterMs
16161
+ });
16162
+ }
16163
+ return Object.freeze({ action: "wait", exitCode: null, retryAfterMs });
16164
+ }
16165
+ async function notifySupervisorReadinessDeferral({
16166
+ processLike,
16167
+ retryAfterMs,
16168
+ error = null,
16169
+ ackTimeoutMs = RUNNER_READINESS_IPC_ACK_TIMEOUT_MS
16170
+ }) {
16171
+ if (processLike?.connected !== true || typeof processLike.send !== "function") {
16172
+ return Object.freeze({ attempted: false, delivered: false });
16173
+ }
16174
+ if (typeof processLike.on !== "function" || typeof processLike.off !== "function") {
16175
+ return Object.freeze({ attempted: true, delivered: false });
16176
+ }
16177
+ const nonce = randomUUID();
16178
+ const delivered = await new Promise((resolve3) => {
16179
+ let settled = false;
16180
+ const onMessage = (message) => {
16181
+ if (message?.type === RUNNER_READINESS_DEFERRAL_ACK_TYPE && message.nonce === nonce) {
16182
+ finish(true);
16183
+ }
16184
+ };
16185
+ const onDisconnect = () => finish(false);
16186
+ const finish = (value) => {
16187
+ if (settled) return;
16188
+ settled = true;
16189
+ clearTimeout(timer);
16190
+ processLike.off("message", onMessage);
16191
+ processLike.off("disconnect", onDisconnect);
16192
+ resolve3(value);
16193
+ };
16194
+ const timer = setTimeout(() => finish(false), ackTimeoutMs);
16195
+ processLike.on("message", onMessage);
16196
+ processLike.on("disconnect", onDisconnect);
16197
+ try {
16198
+ processLike.send({
16199
+ type: RUNNER_READINESS_DEFERRAL_MESSAGE_TYPE,
16200
+ nonce,
16201
+ retryAfterMs,
16202
+ error
16203
+ }, (sendError) => {
16204
+ if (sendError) finish(false);
16205
+ });
16206
+ } catch {
16207
+ finish(false);
16208
+ }
16209
+ });
16210
+ return Object.freeze({ attempted: true, delivered });
16211
+ }
16212
+ async function waitForRunnerReadinessRetry(delayMs, {
16213
+ nowMs = () => Date.now(),
16214
+ wait: wait3 = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms))
16215
+ } = {}) {
16216
+ if (!Number.isFinite(delayMs) || delayMs <= 0) {
16217
+ throw new TypeError("runner readiness retry delay is invalid");
16218
+ }
16219
+ const deadline = nowMs() + Math.ceil(delayMs);
16220
+ while (nowMs() < deadline) {
16221
+ await wait3(Math.min(RUNNER_READINESS_MAX_TIMER_DELAY_MS, deadline - nowMs()));
16222
+ }
16223
+ }
16224
+ function failed({
16225
+ paired = false,
16226
+ operatorId = null,
16227
+ tenantId = null,
16228
+ githubReady = null,
16229
+ retryAfterMs = null,
16230
+ error,
16231
+ message
16232
+ }) {
16233
+ return {
16234
+ ok: false,
16235
+ paired,
16236
+ operatorId,
16237
+ tenantId,
16238
+ githubReady,
16239
+ ...Number.isFinite(retryAfterMs) && retryAfterMs > 0 ? { retryAfterMs } : {},
16240
+ error,
16241
+ message
16242
+ };
16243
+ }
16244
+ function responseRetryAfterMs(response, body) {
16245
+ const transientReadinessFailure = response.status === 503 && body?.error === "github_installation_readiness_retryable";
16246
+ if (response.status !== 429 && !transientReadinessFailure) return null;
16247
+ const bound = (retryAfterMs) => transientReadinessFailure ? Math.min(RUNNER_READINESS_TRANSIENT_MAX_RETRY_AFTER_MS, retryAfterMs) : retryAfterMs;
16248
+ const value = response.headers?.get?.("retry-after")?.trim() ?? "";
16249
+ const seconds = Number(value);
16250
+ if (value && Number.isFinite(seconds) && seconds >= 0) {
16251
+ return bound(Math.max(1e3, Math.ceil(seconds * 1e3)));
16252
+ }
16253
+ const at = value ? Date.parse(value) : Number.NaN;
16254
+ if (Number.isFinite(at)) {
16255
+ return bound(Math.max(1e3, Math.ceil(at - Date.now())));
16256
+ }
16257
+ return bound(6e4);
16100
16258
  }
16101
16259
  async function responseBody(response) {
16102
16260
  try {
@@ -16109,11 +16267,21 @@ async function responseBody(response) {
16109
16267
  function serverMessage(body, fallback) {
16110
16268
  return typeof body.message === "string" && body.message.trim() ? body.message.trim() : fallback;
16111
16269
  }
16112
- async function fetchWithTimeout(fetchImpl, url, init, timeoutMs) {
16270
+ async function fetchJsonWithTimeout(fetchImpl, url, init, timeoutMs) {
16113
16271
  const controller = new AbortController();
16114
- const timer = setTimeout(() => controller.abort(), timeoutMs);
16272
+ let timer;
16273
+ const timeout = new Promise((_, reject) => {
16274
+ timer = setTimeout(() => {
16275
+ controller.abort();
16276
+ reject(new Error(`request aborted after ${timeoutMs}ms`));
16277
+ }, timeoutMs);
16278
+ });
16115
16279
  try {
16116
- return await fetchImpl(url, { ...init, signal: controller.signal });
16280
+ const requestAndBody = (async () => {
16281
+ const response = await fetchImpl(url, { ...init, signal: controller.signal });
16282
+ return { response, body: await responseBody(response) };
16283
+ })();
16284
+ return await Promise.race([requestAndBody, timeout]);
16117
16285
  } finally {
16118
16286
  clearTimeout(timer);
16119
16287
  }
@@ -16123,18 +16291,24 @@ async function probeRunnerReadiness({
16123
16291
  token: token2,
16124
16292
  fetchImpl = fetch,
16125
16293
  requireGithub = false,
16126
- timeoutMs = 1e4
16294
+ repositories = [],
16295
+ timeoutMs = RUNNER_IDENTITY_READINESS_TIMEOUT_MS,
16296
+ // The server performs at most five sequential App-JWT GETs (installation plus
16297
+ // every one of at most four configured repositories), each bounded at 8s.
16298
+ // Keep a transport margin while preventing a stuck proof from hanging startup.
16299
+ githubTimeoutMs = RUNNER_GITHUB_READINESS_TIMEOUT_MS
16127
16300
  }) {
16128
16301
  const base = controlPlaneUrl2.replace(/\/+$/u, "");
16129
16302
  const headers = { authorization: `Bearer ${token2}` };
16130
16303
  let identityResponse;
16304
+ let identity;
16131
16305
  try {
16132
- identityResponse = await fetchWithTimeout(
16306
+ ({ response: identityResponse, body: identity } = await fetchJsonWithTimeout(
16133
16307
  fetchImpl,
16134
16308
  `${base}/api/v1/auth/me`,
16135
16309
  { headers },
16136
16310
  timeoutMs
16137
- );
16311
+ ));
16138
16312
  } catch (error) {
16139
16313
  const detail = error instanceof Error ? error.message : String(error);
16140
16314
  return failed({
@@ -16142,7 +16316,6 @@ async function probeRunnerReadiness({
16142
16316
  message: `AlgoHQ could not be reached: ${detail}`
16143
16317
  });
16144
16318
  }
16145
- const identity = await responseBody(identityResponse);
16146
16319
  if (!identityResponse.ok) {
16147
16320
  return failed({
16148
16321
  error: "credential_rejected",
@@ -16168,18 +16341,33 @@ async function probeRunnerReadiness({
16168
16341
  message: "Paired to AlgoHQ."
16169
16342
  };
16170
16343
  }
16344
+ let repositoryScope = null;
16345
+ try {
16346
+ if (!Array.isArray(repositories)) {
16347
+ throw new Error("VO_CODE_RUNNER_REPOS must be a repository array");
16348
+ }
16349
+ if (repositories.length > 0) repositoryScope = normalizeRunnerRepositoryScope(repositories);
16350
+ } catch (error) {
16351
+ return failed({
16352
+ paired: true,
16353
+ operatorId,
16354
+ tenantId,
16355
+ githubReady: false,
16356
+ error: "github_repository_scope_invalid",
16357
+ message: error instanceof Error ? error.message : String(error)
16358
+ });
16359
+ }
16171
16360
  let githubResponse;
16361
+ let github;
16172
16362
  try {
16173
- githubResponse = await fetchWithTimeout(
16363
+ const readinessUrl = new URL(`${base}/api/v1/github/installation-readiness`);
16364
+ for (const repo of repositoryScope ?? []) readinessUrl.searchParams.append("repo", repo);
16365
+ ({ response: githubResponse, body: github } = await fetchJsonWithTimeout(
16174
16366
  fetchImpl,
16175
- `${base}/api/v1/github/installation-token`,
16176
- {
16177
- method: "POST",
16178
- headers: { ...headers, "content-type": "application/json" },
16179
- body: "{}"
16180
- },
16181
- timeoutMs
16182
- );
16367
+ readinessUrl.toString(),
16368
+ { headers },
16369
+ githubTimeoutMs
16370
+ ));
16183
16371
  } catch (error) {
16184
16372
  const detail = error instanceof Error ? error.message : String(error);
16185
16373
  return failed({
@@ -16191,14 +16379,25 @@ async function probeRunnerReadiness({
16191
16379
  message: `GitHub publication readiness could not be checked: ${detail}`
16192
16380
  });
16193
16381
  }
16194
- const github = await responseBody(githubResponse);
16195
- if (!githubResponse.ok || typeof github.token !== "string" || !github.token) {
16382
+ const repositorySelection = github.repository_selection;
16383
+ const repositoriesVerified = github.repositories_verified;
16384
+ const returnedScope = github.repository_scope;
16385
+ let normalizedReturnedScope = null;
16386
+ try {
16387
+ normalizedReturnedScope = normalizeRunnerRepositoryScope(returnedScope);
16388
+ } catch {
16389
+ }
16390
+ const effectiveScope = repositoryScope ?? normalizedReturnedScope;
16391
+ const sourceVerified = repositoryScope === null ? github.repository_scope_source === "persisted_installation_singleton" && normalizedReturnedScope?.length === 1 : github.repository_scope_source === "runner_config";
16392
+ const repositoryScopeVerified = effectiveScope !== null && normalizedReturnedScope !== null && Array.isArray(returnedScope) && returnedScope.length === normalizedReturnedScope.length && returnedScope.every((repo, index) => repo === normalizedReturnedScope[index]) && normalizedReturnedScope.length === effectiveScope.length && normalizedReturnedScope.every((repo, index) => repo === effectiveScope[index]) && github.repository_scope_sha256 === runnerRepositoryScopeDigest(effectiveScope) && repositoriesVerified === effectiveScope.length && sourceVerified && (repositorySelection === "all" || repositorySelection === "selected");
16393
+ if (!githubResponse.ok || github.configured !== true || github.verified !== true || github.publication_ready !== true || !repositoryScopeVerified) {
16196
16394
  const error = typeof github.error === "string" && github.error ? github.error : "github_not_ready";
16197
16395
  return failed({
16198
16396
  paired: true,
16199
16397
  operatorId,
16200
16398
  tenantId,
16201
16399
  githubReady: false,
16400
+ retryAfterMs: responseRetryAfterMs(githubResponse, github),
16202
16401
  error,
16203
16402
  message: serverMessage(github, `GitHub publication preflight failed (HTTP ${githubResponse.status}).`)
16204
16403
  });
@@ -16209,8 +16408,10 @@ async function probeRunnerReadiness({
16209
16408
  operatorId,
16210
16409
  tenantId,
16211
16410
  githubReady: true,
16411
+ repositoryScope: effectiveScope,
16412
+ repositoryScopeSource: github.repository_scope_source,
16212
16413
  error: null,
16213
- message: "Paired and ready to publish through the Algosuite GitHub App."
16414
+ message: `Paired with a verified Algosuite GitHub App installation (${repositoriesVerified} repos).`
16214
16415
  };
16215
16416
  }
16216
16417
  function pairedOperatorScope(readiness) {
@@ -16221,7 +16422,7 @@ function pairedOperatorScope(readiness) {
16221
16422
  import { closeSync, existsSync, mkdirSync, openSync, unlinkSync } from "node:fs";
16222
16423
  import { homedir } from "node:os";
16223
16424
  import { posix, win32 } from "node:path";
16224
- import { randomUUID } from "node:crypto";
16425
+ import { randomUUID as randomUUID2 } from "node:crypto";
16225
16426
  var APP_IDENTIFIER = "ai.algosuite.vo-runner";
16226
16427
  var CLONES_DIR = "clones";
16227
16428
  function pathsFor(platform4) {
@@ -16291,7 +16492,7 @@ function runnerWorkingDirectory({ repoRoot: repoRoot2, clonesRoot: clonesRoot2 }
16291
16492
  }
16292
16493
  function assertWritableRunnerDirectory(root) {
16293
16494
  mkdirSync(root, { recursive: true });
16294
- const probe = pathsFor(process.platform).join(root, `.vo-runner-write-probe-${process.pid}-${randomUUID()}`);
16495
+ const probe = pathsFor(process.platform).join(root, `.vo-runner-write-probe-${process.pid}-${randomUUID2()}`);
16295
16496
  let handle;
16296
16497
  try {
16297
16498
  handle = openSync(probe, "wx", 384);
@@ -16411,21 +16612,51 @@ if (!token) {
16411
16612
  }
16412
16613
  var controlPlaneUrl = process.env.VO_CONTROL_PLANE_URL || DEFAULT_CONTROL_PLANE_URL3;
16413
16614
  var pairedOperatorId = null;
16414
- if (!explicitAdminToken) {
16415
- const readiness = await probeRunnerReadiness({
16416
- controlPlaneUrl,
16417
- token,
16418
- requireGithub: true
16615
+ async function deferRunnerReadinessRetry(readiness) {
16616
+ const initial = runnerReadinessFailureDisposition(readiness);
16617
+ if (initial.action === "exit") return initial;
16618
+ const { retryAfterMs } = initial;
16619
+ console.error(
16620
+ `[vo-mcp runner] GitHub readiness is temporarily unavailable; retrying after ${retryAfterMs}ms: ${readiness.message}`
16621
+ );
16622
+ const delivery = await notifySupervisorReadinessDeferral({
16623
+ processLike: process,
16624
+ retryAfterMs,
16625
+ error: readiness.error
16419
16626
  });
16420
- if (!readiness.ok) {
16421
- console.error(`[vo-mcp runner] Readiness check failed: ${readiness.message}`);
16422
- process.exit(1);
16627
+ if (delivery.attempted) {
16628
+ const disposition = runnerReadinessFailureDisposition(readiness, delivery);
16629
+ if (disposition.exitCode === 1) {
16630
+ console.error("[vo-mcp runner] Supervisor readiness deferral could not be delivered; failing closed.");
16631
+ }
16632
+ return disposition;
16633
+ }
16634
+ await waitForRunnerReadinessRetry(retryAfterMs);
16635
+ return Object.freeze({ action: "retry", exitCode: null, retryAfterMs });
16636
+ }
16637
+ if (!explicitAdminToken) {
16638
+ let readiness;
16639
+ while (true) {
16640
+ readiness = await probeRunnerReadiness({
16641
+ controlPlaneUrl,
16642
+ token,
16643
+ requireGithub: true,
16644
+ repositories: runnerRepositoryScopeFromEnv(process.env)
16645
+ });
16646
+ if (readiness.ok) break;
16647
+ const disposition = await deferRunnerReadinessRetry(readiness);
16648
+ if (disposition.action === "retry") continue;
16649
+ if (disposition.retryAfterMs === null) {
16650
+ console.error(`[vo-mcp runner] Readiness check failed: ${readiness.message}`);
16651
+ }
16652
+ process.exit(disposition.exitCode);
16423
16653
  }
16424
16654
  pairedOperatorId = pairedOperatorScope(readiness);
16425
16655
  if (!pairedOperatorId) {
16426
16656
  console.error("[vo-mcp runner] Readiness check failed: paired operator scope is missing. Pair this computer again.");
16427
16657
  process.exit(1);
16428
16658
  }
16659
+ process.env.VO_CODE_RUNNER_REPOS = readiness.repositoryScope.join(",");
16429
16660
  }
16430
16661
  var rootConfig;
16431
16662
  try {