@algosuite/vo-mcp 0.2.0-beta.56 → 0.2.0-beta.58
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.
- package/dist/autostart-cli.js +6 -3
- package/dist/autostart-cli.js.map +2 -2
- package/dist/install-cli.js +6 -3
- package/dist/install-cli.js.map +2 -2
- package/dist/runner-cli.js +344 -63
- package/dist/runner-cli.js.map +4 -4
- package/dist/runner-supervisor.js +970 -88
- package/dist/runner-supervisor.js.map +4 -4
- package/package.json +1 -1
package/dist/runner-cli.js
CHANGED
|
@@ -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
|
|
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
|
-
'
|
|
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 "${
|
|
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
|
|
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
|
|
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 =
|
|
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 = {}) {
|
|
@@ -7646,8 +7649,17 @@ function messageExcerpt(output) {
|
|
|
7646
7649
|
const s = String(output ?? "");
|
|
7647
7650
|
return s.length > 1200 ? `...${s.slice(-1200)}` : s;
|
|
7648
7651
|
}
|
|
7649
|
-
async function enforceCompletionGateOrFail({
|
|
7650
|
-
|
|
7652
|
+
async function enforceCompletionGateOrFail({
|
|
7653
|
+
client,
|
|
7654
|
+
id,
|
|
7655
|
+
task,
|
|
7656
|
+
worktreeDir,
|
|
7657
|
+
run = null,
|
|
7658
|
+
truncated = false,
|
|
7659
|
+
log: log2 = () => {
|
|
7660
|
+
},
|
|
7661
|
+
execFileImpl = execFile
|
|
7662
|
+
} = {}) {
|
|
7651
7663
|
const resolved = resolveCompletionGate(task);
|
|
7652
7664
|
if (resolved === null) return false;
|
|
7653
7665
|
if (resolved.invalid) {
|
|
@@ -7671,6 +7683,12 @@ ${messageExcerpt(cached2.output)}`, "completion_gate_failed");
|
|
|
7671
7683
|
return false;
|
|
7672
7684
|
}
|
|
7673
7685
|
const kind = outcome.timedOut ? `timed out after ${COMPLETION_GATE_TIMEOUT_MS}ms` : `exited ${outcome.exitCode}`;
|
|
7686
|
+
if (truncated) {
|
|
7687
|
+
log2(`task ${id}: completion gate FAILED (${kind}) after a budget/turn-truncated run \u2014 salvaging as PARTIAL draft, not terminal-failing`);
|
|
7688
|
+
if (run) run.gateFailureNote = `completion gate '${task.completion_gate}' ${kind} (gate ran against a budget/turn-truncated run):
|
|
7689
|
+
${messageExcerpt(outcome.output)}`;
|
|
7690
|
+
return false;
|
|
7691
|
+
}
|
|
7674
7692
|
log2(`task ${id}: completion gate FAILED (${kind}) \u2014 not publishing`);
|
|
7675
7693
|
await postFailed2(client, id, `completion gate '${task.completion_gate}' ${kind} \u2014 task may not claim completion:
|
|
7676
7694
|
${messageExcerpt(outcome.output)}`, "completion_gate_failed");
|
|
@@ -8666,7 +8684,7 @@ var init_task_prompt = __esm({
|
|
|
8666
8684
|
});
|
|
8667
8685
|
|
|
8668
8686
|
// ../../scripts/virtual-office/code-runner/task-attachments.mjs
|
|
8669
|
-
import { createHash as
|
|
8687
|
+
import { createHash as createHash4, randomUUID as randomUUID3 } from "node:crypto";
|
|
8670
8688
|
import { chmod, mkdir, mkdtemp, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
8671
8689
|
import os2 from "node:os";
|
|
8672
8690
|
import path16 from "node:path";
|
|
@@ -8690,7 +8708,7 @@ async function createAttachmentDirectory(taskId, tempRoot) {
|
|
|
8690
8708
|
const root = path16.resolve(tempRoot);
|
|
8691
8709
|
await mkdir(root, { recursive: true });
|
|
8692
8710
|
const directory = await mkdtemp(path16.join(root, `${DIRECTORY_PREFIX}${safeTaskToken(taskId)}-`));
|
|
8693
|
-
const marker = JSON.stringify({ owner: MARKER_OWNER, token:
|
|
8711
|
+
const marker = JSON.stringify({ owner: MARKER_OWNER, token: randomUUID3(), directory: path16.basename(directory), created_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
8694
8712
|
await writeFile(path16.join(directory, MARKER_FILE), marker, { encoding: "utf8", mode: 384 });
|
|
8695
8713
|
return { directory, marker, tempRoot: root };
|
|
8696
8714
|
}
|
|
@@ -8766,7 +8784,7 @@ async function materializeTaskAttachments(client, task, { tempRoot = os2.tmpdir(
|
|
|
8766
8784
|
const content = await client.downloadTaskAttachment(task.code_task_id, ref.attachment_id);
|
|
8767
8785
|
if (!Buffer.isBuffer(content)) throw new Error(`attachment ${ref.attachment_id} did not return binary content`);
|
|
8768
8786
|
if (content.byteLength !== ref.size_bytes) throw new Error(`attachment ${ref.attachment_id} size mismatch`);
|
|
8769
|
-
const sha256 =
|
|
8787
|
+
const sha256 = createHash4("sha256").update(content).digest("hex");
|
|
8770
8788
|
if (sha256 !== ref.sha256) throw new Error(`attachment ${ref.attachment_id} sha256 mismatch`);
|
|
8771
8789
|
const name = sanitizeTaskAttachmentName(ref.name, index);
|
|
8772
8790
|
const filePath = path16.join(state.directory, name);
|
|
@@ -8797,9 +8815,9 @@ var init_task_attachments = __esm({
|
|
|
8797
8815
|
import { homedir as homedir7 } from "node:os";
|
|
8798
8816
|
import { join as join11 } from "node:path";
|
|
8799
8817
|
import { readdir as readdir2, readFile as readFile2, unlink, writeFile as writeFile2 } from "node:fs/promises";
|
|
8800
|
-
import { createHash as
|
|
8818
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
8801
8819
|
function deriveUuid(seed) {
|
|
8802
|
-
const h =
|
|
8820
|
+
const h = createHash5("sha256").update(seed).digest("hex");
|
|
8803
8821
|
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
8822
|
}
|
|
8805
8823
|
function spoolToCloud(record, ids) {
|
|
@@ -10521,7 +10539,7 @@ var init_error_message = __esm({
|
|
|
10521
10539
|
});
|
|
10522
10540
|
|
|
10523
10541
|
// ../../scripts/virtual-office/code-runner/watcher-coordination.mjs
|
|
10524
|
-
import { createHash as
|
|
10542
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
10525
10543
|
function ciFixOccurrenceKey({ repo, prNumber, headSha, repairAttempt }) {
|
|
10526
10544
|
const occurrence = JSON.stringify([
|
|
10527
10545
|
String(repo).toLowerCase(),
|
|
@@ -10529,7 +10547,7 @@ function ciFixOccurrenceKey({ repo, prNumber, headSha, repairAttempt }) {
|
|
|
10529
10547
|
String(headSha).toLowerCase(),
|
|
10530
10548
|
Number(repairAttempt)
|
|
10531
10549
|
]);
|
|
10532
|
-
return `ci-fix:v1:${
|
|
10550
|
+
return `ci-fix:v1:${createHash6("sha256").update(occurrence).digest("hex")}`;
|
|
10533
10551
|
}
|
|
10534
10552
|
function coordinationRetryDue(entry, nowMs) {
|
|
10535
10553
|
return !entry.nextRetryAt || nowMs >= entry.nextRetryAt;
|
|
@@ -10659,7 +10677,7 @@ var init_watcher_coordination = __esm({
|
|
|
10659
10677
|
});
|
|
10660
10678
|
|
|
10661
10679
|
// ../../scripts/virtual-office/code-runner/watcher-state.mjs
|
|
10662
|
-
import { randomUUID as
|
|
10680
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
10663
10681
|
import { mkdir as mkdir3, open as open2, readFile as readFile4, rename, unlink as unlink2 } from "node:fs/promises";
|
|
10664
10682
|
import { dirname as dirname10 } from "node:path";
|
|
10665
10683
|
async function readWatcherState(stateFile) {
|
|
@@ -10679,7 +10697,7 @@ async function readWatcherState(stateFile) {
|
|
|
10679
10697
|
async function writeWatcherState(stateFile, state) {
|
|
10680
10698
|
const directory = dirname10(stateFile);
|
|
10681
10699
|
await mkdir3(directory, { recursive: true });
|
|
10682
|
-
const temp = `${stateFile}.${process.pid}.${
|
|
10700
|
+
const temp = `${stateFile}.${process.pid}.${randomUUID4()}.tmp`;
|
|
10683
10701
|
let handle;
|
|
10684
10702
|
try {
|
|
10685
10703
|
handle = await open2(temp, "wx");
|
|
@@ -11139,8 +11157,8 @@ var init_pr_watcher_github = __esm({
|
|
|
11139
11157
|
"../../scripts/virtual-office/code-runner/pr-watcher-github.mjs"() {
|
|
11140
11158
|
"use strict";
|
|
11141
11159
|
init_process_runner2();
|
|
11142
|
-
VIEW_FIELDS_WITH_CI = "state,statusCheckRollup,headRefName,headRefOid,url,isDraft,mergeStateStatus";
|
|
11143
|
-
VIEW_FIELDS_WITHOUT_CI = "state,headRefName,headRefOid,url,isDraft,mergeStateStatus";
|
|
11160
|
+
VIEW_FIELDS_WITH_CI = "state,statusCheckRollup,headRefName,headRefOid,url,isDraft,mergeStateStatus,body";
|
|
11161
|
+
VIEW_FIELDS_WITHOUT_CI = "state,headRefName,headRefOid,url,isDraft,mergeStateStatus,body";
|
|
11144
11162
|
CI_UNREADABLE_REASON = "app_token_missing_checks_read";
|
|
11145
11163
|
DIAGNOSTIC_INTERVAL_MS = 30 * 60 * 1e3;
|
|
11146
11164
|
lastDiagnosticAt = 0;
|
|
@@ -11157,7 +11175,7 @@ var init_pr_watcher_github = __esm({
|
|
|
11157
11175
|
});
|
|
11158
11176
|
|
|
11159
11177
|
// ../../scripts/virtual-office/code-runner/enqueue-autonomous-code-task.mjs
|
|
11160
|
-
import { randomUUID as
|
|
11178
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
11161
11179
|
function isDefiniteRefusal(err) {
|
|
11162
11180
|
const status = Number(err?.status);
|
|
11163
11181
|
if (Number.isFinite(status) && status >= 400 && status < 500) return true;
|
|
@@ -11176,7 +11194,7 @@ async function enqueueAutonomousCodeTask(client, task, log2 = () => {
|
|
|
11176
11194
|
if (typeof client?.reserveAutonomousDispatchBudget !== "function" || typeof client?.releaseAutonomousDispatchBudget !== "function") {
|
|
11177
11195
|
throw new Error("autonomous dispatch admission client unavailable");
|
|
11178
11196
|
}
|
|
11179
|
-
const reservationId =
|
|
11197
|
+
const reservationId = randomUUID5();
|
|
11180
11198
|
const admission = await client.reserveAutonomousDispatchBudget({
|
|
11181
11199
|
requestedBudgetUsd,
|
|
11182
11200
|
reservationId,
|
|
@@ -11882,7 +11900,7 @@ function startControlServer({ port, getStatus, requestStop, allowedOrigin, log:
|
|
|
11882
11900
|
server.unref?.();
|
|
11883
11901
|
return server;
|
|
11884
11902
|
}
|
|
11885
|
-
function startDaemonControl({ cfg, runnerInstanceId, requestStop, getActiveCount, isRunning, startedAt, log: log2 = () => {
|
|
11903
|
+
function startDaemonControl({ cfg, runnerInstanceId, requestStop, getActiveCount, getActiveTaskIds = () => [], isRunning, startedAt, log: log2 = () => {
|
|
11886
11904
|
}, onDuplicate = null, getUpdateStatus = () => null, getClaimGate = () => null }) {
|
|
11887
11905
|
if (!cfg.controlEnabled) return null;
|
|
11888
11906
|
return startControlServer({
|
|
@@ -11899,6 +11917,9 @@ function startDaemonControl({ cfg, runnerInstanceId, requestStop, getActiveCount
|
|
|
11899
11917
|
servedOperators: cfg.servedOperators,
|
|
11900
11918
|
watchEnabled: cfg.watchEnabled,
|
|
11901
11919
|
activeTasks: getActiveCount(),
|
|
11920
|
+
// The supervisor's update-drain gate reads THESE ids (not a guess) to mark
|
|
11921
|
+
// in-flight work before a capped staged-update restart kills it.
|
|
11922
|
+
activeTaskIds: getActiveTaskIds(),
|
|
11902
11923
|
startedAt: new Date(startedAt).toISOString(),
|
|
11903
11924
|
uptimeSec: Math.round((Date.now() - startedAt) / 1e3),
|
|
11904
11925
|
// Host version awareness — the app + `runner --status` read drift from here.
|
|
@@ -12010,7 +12031,7 @@ var init_effort_mode_config = __esm({
|
|
|
12010
12031
|
});
|
|
12011
12032
|
|
|
12012
12033
|
// ../../scripts/virtual-office/model-registry.mjs
|
|
12013
|
-
import { randomUUID as
|
|
12034
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
12014
12035
|
import fs11 from "node:fs";
|
|
12015
12036
|
import os4 from "node:os";
|
|
12016
12037
|
import path18 from "node:path";
|
|
@@ -12021,7 +12042,7 @@ function userCacheRoot() {
|
|
|
12021
12042
|
if (home) return path18.join(home, ".claude");
|
|
12022
12043
|
} catch {
|
|
12023
12044
|
}
|
|
12024
|
-
return path18.join(os4.tmpdir(), `vo-model-registry-${
|
|
12045
|
+
return path18.join(os4.tmpdir(), `vo-model-registry-${randomUUID6()}`);
|
|
12025
12046
|
}
|
|
12026
12047
|
function resolveCacheBaseDir(env2 = process.env, moduleDir = __dirname) {
|
|
12027
12048
|
if (env2.VO_MODEL_REGISTRY_CACHE_DIR) return env2.VO_MODEL_REGISTRY_CACHE_DIR;
|
|
@@ -13370,6 +13391,17 @@ var init_redact_tokens = __esm({
|
|
|
13370
13391
|
}
|
|
13371
13392
|
});
|
|
13372
13393
|
|
|
13394
|
+
// ../../scripts/ci/check-consensus-receipt-core.mjs
|
|
13395
|
+
var RECEIPT_MARKER_RE, UNAVAILABILITY_RE;
|
|
13396
|
+
var init_check_consensus_receipt_core = __esm({
|
|
13397
|
+
"../../scripts/ci/check-consensus-receipt-core.mjs"() {
|
|
13398
|
+
"use strict";
|
|
13399
|
+
init_methodology_composer();
|
|
13400
|
+
RECEIPT_MARKER_RE = /receipt id:\s*[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/iu;
|
|
13401
|
+
UNAVAILABILITY_RE = /consensus[^.\n]{0,80}(not available|unavailable|not permitted|could not be reached|no receipt_id)/iu;
|
|
13402
|
+
}
|
|
13403
|
+
});
|
|
13404
|
+
|
|
13373
13405
|
// ../../scripts/virtual-office/code-runner/task-helpers.mjs
|
|
13374
13406
|
function makeSafeProgress(log2) {
|
|
13375
13407
|
return async (client, id, patch) => {
|
|
@@ -13394,6 +13426,17 @@ ${String(run?.lastAgentMessage || "")}`;
|
|
|
13394
13426
|
const missing = found.filter((line) => !String(slicedBodyText || "").includes(line));
|
|
13395
13427
|
return missing.length ? ["", "### Consensus evidence (preserved past truncation)", "", ...missing.map((line) => redactSecrets(line))] : [];
|
|
13396
13428
|
}
|
|
13429
|
+
function consensusUnavailabilityLine(stakes) {
|
|
13430
|
+
const signal = String(stakes ?? "").replace(/\s+/gu, " ").trim().slice(0, 80) || "unspecified";
|
|
13431
|
+
return `Consensus judgment was not available in this headless session (no receipt_id) \u2014 automated declaration by the runner; stakes signal: ${signal}.`;
|
|
13432
|
+
}
|
|
13433
|
+
function withConsensusDeclaration(body, stakes) {
|
|
13434
|
+
const text = String(body ?? "");
|
|
13435
|
+
if (RECEIPT_MARKER_RE.test(text) || UNAVAILABILITY_RE.test(text)) return text;
|
|
13436
|
+
return `${text}
|
|
13437
|
+
|
|
13438
|
+
${consensusUnavailabilityLine(stakes)}`;
|
|
13439
|
+
}
|
|
13397
13440
|
function buildPrBody(task, run, files, { armAutoMerge = false } = {}) {
|
|
13398
13441
|
const governedStakes = matchGovernedStakes({ prompt: String(task.prompt || "") });
|
|
13399
13442
|
const slicedSections = [
|
|
@@ -13419,15 +13462,20 @@ function buildPrBody(task, run, files, { armAutoMerge = false } = {}) {
|
|
|
13419
13462
|
"",
|
|
13420
13463
|
// A budget/turn-capped run ends with no assistant text (summary = the bare
|
|
13421
13464
|
// subtype); its LAST message is the honest report the operator needs.
|
|
13422
|
-
...run.lastAgentMessage ? ["### Last agent message before the cap", "", redactSecrets(String(run.lastAgentMessage)).slice(0, 2e3), ""] : []
|
|
13465
|
+
...run.lastAgentMessage ? ["### Last agent message before the cap", "", redactSecrets(String(run.lastAgentMessage)).slice(0, 2e3), ""] : [],
|
|
13466
|
+
// Completion-gate failures salvaged against a budget/turn-truncated run
|
|
13467
|
+
// (D4, incident afc90342) — the gate output that would otherwise have
|
|
13468
|
+
// only lived in a terminal-failure message the runner never publishes.
|
|
13469
|
+
...run.gateFailureNote ? ["### Completion gate output (not yet passing)", "", redactSecrets(String(run.gateFailureNote)).slice(0, 2e3), ""] : []
|
|
13423
13470
|
];
|
|
13424
|
-
|
|
13471
|
+
const composed = [
|
|
13425
13472
|
...slicedSections,
|
|
13426
13473
|
// Receipt lines the slices dropped — the gate reads only this body.
|
|
13427
13474
|
...preservedReceiptLines(run, slicedSections.join("\n")),
|
|
13428
13475
|
"---",
|
|
13429
13476
|
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._"
|
|
13430
13477
|
].filter((l) => l !== "").join("\n");
|
|
13478
|
+
return governedStakes ? withConsensusDeclaration(composed, governedStakes) : composed;
|
|
13431
13479
|
}
|
|
13432
13480
|
async function mintRunnerGithubTokens({ client, taskId, log: log2, repo = null, requirePublish = false }) {
|
|
13433
13481
|
const publishToken = (await client.getInstallationToken({ required: requirePublish }))?.token ?? null;
|
|
@@ -13448,6 +13496,7 @@ var init_task_helpers = __esm({
|
|
|
13448
13496
|
"use strict";
|
|
13449
13497
|
init_redact_tokens();
|
|
13450
13498
|
init_methodology_composer();
|
|
13499
|
+
init_check_consensus_receipt_core();
|
|
13451
13500
|
RECEIPT_LINE_RE = /receipt id:\s*[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/giu;
|
|
13452
13501
|
}
|
|
13453
13502
|
});
|
|
@@ -15313,7 +15362,7 @@ var init_detached_economics_spool = __esm({
|
|
|
15313
15362
|
});
|
|
15314
15363
|
|
|
15315
15364
|
// ../../scripts/virtual-office/code-runner/killed-run-outcome.mjs
|
|
15316
|
-
import { randomUUID as
|
|
15365
|
+
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
15317
15366
|
async function handleKilledRun({
|
|
15318
15367
|
client,
|
|
15319
15368
|
id,
|
|
@@ -15350,7 +15399,7 @@ async function handleKilledRun({
|
|
|
15350
15399
|
};
|
|
15351
15400
|
}
|
|
15352
15401
|
if (reason === "claim_authority_changed") {
|
|
15353
|
-
const occurrenceId =
|
|
15402
|
+
const occurrenceId = randomUUID7();
|
|
15354
15403
|
const economics = {
|
|
15355
15404
|
occurrence_id: occurrenceId,
|
|
15356
15405
|
runner_id: runnerId,
|
|
@@ -15650,7 +15699,7 @@ var code_runner_daemon_exports = {};
|
|
|
15650
15699
|
__export(code_runner_daemon_exports, {
|
|
15651
15700
|
main: () => main
|
|
15652
15701
|
});
|
|
15653
|
-
import { randomUUID as
|
|
15702
|
+
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
15654
15703
|
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
15655
15704
|
function log(msg) {
|
|
15656
15705
|
console.log(`[code-runner ${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}`);
|
|
@@ -15800,7 +15849,7 @@ async function processOneTask(client, task, cfg, runnerInstanceId, swarmAdmissio
|
|
|
15800
15849
|
}
|
|
15801
15850
|
if (await taskWasCancelled({ client, id, run, safeProgress, log })) return;
|
|
15802
15851
|
if (await gateTestGenTaskOrFail({ client, id, task, files, worktreeDir: wt.worktreeDir, log })) return;
|
|
15803
|
-
if (await enforceCompletionGateOrFail({ client, id, task, worktreeDir: wt.worktreeDir, log })) return;
|
|
15852
|
+
if (await enforceCompletionGateOrFail({ client, id, task, worktreeDir: wt.worktreeDir, run, truncated: partial, log })) return;
|
|
15804
15853
|
const publicationTarget = await resolvePublicationTarget({ task, continuationRestore, worktreeDir: wt.worktreeDir, githubToken, allowAmbientGithubFallback: cfg.allowAmbientGithub });
|
|
15805
15854
|
const localBranch = await resolveOrCreateBranchAsync(wt.worktreeDir, "vo/code-task");
|
|
15806
15855
|
const publicationBranch = publicationTarget.targetBranch || localBranch;
|
|
@@ -15883,7 +15932,7 @@ Closes #${publicationTarget.supersedesPrNumber}` : "";
|
|
|
15883
15932
|
async function main({ env: env2 = process.env, once: once2 = false } = {}) {
|
|
15884
15933
|
const cfg = loadCodeRunnerConfig(env2, { log });
|
|
15885
15934
|
await sweepStaleTaskAttachmentDirectories().catch((error) => log(`stale attachment cleanup failed: ${error.message}`));
|
|
15886
|
-
const runnerInstanceId =
|
|
15935
|
+
const runnerInstanceId = randomUUID8();
|
|
15887
15936
|
const client = createControlPlaneClient({
|
|
15888
15937
|
env: env2,
|
|
15889
15938
|
runnerId: cfg.runnerId,
|
|
@@ -15893,6 +15942,7 @@ async function main({ env: env2 = process.env, once: once2 = false } = {}) {
|
|
|
15893
15942
|
let reconcileStale = true;
|
|
15894
15943
|
let stopping = false;
|
|
15895
15944
|
let active = 0;
|
|
15945
|
+
const activeTaskIds = /* @__PURE__ */ new Set();
|
|
15896
15946
|
const stop = (sig) => {
|
|
15897
15947
|
if (stopping) return;
|
|
15898
15948
|
stopping = true;
|
|
@@ -15907,6 +15957,7 @@ async function main({ env: env2 = process.env, once: once2 = false } = {}) {
|
|
|
15907
15957
|
runnerInstanceId,
|
|
15908
15958
|
requestStop: () => stop("web-control"),
|
|
15909
15959
|
getActiveCount: () => active,
|
|
15960
|
+
getActiveTaskIds: () => [...activeTaskIds],
|
|
15910
15961
|
isRunning: () => !stopping,
|
|
15911
15962
|
startedAt,
|
|
15912
15963
|
log,
|
|
@@ -15997,6 +16048,7 @@ async function main({ env: env2 = process.env, once: once2 = false } = {}) {
|
|
|
15997
16048
|
}
|
|
15998
16049
|
log(`claimed task ${task.code_task_id} (${task.repo})`);
|
|
15999
16050
|
active += 1;
|
|
16051
|
+
activeTaskIds.add(task.code_task_id);
|
|
16000
16052
|
const runTask = task.kind === "inference" ? processInferenceTask(client, task, cfg, { safeProgress, runnerStagePatch, log }) : processOneTask(client, task, cfg, runnerInstanceId, { availableAgents: claimAgents.availableAgents, accountUsage: accountUsage.get() });
|
|
16001
16053
|
const done = runTask.catch(async (error) => {
|
|
16002
16054
|
log(`task ${task.code_task_id} unhandled runner error: ${error.message}`);
|
|
@@ -16016,6 +16068,7 @@ async function main({ env: env2 = process.env, once: once2 = false } = {}) {
|
|
|
16016
16068
|
});
|
|
16017
16069
|
}).finally(() => {
|
|
16018
16070
|
active -= 1;
|
|
16071
|
+
activeTaskIds.delete(task.code_task_id);
|
|
16019
16072
|
});
|
|
16020
16073
|
if (once2) {
|
|
16021
16074
|
await done;
|
|
@@ -16095,8 +16148,163 @@ var init_code_runner_daemon = __esm({
|
|
|
16095
16148
|
import { createRequire as createRequire4 } from "node:module";
|
|
16096
16149
|
|
|
16097
16150
|
// src/runner-readiness.mjs
|
|
16098
|
-
|
|
16099
|
-
|
|
16151
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
16152
|
+
var RUNNER_GITHUB_READINESS_TIMEOUT_MS = 5e4;
|
|
16153
|
+
var RUNNER_IDENTITY_READINESS_TIMEOUT_MS = 1e4;
|
|
16154
|
+
var RUNNER_READINESS_MAX_REPOSITORIES = 4;
|
|
16155
|
+
var RUNNER_READINESS_MAX_TIMER_DELAY_MS = 2147e6;
|
|
16156
|
+
var RUNNER_READINESS_TRANSIENT_MAX_RETRY_AFTER_MS = 3e5;
|
|
16157
|
+
var RUNNER_READINESS_IPC_ACK_TIMEOUT_MS = 5e3;
|
|
16158
|
+
var RUNNER_READINESS_DEFERRAL_MESSAGE_TYPE = "vo-runner-readiness-deferred-v1";
|
|
16159
|
+
var RUNNER_READINESS_DEFERRAL_ACK_TYPE = "vo-runner-readiness-deferred-ack-v1";
|
|
16160
|
+
var RUNNER_REPOSITORY = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u;
|
|
16161
|
+
function runnerRepositoryScopeFromEnv(env2 = {}) {
|
|
16162
|
+
return String(env2.VO_CODE_RUNNER_REPOS || "").split(/[\s,]+/u).map((repo) => repo.trim()).filter(Boolean);
|
|
16163
|
+
}
|
|
16164
|
+
function normalizeRunnerRepositoryScope(repositories) {
|
|
16165
|
+
if (!Array.isArray(repositories) || repositories.length < 1) {
|
|
16166
|
+
throw new Error("VO_CODE_RUNNER_REPOS must name at least one owner/name repository");
|
|
16167
|
+
}
|
|
16168
|
+
if (repositories.length > RUNNER_READINESS_MAX_REPOSITORIES) {
|
|
16169
|
+
throw new Error(`VO_CODE_RUNNER_REPOS supports at most ${RUNNER_READINESS_MAX_REPOSITORIES} repositories`);
|
|
16170
|
+
}
|
|
16171
|
+
const normalized = repositories.map((repo) => {
|
|
16172
|
+
const [owner, name] = typeof repo === "string" ? repo.split("/") : [];
|
|
16173
|
+
if (typeof repo !== "string" || repo.length > 140 || !RUNNER_REPOSITORY.test(repo) || owner === "." || owner === ".." || name === "." || name === "..") {
|
|
16174
|
+
throw new Error("VO_CODE_RUNNER_REPOS entries must be canonical owner/name repositories");
|
|
16175
|
+
}
|
|
16176
|
+
return repo.toLowerCase();
|
|
16177
|
+
});
|
|
16178
|
+
if (new Set(normalized).size !== normalized.length) {
|
|
16179
|
+
throw new Error("VO_CODE_RUNNER_REPOS entries must be unique");
|
|
16180
|
+
}
|
|
16181
|
+
const owners = new Set(normalized.map((repo) => repo.split("/")[0]));
|
|
16182
|
+
if (owners.size !== 1) {
|
|
16183
|
+
throw new Error("VO_CODE_RUNNER_REPOS entries must share one owner");
|
|
16184
|
+
}
|
|
16185
|
+
return Object.freeze(normalized.sort());
|
|
16186
|
+
}
|
|
16187
|
+
function runnerRepositoryScopeDigest(repositories) {
|
|
16188
|
+
const scope = normalizeRunnerRepositoryScope(repositories);
|
|
16189
|
+
return createHash("sha256").update(JSON.stringify({
|
|
16190
|
+
version: 1,
|
|
16191
|
+
repositories: scope
|
|
16192
|
+
}), "utf8").digest("hex");
|
|
16193
|
+
}
|
|
16194
|
+
function runnerReadinessRetryDelayMs(readiness) {
|
|
16195
|
+
if (readiness?.paired !== true || readiness?.githubReady !== false) return null;
|
|
16196
|
+
if (Number.isFinite(readiness.retryAfterMs) && readiness.retryAfterMs > 0) {
|
|
16197
|
+
return Math.ceil(readiness.retryAfterMs);
|
|
16198
|
+
}
|
|
16199
|
+
return null;
|
|
16200
|
+
}
|
|
16201
|
+
function runnerReadinessFailureDisposition(readiness, delivery = null) {
|
|
16202
|
+
const retryAfterMs = runnerReadinessRetryDelayMs(readiness);
|
|
16203
|
+
if (retryAfterMs === null) {
|
|
16204
|
+
return Object.freeze({ action: "exit", exitCode: 1, retryAfterMs: null });
|
|
16205
|
+
}
|
|
16206
|
+
if (delivery?.attempted === true) {
|
|
16207
|
+
return Object.freeze({
|
|
16208
|
+
action: "exit",
|
|
16209
|
+
exitCode: delivery.delivered === true ? 75 : 1,
|
|
16210
|
+
retryAfterMs
|
|
16211
|
+
});
|
|
16212
|
+
}
|
|
16213
|
+
return Object.freeze({ action: "wait", exitCode: null, retryAfterMs });
|
|
16214
|
+
}
|
|
16215
|
+
async function notifySupervisorReadinessDeferral({
|
|
16216
|
+
processLike,
|
|
16217
|
+
retryAfterMs,
|
|
16218
|
+
error = null,
|
|
16219
|
+
ackTimeoutMs = RUNNER_READINESS_IPC_ACK_TIMEOUT_MS
|
|
16220
|
+
}) {
|
|
16221
|
+
if (processLike?.connected !== true || typeof processLike.send !== "function") {
|
|
16222
|
+
return Object.freeze({ attempted: false, delivered: false });
|
|
16223
|
+
}
|
|
16224
|
+
if (typeof processLike.on !== "function" || typeof processLike.off !== "function") {
|
|
16225
|
+
return Object.freeze({ attempted: true, delivered: false });
|
|
16226
|
+
}
|
|
16227
|
+
const nonce = randomUUID();
|
|
16228
|
+
const delivered = await new Promise((resolve3) => {
|
|
16229
|
+
let settled = false;
|
|
16230
|
+
const onMessage = (message) => {
|
|
16231
|
+
if (message?.type === RUNNER_READINESS_DEFERRAL_ACK_TYPE && message.nonce === nonce) {
|
|
16232
|
+
finish(true);
|
|
16233
|
+
}
|
|
16234
|
+
};
|
|
16235
|
+
const onDisconnect = () => finish(false);
|
|
16236
|
+
const finish = (value) => {
|
|
16237
|
+
if (settled) return;
|
|
16238
|
+
settled = true;
|
|
16239
|
+
clearTimeout(timer);
|
|
16240
|
+
processLike.off("message", onMessage);
|
|
16241
|
+
processLike.off("disconnect", onDisconnect);
|
|
16242
|
+
resolve3(value);
|
|
16243
|
+
};
|
|
16244
|
+
const timer = setTimeout(() => finish(false), ackTimeoutMs);
|
|
16245
|
+
processLike.on("message", onMessage);
|
|
16246
|
+
processLike.on("disconnect", onDisconnect);
|
|
16247
|
+
try {
|
|
16248
|
+
processLike.send({
|
|
16249
|
+
type: RUNNER_READINESS_DEFERRAL_MESSAGE_TYPE,
|
|
16250
|
+
nonce,
|
|
16251
|
+
retryAfterMs,
|
|
16252
|
+
error
|
|
16253
|
+
}, (sendError) => {
|
|
16254
|
+
if (sendError) finish(false);
|
|
16255
|
+
});
|
|
16256
|
+
} catch {
|
|
16257
|
+
finish(false);
|
|
16258
|
+
}
|
|
16259
|
+
});
|
|
16260
|
+
return Object.freeze({ attempted: true, delivered });
|
|
16261
|
+
}
|
|
16262
|
+
async function waitForRunnerReadinessRetry(delayMs, {
|
|
16263
|
+
nowMs = () => Date.now(),
|
|
16264
|
+
wait: wait3 = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms))
|
|
16265
|
+
} = {}) {
|
|
16266
|
+
if (!Number.isFinite(delayMs) || delayMs <= 0) {
|
|
16267
|
+
throw new TypeError("runner readiness retry delay is invalid");
|
|
16268
|
+
}
|
|
16269
|
+
const deadline = nowMs() + Math.ceil(delayMs);
|
|
16270
|
+
while (nowMs() < deadline) {
|
|
16271
|
+
await wait3(Math.min(RUNNER_READINESS_MAX_TIMER_DELAY_MS, deadline - nowMs()));
|
|
16272
|
+
}
|
|
16273
|
+
}
|
|
16274
|
+
function failed({
|
|
16275
|
+
paired = false,
|
|
16276
|
+
operatorId = null,
|
|
16277
|
+
tenantId = null,
|
|
16278
|
+
githubReady = null,
|
|
16279
|
+
retryAfterMs = null,
|
|
16280
|
+
error,
|
|
16281
|
+
message
|
|
16282
|
+
}) {
|
|
16283
|
+
return {
|
|
16284
|
+
ok: false,
|
|
16285
|
+
paired,
|
|
16286
|
+
operatorId,
|
|
16287
|
+
tenantId,
|
|
16288
|
+
githubReady,
|
|
16289
|
+
...Number.isFinite(retryAfterMs) && retryAfterMs > 0 ? { retryAfterMs } : {},
|
|
16290
|
+
error,
|
|
16291
|
+
message
|
|
16292
|
+
};
|
|
16293
|
+
}
|
|
16294
|
+
function responseRetryAfterMs(response, body) {
|
|
16295
|
+
const transientReadinessFailure = response.status === 503 && body?.error === "github_installation_readiness_retryable";
|
|
16296
|
+
if (response.status !== 429 && !transientReadinessFailure) return null;
|
|
16297
|
+
const bound = (retryAfterMs) => transientReadinessFailure ? Math.min(RUNNER_READINESS_TRANSIENT_MAX_RETRY_AFTER_MS, retryAfterMs) : retryAfterMs;
|
|
16298
|
+
const value = response.headers?.get?.("retry-after")?.trim() ?? "";
|
|
16299
|
+
const seconds = Number(value);
|
|
16300
|
+
if (value && Number.isFinite(seconds) && seconds >= 0) {
|
|
16301
|
+
return bound(Math.max(1e3, Math.ceil(seconds * 1e3)));
|
|
16302
|
+
}
|
|
16303
|
+
const at = value ? Date.parse(value) : Number.NaN;
|
|
16304
|
+
if (Number.isFinite(at)) {
|
|
16305
|
+
return bound(Math.max(1e3, Math.ceil(at - Date.now())));
|
|
16306
|
+
}
|
|
16307
|
+
return bound(6e4);
|
|
16100
16308
|
}
|
|
16101
16309
|
async function responseBody(response) {
|
|
16102
16310
|
try {
|
|
@@ -16109,11 +16317,21 @@ async function responseBody(response) {
|
|
|
16109
16317
|
function serverMessage(body, fallback) {
|
|
16110
16318
|
return typeof body.message === "string" && body.message.trim() ? body.message.trim() : fallback;
|
|
16111
16319
|
}
|
|
16112
|
-
async function
|
|
16320
|
+
async function fetchJsonWithTimeout(fetchImpl, url, init, timeoutMs) {
|
|
16113
16321
|
const controller = new AbortController();
|
|
16114
|
-
|
|
16322
|
+
let timer;
|
|
16323
|
+
const timeout = new Promise((_, reject) => {
|
|
16324
|
+
timer = setTimeout(() => {
|
|
16325
|
+
controller.abort();
|
|
16326
|
+
reject(new Error(`request aborted after ${timeoutMs}ms`));
|
|
16327
|
+
}, timeoutMs);
|
|
16328
|
+
});
|
|
16115
16329
|
try {
|
|
16116
|
-
|
|
16330
|
+
const requestAndBody = (async () => {
|
|
16331
|
+
const response = await fetchImpl(url, { ...init, signal: controller.signal });
|
|
16332
|
+
return { response, body: await responseBody(response) };
|
|
16333
|
+
})();
|
|
16334
|
+
return await Promise.race([requestAndBody, timeout]);
|
|
16117
16335
|
} finally {
|
|
16118
16336
|
clearTimeout(timer);
|
|
16119
16337
|
}
|
|
@@ -16123,18 +16341,24 @@ async function probeRunnerReadiness({
|
|
|
16123
16341
|
token: token2,
|
|
16124
16342
|
fetchImpl = fetch,
|
|
16125
16343
|
requireGithub = false,
|
|
16126
|
-
|
|
16344
|
+
repositories = [],
|
|
16345
|
+
timeoutMs = RUNNER_IDENTITY_READINESS_TIMEOUT_MS,
|
|
16346
|
+
// The server performs at most five sequential App-JWT GETs (installation plus
|
|
16347
|
+
// every one of at most four configured repositories), each bounded at 8s.
|
|
16348
|
+
// Keep a transport margin while preventing a stuck proof from hanging startup.
|
|
16349
|
+
githubTimeoutMs = RUNNER_GITHUB_READINESS_TIMEOUT_MS
|
|
16127
16350
|
}) {
|
|
16128
16351
|
const base = controlPlaneUrl2.replace(/\/+$/u, "");
|
|
16129
16352
|
const headers = { authorization: `Bearer ${token2}` };
|
|
16130
16353
|
let identityResponse;
|
|
16354
|
+
let identity;
|
|
16131
16355
|
try {
|
|
16132
|
-
identityResponse = await
|
|
16356
|
+
({ response: identityResponse, body: identity } = await fetchJsonWithTimeout(
|
|
16133
16357
|
fetchImpl,
|
|
16134
16358
|
`${base}/api/v1/auth/me`,
|
|
16135
16359
|
{ headers },
|
|
16136
16360
|
timeoutMs
|
|
16137
|
-
);
|
|
16361
|
+
));
|
|
16138
16362
|
} catch (error) {
|
|
16139
16363
|
const detail = error instanceof Error ? error.message : String(error);
|
|
16140
16364
|
return failed({
|
|
@@ -16142,7 +16366,6 @@ async function probeRunnerReadiness({
|
|
|
16142
16366
|
message: `AlgoHQ could not be reached: ${detail}`
|
|
16143
16367
|
});
|
|
16144
16368
|
}
|
|
16145
|
-
const identity = await responseBody(identityResponse);
|
|
16146
16369
|
if (!identityResponse.ok) {
|
|
16147
16370
|
return failed({
|
|
16148
16371
|
error: "credential_rejected",
|
|
@@ -16168,18 +16391,33 @@ async function probeRunnerReadiness({
|
|
|
16168
16391
|
message: "Paired to AlgoHQ."
|
|
16169
16392
|
};
|
|
16170
16393
|
}
|
|
16394
|
+
let repositoryScope = null;
|
|
16395
|
+
try {
|
|
16396
|
+
if (!Array.isArray(repositories)) {
|
|
16397
|
+
throw new Error("VO_CODE_RUNNER_REPOS must be a repository array");
|
|
16398
|
+
}
|
|
16399
|
+
if (repositories.length > 0) repositoryScope = normalizeRunnerRepositoryScope(repositories);
|
|
16400
|
+
} catch (error) {
|
|
16401
|
+
return failed({
|
|
16402
|
+
paired: true,
|
|
16403
|
+
operatorId,
|
|
16404
|
+
tenantId,
|
|
16405
|
+
githubReady: false,
|
|
16406
|
+
error: "github_repository_scope_invalid",
|
|
16407
|
+
message: error instanceof Error ? error.message : String(error)
|
|
16408
|
+
});
|
|
16409
|
+
}
|
|
16171
16410
|
let githubResponse;
|
|
16411
|
+
let github;
|
|
16172
16412
|
try {
|
|
16173
|
-
|
|
16413
|
+
const readinessUrl = new URL(`${base}/api/v1/github/installation-readiness`);
|
|
16414
|
+
for (const repo of repositoryScope ?? []) readinessUrl.searchParams.append("repo", repo);
|
|
16415
|
+
({ response: githubResponse, body: github } = await fetchJsonWithTimeout(
|
|
16174
16416
|
fetchImpl,
|
|
16175
|
-
|
|
16176
|
-
{
|
|
16177
|
-
|
|
16178
|
-
|
|
16179
|
-
body: "{}"
|
|
16180
|
-
},
|
|
16181
|
-
timeoutMs
|
|
16182
|
-
);
|
|
16417
|
+
readinessUrl.toString(),
|
|
16418
|
+
{ headers },
|
|
16419
|
+
githubTimeoutMs
|
|
16420
|
+
));
|
|
16183
16421
|
} catch (error) {
|
|
16184
16422
|
const detail = error instanceof Error ? error.message : String(error);
|
|
16185
16423
|
return failed({
|
|
@@ -16191,14 +16429,25 @@ async function probeRunnerReadiness({
|
|
|
16191
16429
|
message: `GitHub publication readiness could not be checked: ${detail}`
|
|
16192
16430
|
});
|
|
16193
16431
|
}
|
|
16194
|
-
const
|
|
16195
|
-
|
|
16432
|
+
const repositorySelection = github.repository_selection;
|
|
16433
|
+
const repositoriesVerified = github.repositories_verified;
|
|
16434
|
+
const returnedScope = github.repository_scope;
|
|
16435
|
+
let normalizedReturnedScope = null;
|
|
16436
|
+
try {
|
|
16437
|
+
normalizedReturnedScope = normalizeRunnerRepositoryScope(returnedScope);
|
|
16438
|
+
} catch {
|
|
16439
|
+
}
|
|
16440
|
+
const effectiveScope = repositoryScope ?? normalizedReturnedScope;
|
|
16441
|
+
const sourceVerified = repositoryScope === null ? github.repository_scope_source === "persisted_installation_singleton" && normalizedReturnedScope?.length === 1 : github.repository_scope_source === "runner_config";
|
|
16442
|
+
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");
|
|
16443
|
+
if (!githubResponse.ok || github.configured !== true || github.verified !== true || github.publication_ready !== true || !repositoryScopeVerified) {
|
|
16196
16444
|
const error = typeof github.error === "string" && github.error ? github.error : "github_not_ready";
|
|
16197
16445
|
return failed({
|
|
16198
16446
|
paired: true,
|
|
16199
16447
|
operatorId,
|
|
16200
16448
|
tenantId,
|
|
16201
16449
|
githubReady: false,
|
|
16450
|
+
retryAfterMs: responseRetryAfterMs(githubResponse, github),
|
|
16202
16451
|
error,
|
|
16203
16452
|
message: serverMessage(github, `GitHub publication preflight failed (HTTP ${githubResponse.status}).`)
|
|
16204
16453
|
});
|
|
@@ -16209,8 +16458,10 @@ async function probeRunnerReadiness({
|
|
|
16209
16458
|
operatorId,
|
|
16210
16459
|
tenantId,
|
|
16211
16460
|
githubReady: true,
|
|
16461
|
+
repositoryScope: effectiveScope,
|
|
16462
|
+
repositoryScopeSource: github.repository_scope_source,
|
|
16212
16463
|
error: null,
|
|
16213
|
-
message:
|
|
16464
|
+
message: `Paired with a verified Algosuite GitHub App installation (${repositoriesVerified} repos).`
|
|
16214
16465
|
};
|
|
16215
16466
|
}
|
|
16216
16467
|
function pairedOperatorScope(readiness) {
|
|
@@ -16221,7 +16472,7 @@ function pairedOperatorScope(readiness) {
|
|
|
16221
16472
|
import { closeSync, existsSync, mkdirSync, openSync, unlinkSync } from "node:fs";
|
|
16222
16473
|
import { homedir } from "node:os";
|
|
16223
16474
|
import { posix, win32 } from "node:path";
|
|
16224
|
-
import { randomUUID } from "node:crypto";
|
|
16475
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
16225
16476
|
var APP_IDENTIFIER = "ai.algosuite.vo-runner";
|
|
16226
16477
|
var CLONES_DIR = "clones";
|
|
16227
16478
|
function pathsFor(platform4) {
|
|
@@ -16291,7 +16542,7 @@ function runnerWorkingDirectory({ repoRoot: repoRoot2, clonesRoot: clonesRoot2 }
|
|
|
16291
16542
|
}
|
|
16292
16543
|
function assertWritableRunnerDirectory(root) {
|
|
16293
16544
|
mkdirSync(root, { recursive: true });
|
|
16294
|
-
const probe = pathsFor(process.platform).join(root, `.vo-runner-write-probe-${process.pid}-${
|
|
16545
|
+
const probe = pathsFor(process.platform).join(root, `.vo-runner-write-probe-${process.pid}-${randomUUID2()}`);
|
|
16295
16546
|
let handle;
|
|
16296
16547
|
try {
|
|
16297
16548
|
handle = openSync(probe, "wx", 384);
|
|
@@ -16411,21 +16662,51 @@ if (!token) {
|
|
|
16411
16662
|
}
|
|
16412
16663
|
var controlPlaneUrl = process.env.VO_CONTROL_PLANE_URL || DEFAULT_CONTROL_PLANE_URL3;
|
|
16413
16664
|
var pairedOperatorId = null;
|
|
16414
|
-
|
|
16415
|
-
const
|
|
16416
|
-
|
|
16417
|
-
|
|
16418
|
-
|
|
16665
|
+
async function deferRunnerReadinessRetry(readiness) {
|
|
16666
|
+
const initial = runnerReadinessFailureDisposition(readiness);
|
|
16667
|
+
if (initial.action === "exit") return initial;
|
|
16668
|
+
const { retryAfterMs } = initial;
|
|
16669
|
+
console.error(
|
|
16670
|
+
`[vo-mcp runner] GitHub readiness is temporarily unavailable; retrying after ${retryAfterMs}ms: ${readiness.message}`
|
|
16671
|
+
);
|
|
16672
|
+
const delivery = await notifySupervisorReadinessDeferral({
|
|
16673
|
+
processLike: process,
|
|
16674
|
+
retryAfterMs,
|
|
16675
|
+
error: readiness.error
|
|
16419
16676
|
});
|
|
16420
|
-
if (
|
|
16421
|
-
|
|
16422
|
-
|
|
16677
|
+
if (delivery.attempted) {
|
|
16678
|
+
const disposition = runnerReadinessFailureDisposition(readiness, delivery);
|
|
16679
|
+
if (disposition.exitCode === 1) {
|
|
16680
|
+
console.error("[vo-mcp runner] Supervisor readiness deferral could not be delivered; failing closed.");
|
|
16681
|
+
}
|
|
16682
|
+
return disposition;
|
|
16683
|
+
}
|
|
16684
|
+
await waitForRunnerReadinessRetry(retryAfterMs);
|
|
16685
|
+
return Object.freeze({ action: "retry", exitCode: null, retryAfterMs });
|
|
16686
|
+
}
|
|
16687
|
+
if (!explicitAdminToken) {
|
|
16688
|
+
let readiness;
|
|
16689
|
+
while (true) {
|
|
16690
|
+
readiness = await probeRunnerReadiness({
|
|
16691
|
+
controlPlaneUrl,
|
|
16692
|
+
token,
|
|
16693
|
+
requireGithub: true,
|
|
16694
|
+
repositories: runnerRepositoryScopeFromEnv(process.env)
|
|
16695
|
+
});
|
|
16696
|
+
if (readiness.ok) break;
|
|
16697
|
+
const disposition = await deferRunnerReadinessRetry(readiness);
|
|
16698
|
+
if (disposition.action === "retry") continue;
|
|
16699
|
+
if (disposition.retryAfterMs === null) {
|
|
16700
|
+
console.error(`[vo-mcp runner] Readiness check failed: ${readiness.message}`);
|
|
16701
|
+
}
|
|
16702
|
+
process.exit(disposition.exitCode);
|
|
16423
16703
|
}
|
|
16424
16704
|
pairedOperatorId = pairedOperatorScope(readiness);
|
|
16425
16705
|
if (!pairedOperatorId) {
|
|
16426
16706
|
console.error("[vo-mcp runner] Readiness check failed: paired operator scope is missing. Pair this computer again.");
|
|
16427
16707
|
process.exit(1);
|
|
16428
16708
|
}
|
|
16709
|
+
process.env.VO_CODE_RUNNER_REPOS = readiness.repositoryScope.join(",");
|
|
16429
16710
|
}
|
|
16430
16711
|
var rootConfig;
|
|
16431
16712
|
try {
|