@algosuite/vo-mcp 0.2.0-beta.18 → 0.2.0-beta.19
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 +25 -12
- package/dist/autostart-cli.js.map +2 -2
- package/dist/cli.js +117 -34
- package/dist/cli.js.map +4 -4
- package/dist/index.js +108 -22
- package/dist/index.js.map +4 -4
- package/dist/install-cli.js +15 -6
- package/dist/install-cli.js.map +2 -2
- package/dist/login-cli.js +1 -1
- package/dist/login-cli.js.map +2 -2
- package/dist/runner-cli.js +284 -113
- package/dist/runner-cli.js.map +4 -4
- package/dist/runner-supervisor.js +21 -6
- package/dist/runner-supervisor.js.map +2 -2
- package/package.json +1 -1
package/dist/runner-cli.js
CHANGED
|
@@ -2142,11 +2142,11 @@ function createControlPlaneClient({
|
|
|
2142
2142
|
throw new Error("VO_CONTROL_PLANE_URL is required for the code-runner daemon");
|
|
2143
2143
|
}
|
|
2144
2144
|
const root = resolvedBaseUrl.replace(/\/+$/, "");
|
|
2145
|
-
async function req(method,
|
|
2145
|
+
async function req(method, path16, body, { timeoutMs } = {}) {
|
|
2146
2146
|
const bearer = await resolveBearer(env2);
|
|
2147
2147
|
const controller = timeoutMs ? new AbortController() : null;
|
|
2148
2148
|
let timeoutId;
|
|
2149
|
-
const request = Promise.resolve(fetchImpl(`${root}${
|
|
2149
|
+
const request = Promise.resolve(fetchImpl(`${root}${path16}`, {
|
|
2150
2150
|
method,
|
|
2151
2151
|
headers: {
|
|
2152
2152
|
"content-type": "application/json",
|
|
@@ -2159,7 +2159,7 @@ function createControlPlaneClient({
|
|
|
2159
2159
|
const timeout = new Promise((_, reject) => {
|
|
2160
2160
|
timeoutId = setTimeout(() => {
|
|
2161
2161
|
controller.abort();
|
|
2162
|
-
reject(new Error(`control-plane ${
|
|
2162
|
+
reject(new Error(`control-plane ${path16} timed out after ${timeoutMs}ms`));
|
|
2163
2163
|
}, timeoutMs);
|
|
2164
2164
|
});
|
|
2165
2165
|
try {
|
|
@@ -2278,8 +2278,8 @@ function createControlPlaneClient({
|
|
|
2278
2278
|
return json ? json.task : null;
|
|
2279
2279
|
},
|
|
2280
2280
|
async downloadTaskAttachment(taskId, attachmentId) {
|
|
2281
|
-
const
|
|
2282
|
-
const res = await req("GET",
|
|
2281
|
+
const path16 = `/api/v1/code-task/${encodeURIComponent(taskId)}/attachment/${encodeURIComponent(attachmentId)}`;
|
|
2282
|
+
const res = await req("GET", path16);
|
|
2283
2283
|
if (res.status === 401) cachedFirebaseToken = null;
|
|
2284
2284
|
if (!res.ok) throw new Error(`attachment download failed: HTTP ${res.status}`);
|
|
2285
2285
|
return Buffer.from(await res.arrayBuffer());
|
|
@@ -3089,17 +3089,26 @@ function selectOrphanKills({ instances = [], liveProcesses = /* @__PURE__ */ new
|
|
|
3089
3089
|
}
|
|
3090
3090
|
return { kills, pruneDirs };
|
|
3091
3091
|
}
|
|
3092
|
-
function
|
|
3092
|
+
function windowsSystemRoot(env2 = process.env) {
|
|
3093
|
+
return env2.SystemRoot || env2.WINDIR || "C:\\Windows";
|
|
3094
|
+
}
|
|
3095
|
+
function windowsPowershellExe(env2 = process.env) {
|
|
3096
|
+
return path10.join(windowsSystemRoot(env2), "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
|
|
3097
|
+
}
|
|
3098
|
+
function listProcessCreationTimes({ platform = process.platform, spawn: spawn5 = spawnSync5, env: env2 = process.env, warn = console.warn } = {}) {
|
|
3093
3099
|
const map = /* @__PURE__ */ new Map();
|
|
3094
3100
|
if (platform === "win32") {
|
|
3095
3101
|
const ps = "Get-CimInstance Win32_Process | ForEach-Object { '{0} {1}' -f $_.ProcessId, (([DateTimeOffset]$_.CreationDate.ToUniversalTime()).ToUnixTimeMilliseconds()) }";
|
|
3096
|
-
const result2 = spawn5(
|
|
3102
|
+
const result2 = spawn5(windowsPowershellExe(env2), ["-NoProfile", "-NonInteractive", "-Command", ps], {
|
|
3097
3103
|
windowsHide: true,
|
|
3098
3104
|
encoding: "utf8",
|
|
3099
3105
|
timeout: 2e4,
|
|
3100
3106
|
maxBuffer: 32 * 1024 * 1024
|
|
3101
3107
|
});
|
|
3102
|
-
if (result2.error || result2.status !== 0 || typeof result2.stdout !== "string")
|
|
3108
|
+
if (result2.error || result2.status !== 0 || typeof result2.stdout !== "string") {
|
|
3109
|
+
warn(`[orphan-reaper] process enumeration failed (${result2.error ? result2.error.message : `powershell exit ${result2.status}`}); reaping nothing this cycle`);
|
|
3110
|
+
return map;
|
|
3111
|
+
}
|
|
3103
3112
|
for (const line of result2.stdout.split(/\r?\n/)) {
|
|
3104
3113
|
const m = line.trim().match(/^(\d+)\s+(-?\d+)$/);
|
|
3105
3114
|
if (m) map.set(Number(m[1]), { creationMs: Number(m[2]) });
|
|
@@ -3107,7 +3116,10 @@ function listProcessCreationTimes({ platform = process.platform, spawn: spawn5 =
|
|
|
3107
3116
|
return map;
|
|
3108
3117
|
}
|
|
3109
3118
|
const result = spawn5("ps", ["-eo", "pid=,lstart="], { encoding: "utf8", timeout: 2e4, maxBuffer: 32 * 1024 * 1024 });
|
|
3110
|
-
if (result.error || result.status !== 0 || typeof result.stdout !== "string")
|
|
3119
|
+
if (result.error || result.status !== 0 || typeof result.stdout !== "string") {
|
|
3120
|
+
warn(`[orphan-reaper] process enumeration failed (${result.error ? result.error.message : `ps exit ${result.status}`}); reaping nothing this cycle`);
|
|
3121
|
+
return map;
|
|
3122
|
+
}
|
|
3111
3123
|
for (const line of result.stdout.split(/\r?\n/)) {
|
|
3112
3124
|
const parsed = parsePosixPsLine(line);
|
|
3113
3125
|
if (parsed) map.set(parsed.pid, { creationMs: parsed.creationMs });
|
|
@@ -3123,10 +3135,11 @@ function parsePosixPsLine(line) {
|
|
|
3123
3135
|
if (!Number.isInteger(pid) || pid <= 0 || !Number.isFinite(when)) return null;
|
|
3124
3136
|
return { pid, creationMs: when };
|
|
3125
3137
|
}
|
|
3126
|
-
function killProcessTree(pid, { platform = process.platform, spawn: spawn5 = spawnSync5 } = {}) {
|
|
3138
|
+
function killProcessTree(pid, { platform = process.platform, spawn: spawn5 = spawnSync5, env: env2 = process.env } = {}) {
|
|
3127
3139
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
3128
3140
|
if (platform === "win32") {
|
|
3129
|
-
const
|
|
3141
|
+
const taskkill = path10.join(windowsSystemRoot(env2), "System32", "taskkill.exe");
|
|
3142
|
+
const r = spawn5(taskkill, ["/PID", String(pid), "/T", "/F"], { windowsHide: true, stdio: "ignore", timeout: 15e3 });
|
|
3130
3143
|
return !r.error && r.status === 0;
|
|
3131
3144
|
}
|
|
3132
3145
|
try {
|
|
@@ -3660,7 +3673,7 @@ function resolveCodexBinary({
|
|
|
3660
3673
|
return "codex";
|
|
3661
3674
|
}
|
|
3662
3675
|
function buildCodexArgs({ model, effort } = {}) {
|
|
3663
|
-
const args = ["exec", "--json", "-c", 'approval_policy="never"', "--sandbox", "workspace-write"];
|
|
3676
|
+
const args = ["exec", "--json", "-c", 'approval_policy="never"', "--sandbox", "workspace-write", "--skip-git-repo-check"];
|
|
3664
3677
|
if (model) {
|
|
3665
3678
|
args.push("--model", String(model));
|
|
3666
3679
|
}
|
|
@@ -3729,11 +3742,25 @@ var init_codex_runner = __esm({
|
|
|
3729
3742
|
parseEvent(line) {
|
|
3730
3743
|
return parseCodexEvent(line);
|
|
3731
3744
|
}
|
|
3732
|
-
|
|
3733
|
-
|
|
3745
|
+
/**
|
|
3746
|
+
* SECURITY: never `shell: true` — same RCE class as cursor-runner. The old
|
|
3747
|
+
* `shell: win32 && !/\.exe$/` fell back to shell mode whenever
|
|
3748
|
+
* resolveCodexBinary() could not find one of its hardcoded absolute paths and
|
|
3749
|
+
* returned the bare string 'codex'. Node's shell mode joins argv into
|
|
3750
|
+
* `cmd /d /s /c` with windowsVerbatimArguments, and buildCodexArgs() puts the
|
|
3751
|
+
* control-plane-controlled `model` into argv, so a payload of
|
|
3752
|
+
* `{ agent: 'codex', model: 'gpt-5 & <cmd>' }` executed arbitrary code —
|
|
3753
|
+
* including on hosts where codex is NOT installed, because cmd runs the first
|
|
3754
|
+
* command, it fails, and `&` runs the rest anyway.
|
|
3755
|
+
*
|
|
3756
|
+
* With shell:false a `.cmd`/`.ps1` shim no longer resolves and the spawn fails
|
|
3757
|
+
* closed with ENOENT, matching resolveWindowsClaudeExecutable()'s policy.
|
|
3758
|
+
*/
|
|
3759
|
+
getSpawnOptions() {
|
|
3734
3760
|
return {
|
|
3735
|
-
shell:
|
|
3736
|
-
windowsHide: true
|
|
3761
|
+
shell: false,
|
|
3762
|
+
windowsHide: true,
|
|
3763
|
+
windowsVerbatimArguments: false
|
|
3737
3764
|
};
|
|
3738
3765
|
}
|
|
3739
3766
|
/**
|
|
@@ -3867,10 +3894,26 @@ var init_cursor_runner = __esm({
|
|
|
3867
3894
|
parseEvent(line) {
|
|
3868
3895
|
return parseCursorEvent(line);
|
|
3869
3896
|
}
|
|
3897
|
+
/**
|
|
3898
|
+
* SECURITY: never `shell: true`. Node's shell mode on Windows joins argv and
|
|
3899
|
+
* hands it to `cmd /d /s /c` with windowsVerbatimArguments, so every cmd
|
|
3900
|
+
* metacharacter (& | > ^) in an argument is interpreted by the shell. This
|
|
3901
|
+
* runner puts two control-plane-controlled strings into argv — `task.model`
|
|
3902
|
+
* and the composed prompt (buildCursorArgs) — so shell mode turned a task
|
|
3903
|
+
* payload into arbitrary host code execution. It fired even without
|
|
3904
|
+
* cursor-agent installed: cmd runs the first command, it fails, and `&` runs
|
|
3905
|
+
* the rest anyway. With shell:false argv goes straight to CreateProcess and
|
|
3906
|
+
* metacharacters are inert.
|
|
3907
|
+
*
|
|
3908
|
+
* Consequence on Windows: a `.cmd`/`.ps1` shim no longer resolves, so the
|
|
3909
|
+
* runner fails closed with ENOENT rather than executing through a shell —
|
|
3910
|
+
* the same policy resolveWindowsClaudeExecutable() enforces for Claude.
|
|
3911
|
+
*/
|
|
3870
3912
|
getSpawnOptions() {
|
|
3871
3913
|
return {
|
|
3872
|
-
shell:
|
|
3873
|
-
windowsHide: true
|
|
3914
|
+
shell: false,
|
|
3915
|
+
windowsHide: true,
|
|
3916
|
+
windowsVerbatimArguments: false
|
|
3874
3917
|
};
|
|
3875
3918
|
}
|
|
3876
3919
|
/**
|
|
@@ -3885,7 +3928,7 @@ var init_cursor_runner = __esm({
|
|
|
3885
3928
|
async checkAuth() {
|
|
3886
3929
|
try {
|
|
3887
3930
|
const { status, error } = spawnSync7("cursor-agent", ["--version"], {
|
|
3888
|
-
shell:
|
|
3931
|
+
shell: false,
|
|
3889
3932
|
windowsHide: true,
|
|
3890
3933
|
timeout: 3e3,
|
|
3891
3934
|
stdio: "ignore"
|
|
@@ -3942,11 +3985,13 @@ var init_meta_runner = __esm({
|
|
|
3942
3985
|
parseEvent(line) {
|
|
3943
3986
|
return parseCodexEvent(line);
|
|
3944
3987
|
}
|
|
3945
|
-
|
|
3946
|
-
|
|
3988
|
+
// SECURITY: never shell — see no-shell-spawn.test.mjs. Inert today (buildArgs
|
|
3989
|
+
// throws) but this goes hot the moment the transport is enabled.
|
|
3990
|
+
getSpawnOptions() {
|
|
3947
3991
|
return {
|
|
3948
|
-
shell:
|
|
3949
|
-
windowsHide: true
|
|
3992
|
+
shell: false,
|
|
3993
|
+
windowsHide: true,
|
|
3994
|
+
windowsVerbatimArguments: false
|
|
3950
3995
|
};
|
|
3951
3996
|
}
|
|
3952
3997
|
applyAuthEnv(env2 = process.env) {
|
|
@@ -3995,11 +4040,13 @@ var init_openai_compatible_runner = __esm({
|
|
|
3995
4040
|
parseEvent(line) {
|
|
3996
4041
|
return parseCodexEvent(line);
|
|
3997
4042
|
}
|
|
3998
|
-
|
|
3999
|
-
|
|
4043
|
+
// SECURITY: never shell — see no-shell-spawn.test.mjs. Inert today (buildArgs
|
|
4044
|
+
// throws) but this goes hot the moment the transport is enabled.
|
|
4045
|
+
getSpawnOptions() {
|
|
4000
4046
|
return {
|
|
4001
|
-
shell:
|
|
4002
|
-
windowsHide: true
|
|
4047
|
+
shell: false,
|
|
4048
|
+
windowsHide: true,
|
|
4049
|
+
windowsVerbatimArguments: false
|
|
4003
4050
|
};
|
|
4004
4051
|
}
|
|
4005
4052
|
/** Fill the BYO key env var from the OS keychain when not already set. */
|
|
@@ -4244,6 +4291,16 @@ var init_rate_limit_resume = __esm({
|
|
|
4244
4291
|
}
|
|
4245
4292
|
});
|
|
4246
4293
|
|
|
4294
|
+
// ../../scripts/virtual-office/code-runner/secure-random.mjs
|
|
4295
|
+
import { randomInt } from "node:crypto";
|
|
4296
|
+
var secureUnitRandom;
|
|
4297
|
+
var init_secure_random = __esm({
|
|
4298
|
+
"../../scripts/virtual-office/code-runner/secure-random.mjs"() {
|
|
4299
|
+
"use strict";
|
|
4300
|
+
secureUnitRandom = () => randomInt(0, 2 ** 32) / 2 ** 32;
|
|
4301
|
+
}
|
|
4302
|
+
});
|
|
4303
|
+
|
|
4247
4304
|
// ../../scripts/virtual-office/code-runner/git-resilience.mjs
|
|
4248
4305
|
function isTransientGitError(err) {
|
|
4249
4306
|
if (!err) return false;
|
|
@@ -4255,7 +4312,7 @@ function isTransientGitError(err) {
|
|
|
4255
4312
|
}
|
|
4256
4313
|
return TRANSIENT_RE.test(msg);
|
|
4257
4314
|
}
|
|
4258
|
-
function computeGitBackoffMs(attempt, { baseMs = 5e3, capMs = 3e4, rng =
|
|
4315
|
+
function computeGitBackoffMs(attempt, { baseMs = 5e3, capMs = 3e4, rng = secureUnitRandom } = {}) {
|
|
4259
4316
|
const exp = Math.min(capMs, baseMs * Math.pow(2, Math.max(0, attempt)));
|
|
4260
4317
|
return Math.floor(exp / 2 + rng() * (exp / 2));
|
|
4261
4318
|
}
|
|
@@ -4263,6 +4320,7 @@ var TRANSIENT_CODES, TRANSIENT_RE;
|
|
|
4263
4320
|
var init_git_resilience = __esm({
|
|
4264
4321
|
"../../scripts/virtual-office/code-runner/git-resilience.mjs"() {
|
|
4265
4322
|
"use strict";
|
|
4323
|
+
init_secure_random();
|
|
4266
4324
|
TRANSIENT_CODES = /* @__PURE__ */ new Set([
|
|
4267
4325
|
"ETIMEDOUT",
|
|
4268
4326
|
"ECONNRESET",
|
|
@@ -4289,10 +4347,39 @@ var init_auto_merge = __esm({
|
|
|
4289
4347
|
|
|
4290
4348
|
// ../../scripts/virtual-office/code-runner/pr-overlap-gate.mjs
|
|
4291
4349
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
4292
|
-
import
|
|
4350
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
4351
|
+
import { fileURLToPath } from "node:url";
|
|
4352
|
+
function stripCredentials(env2 = process.env) {
|
|
4353
|
+
const safe = { ...env2 };
|
|
4354
|
+
for (const key of CREDENTIAL_ENV_KEYS) delete safe[key];
|
|
4355
|
+
return safe;
|
|
4356
|
+
}
|
|
4357
|
+
function resolveOverlapScript({
|
|
4358
|
+
worktreeDir,
|
|
4359
|
+
trustedPath = TRUSTED_OVERLAP_SCRIPT,
|
|
4360
|
+
existsFn = existsSync5,
|
|
4361
|
+
joinFn = (dir) => `${dir}/scripts/ci/check-local-pr-overlap.mjs`
|
|
4362
|
+
} = {}) {
|
|
4363
|
+
if (existsFn(trustedPath)) return { scriptPath: trustedPath, trusted: true };
|
|
4364
|
+
return { scriptPath: joinFn(worktreeDir), trusted: false };
|
|
4365
|
+
}
|
|
4366
|
+
var TRUSTED_OVERLAP_SCRIPT, CREDENTIAL_ENV_KEYS;
|
|
4293
4367
|
var init_pr_overlap_gate = __esm({
|
|
4294
4368
|
"../../scripts/virtual-office/code-runner/pr-overlap-gate.mjs"() {
|
|
4295
4369
|
"use strict";
|
|
4370
|
+
TRUSTED_OVERLAP_SCRIPT = fileURLToPath(
|
|
4371
|
+
new URL("../../ci/check-local-pr-overlap.mjs", import.meta.url)
|
|
4372
|
+
);
|
|
4373
|
+
CREDENTIAL_ENV_KEYS = Object.freeze([
|
|
4374
|
+
"GH_TOKEN",
|
|
4375
|
+
"GITHUB_TOKEN",
|
|
4376
|
+
"VO_CONTROL_PLANE_ADMIN_TOKEN",
|
|
4377
|
+
"VO_CONTROL_PLANE_TOKEN",
|
|
4378
|
+
"GITHUB_APP_PRIVATE_KEY",
|
|
4379
|
+
"ANTHROPIC_API_KEY",
|
|
4380
|
+
"OPENAI_API_KEY",
|
|
4381
|
+
"CURSOR_API_KEY"
|
|
4382
|
+
]);
|
|
4296
4383
|
}
|
|
4297
4384
|
});
|
|
4298
4385
|
|
|
@@ -4336,14 +4423,14 @@ function parsePorcelainZ(out) {
|
|
|
4336
4423
|
for (let i = 0; i < tokens.length; i += 1) {
|
|
4337
4424
|
const tok = tokens[i];
|
|
4338
4425
|
if (!tok) continue;
|
|
4339
|
-
const
|
|
4340
|
-
if (
|
|
4426
|
+
const path16 = tok.slice(3);
|
|
4427
|
+
if (path16) files.push(path16);
|
|
4341
4428
|
if (tok[0] === "R" || tok[0] === "C") i += 1;
|
|
4342
4429
|
}
|
|
4343
4430
|
return files;
|
|
4344
4431
|
}
|
|
4345
|
-
function isAgentScratch(
|
|
4346
|
-
const p = String(
|
|
4432
|
+
function isAgentScratch(path16) {
|
|
4433
|
+
const p = String(path16 || "");
|
|
4347
4434
|
return SCRATCH_PATTERNS.some((re) => re.test(p));
|
|
4348
4435
|
}
|
|
4349
4436
|
function isMaxTurnsResult(summary) {
|
|
@@ -4550,7 +4637,6 @@ var init_partial_pr_continuation = __esm({
|
|
|
4550
4637
|
});
|
|
4551
4638
|
|
|
4552
4639
|
// ../../scripts/virtual-office/code-runner/publish-async.mjs
|
|
4553
|
-
import path12 from "node:path";
|
|
4554
4640
|
function compactTitle(value, max = 100) {
|
|
4555
4641
|
return String(value || "").replace(/\s+/g, " ").trim().slice(0, max) || "code-task";
|
|
4556
4642
|
}
|
|
@@ -4560,7 +4646,7 @@ function gitRetryLog(op) {
|
|
|
4560
4646
|
console.error(`[publish] transient ${op} failure (attempt ${attempt}): ${why} \u2014 retrying in ${Math.round(delayMs / 1e3)}s`);
|
|
4561
4647
|
};
|
|
4562
4648
|
}
|
|
4563
|
-
async function retryTransientAsync(fn, { attempts = 3, baseMs = 5e3, capMs = 3e4, rng =
|
|
4649
|
+
async function retryTransientAsync(fn, { attempts = 3, baseMs = 5e3, capMs = 3e4, rng = secureUnitRandom, onRetry } = {}) {
|
|
4564
4650
|
let lastErr;
|
|
4565
4651
|
for (let i = 0; i < attempts; i += 1) {
|
|
4566
4652
|
try {
|
|
@@ -4591,8 +4677,12 @@ async function resolveOrCreateBranchAsync(worktreeDir, branchPrefix, runCommand
|
|
|
4591
4677
|
}
|
|
4592
4678
|
return branch;
|
|
4593
4679
|
}
|
|
4594
|
-
async function runLocalPrOverlapGateAsync(worktreeDir, files, { branch = "", env: env2 = process.env, excludePrNumber = null } = {}) {
|
|
4595
|
-
const scriptPath =
|
|
4680
|
+
async function runLocalPrOverlapGateAsync(worktreeDir, files, { branch = "", env: env2 = process.env, excludePrNumber = null, log: log3 = (m) => console.warn(`[pr-overlap-gate] ${m}`) } = {}) {
|
|
4681
|
+
const { scriptPath, trusted } = resolveOverlapScript({ worktreeDir });
|
|
4682
|
+
const childEnv = trusted ? env2 : stripCredentials(env2);
|
|
4683
|
+
if (!trusted) {
|
|
4684
|
+
log3(`WARNING: trusted overlap script not found; running worktree copy ${scriptPath} with credentials stripped.`);
|
|
4685
|
+
}
|
|
4596
4686
|
try {
|
|
4597
4687
|
const output = await runProcess2("node", [
|
|
4598
4688
|
scriptPath,
|
|
@@ -4601,7 +4691,7 @@ async function runLocalPrOverlapGateAsync(worktreeDir, files, { branch = "", env
|
|
|
4601
4691
|
...excludePrNumber ? ["--exclude-pr", String(excludePrNumber)] : []
|
|
4602
4692
|
], {
|
|
4603
4693
|
cwd: worktreeDir,
|
|
4604
|
-
env:
|
|
4694
|
+
env: childEnv,
|
|
4605
4695
|
input: JSON.stringify([...new Set((files || []).map((file) => String(file || "").trim()).filter(Boolean))]),
|
|
4606
4696
|
timeout: 12e4
|
|
4607
4697
|
});
|
|
@@ -4883,6 +4973,8 @@ var init_publish_async = __esm({
|
|
|
4883
4973
|
init_auto_merge();
|
|
4884
4974
|
init_git_resilience();
|
|
4885
4975
|
init_process_runner2();
|
|
4976
|
+
init_secure_random();
|
|
4977
|
+
init_pr_overlap_gate();
|
|
4886
4978
|
init_existing_pr_publication();
|
|
4887
4979
|
init_partial_pr_continuation();
|
|
4888
4980
|
sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
@@ -4899,7 +4991,7 @@ function defaultRunCommand2(cmd, args, cwd, opts = {}) {
|
|
|
4899
4991
|
function buildResumeLocalBranchName(remoteBranch, {
|
|
4900
4992
|
now = () => /* @__PURE__ */ new Date(),
|
|
4901
4993
|
pid = process.pid,
|
|
4902
|
-
random =
|
|
4994
|
+
random = secureUnitRandom
|
|
4903
4995
|
} = {}) {
|
|
4904
4996
|
const stamp = now().toISOString().replace(/[:.]/g, "-");
|
|
4905
4997
|
const unique = `${pid}-${random().toString(36).slice(2, 8)}`;
|
|
@@ -4991,13 +5083,14 @@ var init_resume_branch = __esm({
|
|
|
4991
5083
|
"use strict";
|
|
4992
5084
|
init_publish();
|
|
4993
5085
|
init_process_runner2();
|
|
5086
|
+
init_secure_random();
|
|
4994
5087
|
}
|
|
4995
5088
|
});
|
|
4996
5089
|
|
|
4997
5090
|
// ../../scripts/virtual-office/code-runner/skill-catalog.mjs
|
|
4998
5091
|
import { readdirSync as readdirSync2, readFileSync as readFileSync3, statSync } from "node:fs";
|
|
4999
5092
|
import { dirname as dirname3, join as join3 } from "node:path";
|
|
5000
|
-
import { fileURLToPath } from "node:url";
|
|
5093
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
5001
5094
|
function parseFrontmatterNameDescription(raw) {
|
|
5002
5095
|
const text = String(raw).replace(/\r\n/g, "\n");
|
|
5003
5096
|
if (!text.startsWith("---\n")) return null;
|
|
@@ -5016,7 +5109,7 @@ function parseFrontmatterNameDescription(raw) {
|
|
|
5016
5109
|
return name && description ? { name, description } : null;
|
|
5017
5110
|
}
|
|
5018
5111
|
function resolveDefaultRepoRoot() {
|
|
5019
|
-
const starts = [dirname3(
|
|
5112
|
+
const starts = [dirname3(fileURLToPath2(import.meta.url)), process.cwd()];
|
|
5020
5113
|
for (const start of starts) {
|
|
5021
5114
|
let dir = start;
|
|
5022
5115
|
for (let i = 0; i < 8; i += 1) {
|
|
@@ -5271,7 +5364,7 @@ var init_task_prompt = __esm({
|
|
|
5271
5364
|
import { createHash as createHash3, randomUUID } from "node:crypto";
|
|
5272
5365
|
import { chmod, mkdir, mkdtemp, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
5273
5366
|
import os2 from "node:os";
|
|
5274
|
-
import
|
|
5367
|
+
import path11 from "node:path";
|
|
5275
5368
|
function safeTaskToken(taskId) {
|
|
5276
5369
|
return String(taskId || "task").replace(/[^0-9A-Za-z_-]/gu, "_").slice(0, 48) || "task";
|
|
5277
5370
|
}
|
|
@@ -5281,25 +5374,25 @@ function sanitizeTaskAttachmentName(name, index = 0) {
|
|
|
5281
5374
|
return `${String(index + 1).padStart(2, "0")}-${normalized}`;
|
|
5282
5375
|
}
|
|
5283
5376
|
function assertGeneratedDirectory(directory, tempRoot) {
|
|
5284
|
-
const resolvedDirectory =
|
|
5285
|
-
const resolvedRoot =
|
|
5286
|
-
if (
|
|
5377
|
+
const resolvedDirectory = path11.resolve(directory);
|
|
5378
|
+
const resolvedRoot = path11.resolve(tempRoot);
|
|
5379
|
+
if (path11.dirname(resolvedDirectory) !== resolvedRoot || !path11.basename(resolvedDirectory).startsWith(DIRECTORY_PREFIX)) {
|
|
5287
5380
|
throw new Error("refusing to clean an unverified task-attachment directory");
|
|
5288
5381
|
}
|
|
5289
5382
|
return resolvedDirectory;
|
|
5290
5383
|
}
|
|
5291
5384
|
async function createAttachmentDirectory(taskId, tempRoot) {
|
|
5292
|
-
const root =
|
|
5385
|
+
const root = path11.resolve(tempRoot);
|
|
5293
5386
|
await mkdir(root, { recursive: true });
|
|
5294
|
-
const directory = await mkdtemp(
|
|
5295
|
-
const marker = JSON.stringify({ owner: MARKER_OWNER, token: randomUUID(), directory:
|
|
5296
|
-
await writeFile(
|
|
5387
|
+
const directory = await mkdtemp(path11.join(root, `${DIRECTORY_PREFIX}${safeTaskToken(taskId)}-`));
|
|
5388
|
+
const marker = JSON.stringify({ owner: MARKER_OWNER, token: randomUUID(), directory: path11.basename(directory), created_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
5389
|
+
await writeFile(path11.join(directory, MARKER_FILE), marker, { encoding: "utf8", mode: 384 });
|
|
5297
5390
|
return { directory, marker, tempRoot: root };
|
|
5298
5391
|
}
|
|
5299
5392
|
async function cleanupGeneratedDirectory(state) {
|
|
5300
5393
|
if (!state || state.cleaned) return;
|
|
5301
5394
|
const directory = assertGeneratedDirectory(state.directory, state.tempRoot);
|
|
5302
|
-
const marker = await readFile(
|
|
5395
|
+
const marker = await readFile(path11.join(directory, MARKER_FILE), "utf8").catch(() => "");
|
|
5303
5396
|
if (marker !== state.marker) throw new Error("refusing to clean a task-attachment directory without its exact marker");
|
|
5304
5397
|
await rm(directory, { recursive: true, force: true });
|
|
5305
5398
|
state.cleaned = true;
|
|
@@ -5318,7 +5411,7 @@ async function sweepStaleTaskAttachmentDirectories({
|
|
|
5318
5411
|
now = Date.now(),
|
|
5319
5412
|
maxAgeMs = DEFAULT_STALE_AGE_MS
|
|
5320
5413
|
} = {}) {
|
|
5321
|
-
const root =
|
|
5414
|
+
const root = path11.resolve(tempRoot);
|
|
5322
5415
|
if (!Number.isFinite(maxAgeMs) || maxAgeMs <= 0) throw new Error("stale attachment age must be positive");
|
|
5323
5416
|
const entries = await readdir(root, { withFileTypes: true }).catch((error) => {
|
|
5324
5417
|
if (error?.code === "ENOENT") return [];
|
|
@@ -5327,8 +5420,8 @@ async function sweepStaleTaskAttachmentDirectories({
|
|
|
5327
5420
|
let removed = 0;
|
|
5328
5421
|
for (const entry of entries) {
|
|
5329
5422
|
if (!entry.isDirectory() || !entry.name.startsWith(DIRECTORY_PREFIX)) continue;
|
|
5330
|
-
const directory = assertGeneratedDirectory(
|
|
5331
|
-
const markerRaw = await readFile(
|
|
5423
|
+
const directory = assertGeneratedDirectory(path11.join(root, entry.name), root);
|
|
5424
|
+
const markerRaw = await readFile(path11.join(directory, MARKER_FILE), "utf8").catch(() => "");
|
|
5332
5425
|
const marker = parseOwnedMarker(markerRaw, entry.name);
|
|
5333
5426
|
if (!marker) continue;
|
|
5334
5427
|
const directoryStat = await stat(directory);
|
|
@@ -5371,10 +5464,10 @@ async function materializeTaskAttachments(client, task, { tempRoot = os2.tmpdir(
|
|
|
5371
5464
|
const sha256 = createHash3("sha256").update(content).digest("hex");
|
|
5372
5465
|
if (sha256 !== ref.sha256) throw new Error(`attachment ${ref.attachment_id} sha256 mismatch`);
|
|
5373
5466
|
const name = sanitizeTaskAttachmentName(ref.name, index);
|
|
5374
|
-
const filePath =
|
|
5467
|
+
const filePath = path11.join(state.directory, name);
|
|
5375
5468
|
await writeFile(filePath, content, { flag: "wx", mode: 384 });
|
|
5376
5469
|
await chmod(filePath, 384);
|
|
5377
|
-
files.push({ attachmentId: ref.attachment_id, name, mime: ref.mime, sizeBytes: ref.size_bytes, sha256, path:
|
|
5470
|
+
files.push({ attachmentId: ref.attachment_id, name, mime: ref.mime, sizeBytes: ref.size_bytes, sha256, path: path11.resolve(filePath) });
|
|
5378
5471
|
}
|
|
5379
5472
|
return { directory: state.directory, files, manifestMarkdown: buildManifest(files), cleanup: () => cleanupGeneratedDirectory(state) };
|
|
5380
5473
|
} catch (error) {
|
|
@@ -5438,9 +5531,9 @@ async function readSpool(spoolDir = SPOOL_DIR) {
|
|
|
5438
5531
|
}
|
|
5439
5532
|
return out;
|
|
5440
5533
|
}
|
|
5441
|
-
async function readCloudMap(
|
|
5534
|
+
async function readCloudMap(path16) {
|
|
5442
5535
|
try {
|
|
5443
|
-
return JSON.parse(await readFile2(
|
|
5536
|
+
return JSON.parse(await readFile2(path16, "utf8"));
|
|
5444
5537
|
} catch {
|
|
5445
5538
|
return {};
|
|
5446
5539
|
}
|
|
@@ -5583,13 +5676,13 @@ var init_rate_limit_resume_scheduler_core = __esm({
|
|
|
5583
5676
|
});
|
|
5584
5677
|
|
|
5585
5678
|
// ../../scripts/virtual-office/code-runner/rate-limit-resume-scheduler.mjs
|
|
5586
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as
|
|
5679
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "node:fs";
|
|
5587
5680
|
import { dirname as dirname4, join as join5, resolve } from "node:path";
|
|
5588
5681
|
function log(msg) {
|
|
5589
5682
|
console.log(`[rate-limit-scheduler ${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}`);
|
|
5590
5683
|
}
|
|
5591
5684
|
function readQueue(queuePath) {
|
|
5592
|
-
if (!
|
|
5685
|
+
if (!existsSync6(queuePath)) return [];
|
|
5593
5686
|
const content = readFileSync4(queuePath, "utf-8");
|
|
5594
5687
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
5595
5688
|
const entries = [];
|
|
@@ -5612,7 +5705,7 @@ function attemptsStorePath() {
|
|
|
5612
5705
|
}
|
|
5613
5706
|
function readAttemptsStore() {
|
|
5614
5707
|
const p = attemptsStorePath();
|
|
5615
|
-
if (!
|
|
5708
|
+
if (!existsSync6(p)) return {};
|
|
5616
5709
|
try {
|
|
5617
5710
|
const parsed = JSON.parse(readFileSync4(p, "utf-8"));
|
|
5618
5711
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
@@ -6008,7 +6101,7 @@ var init_agent_availability = __esm({
|
|
|
6008
6101
|
import { spawn as spawn4 } from "node:child_process";
|
|
6009
6102
|
import fs6 from "node:fs";
|
|
6010
6103
|
import os3 from "node:os";
|
|
6011
|
-
import
|
|
6104
|
+
import path12 from "node:path";
|
|
6012
6105
|
function readClaudeUsage({ homeDir = os3.homedir(), read: rawRead = readJson } = {}) {
|
|
6013
6106
|
const read = (p) => {
|
|
6014
6107
|
try {
|
|
@@ -6017,7 +6110,7 @@ function readClaudeUsage({ homeDir = os3.homedir(), read: rawRead = readJson } =
|
|
|
6017
6110
|
return null;
|
|
6018
6111
|
}
|
|
6019
6112
|
};
|
|
6020
|
-
const status = read(
|
|
6113
|
+
const status = read(path12.join(homeDir, ".claude", "claude-usage.json"));
|
|
6021
6114
|
if (status && (status.seven_day || status.five_hour)) {
|
|
6022
6115
|
const entry = {
|
|
6023
6116
|
agent: "claude",
|
|
@@ -6026,7 +6119,7 @@ function readClaudeUsage({ homeDir = os3.homedir(), read: rawRead = readJson } =
|
|
|
6026
6119
|
};
|
|
6027
6120
|
if (entry.seven_day_used_pct !== null || entry.five_hour_used_pct !== null) return entry;
|
|
6028
6121
|
}
|
|
6029
|
-
const weekly = read(
|
|
6122
|
+
const weekly = read(path12.join(homeDir, ".claude", "claude-weekly-usage.json"));
|
|
6030
6123
|
if (weekly) {
|
|
6031
6124
|
const entry = {
|
|
6032
6125
|
agent: "claude",
|
|
@@ -6764,9 +6857,9 @@ function buildControlHandler({ getStatus, requestStop, allowedOrigin }) {
|
|
|
6764
6857
|
res.end();
|
|
6765
6858
|
return;
|
|
6766
6859
|
}
|
|
6767
|
-
const
|
|
6860
|
+
const path16 = String(req.url || "").split("?")[0];
|
|
6768
6861
|
res.setHeader("content-type", "application/json");
|
|
6769
|
-
if (req.method === "GET" &&
|
|
6862
|
+
if (req.method === "GET" && path16 === "/status") {
|
|
6770
6863
|
let status;
|
|
6771
6864
|
try {
|
|
6772
6865
|
status = getStatus();
|
|
@@ -6777,7 +6870,7 @@ function buildControlHandler({ getStatus, requestStop, allowedOrigin }) {
|
|
|
6777
6870
|
res.end(JSON.stringify({ ok: true, ...status }));
|
|
6778
6871
|
return;
|
|
6779
6872
|
}
|
|
6780
|
-
if (req.method === "POST" &&
|
|
6873
|
+
if (req.method === "POST" && path16 === "/stop") {
|
|
6781
6874
|
if (!isControlOriginAllowed(req.headers.origin, allowedOrigin) || !req.headers["x-vo-control"]) {
|
|
6782
6875
|
res.statusCode = 403;
|
|
6783
6876
|
res.end(JSON.stringify({ ok: false, error: "forbidden" }));
|
|
@@ -6936,8 +7029,8 @@ var init_effort_mode_config = __esm({
|
|
|
6936
7029
|
|
|
6937
7030
|
// ../../scripts/virtual-office/model-registry.mjs
|
|
6938
7031
|
import fs7 from "node:fs";
|
|
6939
|
-
import
|
|
6940
|
-
import { fileURLToPath as
|
|
7032
|
+
import path13 from "node:path";
|
|
7033
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
6941
7034
|
function uniqueModels(models = []) {
|
|
6942
7035
|
return [...new Set(models.map((model) => String(model || "").trim()).filter(Boolean))];
|
|
6943
7036
|
}
|
|
@@ -7059,7 +7152,7 @@ function readCache(cacheFile = DEFAULT_CACHE_FILE, nowMs = Date.now(), ttlMs = D
|
|
|
7059
7152
|
}
|
|
7060
7153
|
}
|
|
7061
7154
|
function writeCache(cacheFile = DEFAULT_CACHE_FILE, payload) {
|
|
7062
|
-
fs7.mkdirSync(
|
|
7155
|
+
fs7.mkdirSync(path13.dirname(cacheFile), { recursive: true });
|
|
7063
7156
|
fs7.writeFileSync(cacheFile, JSON.stringify(payload, null, 2));
|
|
7064
7157
|
}
|
|
7065
7158
|
async function fetchRegistryCatalog({
|
|
@@ -7117,10 +7210,10 @@ var __dirname, ROOT, DEFAULT_CACHE_DIR, DEFAULT_CACHE_FILE, DEFAULT_TTL_MS3, ANT
|
|
|
7117
7210
|
var init_model_registry = __esm({
|
|
7118
7211
|
"../../scripts/virtual-office/model-registry.mjs"() {
|
|
7119
7212
|
"use strict";
|
|
7120
|
-
__dirname =
|
|
7121
|
-
ROOT =
|
|
7122
|
-
DEFAULT_CACHE_DIR =
|
|
7123
|
-
DEFAULT_CACHE_FILE =
|
|
7213
|
+
__dirname = path13.dirname(fileURLToPath3(import.meta.url));
|
|
7214
|
+
ROOT = path13.resolve(__dirname, "..", "..");
|
|
7215
|
+
DEFAULT_CACHE_DIR = path13.join(ROOT, ".virtual-office-cache", "model-registry");
|
|
7216
|
+
DEFAULT_CACHE_FILE = path13.join(DEFAULT_CACHE_DIR, "catalog.json");
|
|
7124
7217
|
DEFAULT_TTL_MS3 = 60 * 60 * 1e3;
|
|
7125
7218
|
ANTHROPIC_API_VERSION = "2023-06-01";
|
|
7126
7219
|
FAMILY_DEFINITIONS = {
|
|
@@ -7321,10 +7414,18 @@ var init_model_router = __esm({
|
|
|
7321
7414
|
}
|
|
7322
7415
|
};
|
|
7323
7416
|
AGENT_MODEL_COMPATIBILITY = {
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7417
|
+
// SECURITY: these are ANCHORED AT BOTH ENDS on purpose. The old patterns were
|
|
7418
|
+
// prefix-only, so `gpt-5 & <cmd>` and `claude-3 & <cmd>` passed the gate with
|
|
7419
|
+
// the payload still attached and landed in the agent's argv.
|
|
7420
|
+
claude: (model) => /^claude-[A-Za-z0-9._:@\[\]-]{0,79}$/i.test(String(model || "")),
|
|
7421
|
+
codex: (model) => /^(?:gpt-|o\d|codex)[A-Za-z0-9._:@\[\]-]{0,79}$/i.test(String(model || "")),
|
|
7422
|
+
// Defense in depth: `task.model` is control-plane-controlled and lands in the
|
|
7423
|
+
// cursor-agent argv. `() => true` accepted ANY string, including cmd
|
|
7424
|
+
// metacharacters — which was the second half of the shell:true RCE in
|
|
7425
|
+
// cursor-runner. Restrict to the shape a model id actually has so a payload
|
|
7426
|
+
// like `x & powershell -enc ...` is rejected before it reaches a spawn.
|
|
7427
|
+
cursor: (model) => /^[A-Za-z0-9][A-Za-z0-9._:@\[\]-]{0,79}$/.test(String(model || "")),
|
|
7428
|
+
meta: (model) => /^muse-spark-[A-Za-z0-9._:@\[\]-]{0,79}$/i.test(String(model || ""))
|
|
7328
7429
|
};
|
|
7329
7430
|
}
|
|
7330
7431
|
});
|
|
@@ -7724,9 +7825,9 @@ function operatorTierToRung(tier, difficulty, thresholds) {
|
|
|
7724
7825
|
if (tier === "best" && difficulty >= thresholds.rungBounds.R5) return "R5";
|
|
7725
7826
|
return base;
|
|
7726
7827
|
}
|
|
7727
|
-
function readCodexModelsCache({ path:
|
|
7828
|
+
function readCodexModelsCache({ path: path16 = DEFAULT_CODEX_MODELS_CACHE, read = readFileSync5 } = {}) {
|
|
7728
7829
|
try {
|
|
7729
|
-
const parsed = JSON.parse(read(
|
|
7830
|
+
const parsed = JSON.parse(read(path16, "utf8"));
|
|
7730
7831
|
return Array.isArray(parsed?.models) ? parsed : null;
|
|
7731
7832
|
} catch {
|
|
7732
7833
|
return null;
|
|
@@ -7909,14 +8010,14 @@ var init_role_cost_shadow = __esm({
|
|
|
7909
8010
|
import { readFileSync as readFileSync6, appendFileSync as appendFileSync2, mkdirSync as mkdirSync5 } from "node:fs";
|
|
7910
8011
|
import { homedir as homedir6 } from "node:os";
|
|
7911
8012
|
import { join as join8, dirname as dirname5 } from "node:path";
|
|
7912
|
-
import { fileURLToPath as
|
|
8013
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
7913
8014
|
function getAutoRouterMode(env2 = process.env) {
|
|
7914
8015
|
const raw = String(env2.VO_CODE_RUNNER_AUTO_ROUTER || "").trim().toLowerCase();
|
|
7915
8016
|
return MODES.has(raw) ? raw : "off";
|
|
7916
8017
|
}
|
|
7917
8018
|
function loadThresholds() {
|
|
7918
8019
|
if (!cachedThresholds) {
|
|
7919
|
-
const here = dirname5(
|
|
8020
|
+
const here = dirname5(fileURLToPath4(import.meta.url));
|
|
7920
8021
|
cachedThresholds = JSON.parse(readFileSync6(join8(here, "thresholds.json"), "utf8"));
|
|
7921
8022
|
}
|
|
7922
8023
|
return cachedThresholds;
|
|
@@ -7983,15 +8084,15 @@ function formatDecisionReason(decision, maxLen = 480) {
|
|
|
7983
8084
|
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("; ")}`;
|
|
7984
8085
|
return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
|
|
7985
8086
|
}
|
|
7986
|
-
function appendDecisionFallback(decision, { path:
|
|
8087
|
+
function appendDecisionFallback(decision, { path: path16 = DECISION_FALLBACK_PATH, append = appendFileSync2, mkdir: mkdir3 = mkdirSync5, task, thresholds, roleCostInputs } = {}) {
|
|
7987
8088
|
try {
|
|
7988
|
-
mkdir3(dirname5(
|
|
7989
|
-
append(
|
|
8089
|
+
mkdir3(dirname5(path16), { recursive: true });
|
|
8090
|
+
append(path16, `${JSON.stringify(decision)}
|
|
7990
8091
|
`, "utf8");
|
|
7991
8092
|
if (isRouterDecision(decision)) {
|
|
7992
8093
|
try {
|
|
7993
8094
|
const records = buildShadowRecords({ decision, task, thresholds: thresholds || loadThresholds(), roleCostInputs });
|
|
7994
|
-
for (const record of records) append(
|
|
8095
|
+
for (const record of records) append(path16, `${JSON.stringify(record)}
|
|
7995
8096
|
`, "utf8");
|
|
7996
8097
|
} catch {
|
|
7997
8098
|
}
|
|
@@ -8148,7 +8249,7 @@ function makeReconnectBackoff({
|
|
|
8148
8249
|
jitter = 0.2,
|
|
8149
8250
|
log: log3 = () => {
|
|
8150
8251
|
},
|
|
8151
|
-
random =
|
|
8252
|
+
random = secureUnitRandom
|
|
8152
8253
|
} = {}) {
|
|
8153
8254
|
let consecutiveFailures = 0;
|
|
8154
8255
|
return {
|
|
@@ -8208,6 +8309,7 @@ function installProcessSafetyNet({ log: log3 = () => {
|
|
|
8208
8309
|
var init_reconnect_backoff = __esm({
|
|
8209
8310
|
"../../scripts/virtual-office/code-runner/reconnect-backoff.mjs"() {
|
|
8210
8311
|
"use strict";
|
|
8312
|
+
init_secure_random();
|
|
8211
8313
|
}
|
|
8212
8314
|
});
|
|
8213
8315
|
|
|
@@ -8322,7 +8424,7 @@ var init_agent_process_env = __esm({
|
|
|
8322
8424
|
// ../../scripts/virtual-office/code-runner/isolation-audit.mjs
|
|
8323
8425
|
import fs8 from "node:fs";
|
|
8324
8426
|
import fsp9 from "node:fs/promises";
|
|
8325
|
-
import
|
|
8427
|
+
import path14 from "node:path";
|
|
8326
8428
|
async function defaultRun(command, args, cwd, options = {}) {
|
|
8327
8429
|
return runProcess2(command, args, { cwd, timeout: 6e4, ...options });
|
|
8328
8430
|
}
|
|
@@ -8335,7 +8437,7 @@ async function canonicalRootForWorktree(worktreeDir, run) {
|
|
|
8335
8437
|
"--path-format=absolute",
|
|
8336
8438
|
"--git-common-dir"
|
|
8337
8439
|
])).trim();
|
|
8338
|
-
const root =
|
|
8440
|
+
const root = path14.dirname(commonDir);
|
|
8339
8441
|
return samePath2(root, worktreeDir) ? null : root;
|
|
8340
8442
|
}
|
|
8341
8443
|
async function snapshot(root, run) {
|
|
@@ -8377,21 +8479,21 @@ async function changedPaths(root, run) {
|
|
|
8377
8479
|
}
|
|
8378
8480
|
async function quarantineCanonicalWrites({ baseline, worktreeDir, taskId, run, now }) {
|
|
8379
8481
|
const paths = await changedPaths(baseline.root, run);
|
|
8380
|
-
const quarantineDir =
|
|
8381
|
-
|
|
8482
|
+
const quarantineDir = path14.join(
|
|
8483
|
+
path14.dirname(worktreeDir),
|
|
8382
8484
|
".canonical-recovery",
|
|
8383
8485
|
`${String(taskId || "unknown").replace(/[^a-z0-9-]/gi, "-")}-${now().toISOString().replace(/[:.]/g, "-")}`
|
|
8384
8486
|
);
|
|
8385
8487
|
await fsp9.mkdir(quarantineDir, { recursive: true });
|
|
8386
8488
|
const patch = await git2(run, baseline.root, ["diff", "--binary", "HEAD"], { raw: true });
|
|
8387
|
-
await fsp9.writeFile(
|
|
8489
|
+
await fsp9.writeFile(path14.join(quarantineDir, "tracked.patch"), patch, "utf8");
|
|
8388
8490
|
for (const relative of paths.untracked) {
|
|
8389
|
-
const source =
|
|
8390
|
-
const target =
|
|
8391
|
-
await fsp9.mkdir(
|
|
8491
|
+
const source = path14.join(baseline.root, relative);
|
|
8492
|
+
const target = path14.join(quarantineDir, "untracked", relative);
|
|
8493
|
+
await fsp9.mkdir(path14.dirname(target), { recursive: true });
|
|
8392
8494
|
await fsp9.copyFile(source, target);
|
|
8393
8495
|
}
|
|
8394
|
-
await fsp9.writeFile(
|
|
8496
|
+
await fsp9.writeFile(path14.join(quarantineDir, "manifest.json"), `${JSON.stringify({
|
|
8395
8497
|
taskId,
|
|
8396
8498
|
canonicalRoot: baseline.root,
|
|
8397
8499
|
canonicalHead: baseline.head,
|
|
@@ -8413,8 +8515,8 @@ async function restoreExactCanonicalPaths(baseline, evidence, run) {
|
|
|
8413
8515
|
]);
|
|
8414
8516
|
}
|
|
8415
8517
|
for (const relative of evidence.untracked) {
|
|
8416
|
-
const target =
|
|
8417
|
-
const prefix = `${
|
|
8518
|
+
const target = path14.resolve(baseline.root, relative);
|
|
8519
|
+
const prefix = `${path14.resolve(baseline.root)}${path14.sep}`;
|
|
8418
8520
|
if (!target.startsWith(prefix) || !fs8.existsSync(target)) continue;
|
|
8419
8521
|
await fsp9.rm(target, { force: true });
|
|
8420
8522
|
}
|
|
@@ -8451,7 +8553,7 @@ var init_isolation_audit = __esm({
|
|
|
8451
8553
|
init_process_runner2();
|
|
8452
8554
|
splitZ2 = (value) => String(value || "").split("\0").map((item) => item.trim()).filter(Boolean);
|
|
8453
8555
|
samePath2 = (left, right) => {
|
|
8454
|
-
const [a, b] = [left, right].map((value) =>
|
|
8556
|
+
const [a, b] = [left, right].map((value) => path14.resolve(value));
|
|
8455
8557
|
return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
8456
8558
|
};
|
|
8457
8559
|
}
|
|
@@ -8460,7 +8562,7 @@ var init_isolation_audit = __esm({
|
|
|
8460
8562
|
// ../../scripts/virtual-office/code-runner/recovery-ledger.mjs
|
|
8461
8563
|
import fs9 from "node:fs";
|
|
8462
8564
|
import fsp10 from "node:fs/promises";
|
|
8463
|
-
import
|
|
8565
|
+
import path15 from "node:path";
|
|
8464
8566
|
function recoveryTaskId(prompt) {
|
|
8465
8567
|
const match = String(prompt || "").match(/VO_RECOVERY_FROM_CODE_TASK:\s*([0-9a-f-]{36})/i);
|
|
8466
8568
|
return match ? match[1].toLowerCase() : null;
|
|
@@ -8474,10 +8576,10 @@ function cloneLeaf(repo) {
|
|
|
8474
8576
|
function recoveryLedgerCandidates(repo, clonesRoot2) {
|
|
8475
8577
|
const leaf = cloneLeaf(repo);
|
|
8476
8578
|
if (!leaf || !clonesRoot2) return [];
|
|
8477
|
-
const canonical =
|
|
8579
|
+
const canonical = path15.join(clonesRoot2, leaf);
|
|
8478
8580
|
return [
|
|
8479
|
-
|
|
8480
|
-
|
|
8581
|
+
path15.join(clonesRoot2, ".agent-worktrees", leaf, "recovery-ledger.jsonl"),
|
|
8582
|
+
path15.join(canonical, ".agent-worktrees", "recovery-ledger.jsonl")
|
|
8481
8583
|
];
|
|
8482
8584
|
}
|
|
8483
8585
|
async function readLedger(file, readFile4) {
|
|
@@ -8599,6 +8701,9 @@ var init_recovery_ledger = __esm({
|
|
|
8599
8701
|
});
|
|
8600
8702
|
|
|
8601
8703
|
// ../../scripts/virtual-office/code-runner/no-changes-terminal-status.mjs
|
|
8704
|
+
function defaultRunCommand3(cmd, args, cwd, opts = {}) {
|
|
8705
|
+
return runProcess2(cmd, args, { cwd, ...opts });
|
|
8706
|
+
}
|
|
8602
8707
|
function explicitTaskOutcome(summary) {
|
|
8603
8708
|
const matches = [...String(summary || "").matchAll(/ALGOSUITE_TASK_OUTCOME\s*:\s*(NO_CHANGES|BLOCKED|FAILED)\b/gi)];
|
|
8604
8709
|
return matches.at(-1)?.[1]?.toUpperCase() || "";
|
|
@@ -8636,16 +8741,78 @@ function decideNoChangesTerminalStatus({ partial, run = {}, maxTurns } = {}) {
|
|
|
8636
8741
|
result: "inconclusive_max_turns"
|
|
8637
8742
|
};
|
|
8638
8743
|
}
|
|
8744
|
+
const cause = String(run.summary || "").trim();
|
|
8639
8745
|
return {
|
|
8640
8746
|
status: "failed",
|
|
8641
|
-
message: "agent made no file changes",
|
|
8642
|
-
result: "no_changes"
|
|
8747
|
+
message: cause ? `agent made no file changes \u2014 ${cause.slice(0, 200)}` : "agent made no file changes",
|
|
8748
|
+
result: (cause || "no_changes").slice(0, RESULT_LIMIT)
|
|
8643
8749
|
};
|
|
8644
8750
|
}
|
|
8751
|
+
async function closeSupersededSourceOnNoChanges({
|
|
8752
|
+
task,
|
|
8753
|
+
run,
|
|
8754
|
+
worktreeDir,
|
|
8755
|
+
githubToken,
|
|
8756
|
+
log: log3 = () => {
|
|
8757
|
+
},
|
|
8758
|
+
runCommand = defaultRunCommand3
|
|
8759
|
+
} = {}) {
|
|
8760
|
+
const prNumber = supersededSourcePrNumber(task?.prompt);
|
|
8761
|
+
if (!Number.isInteger(prNumber) || prNumber <= 0) return false;
|
|
8762
|
+
const env2 = githubToken ? installationTokenEnv(githubToken) : void 0;
|
|
8763
|
+
try {
|
|
8764
|
+
const raw = await runCommand("gh", ["pr", "view", String(prNumber), "--json", "state"], worktreeDir, { env: env2, timeout: 6e4 });
|
|
8765
|
+
if (JSON.parse(raw || "{}")?.state !== "OPEN") return false;
|
|
8766
|
+
const evidence = String(run?.summary || "verified: no re-implementation needed").replace(/\s+/g, " ").slice(0, 600);
|
|
8767
|
+
await runCommand(
|
|
8768
|
+
"gh",
|
|
8769
|
+
["pr", "close", String(prNumber), "--comment", `Closing: AlgoHQ repair verified this PR's intent is already satisfied on current main \u2014 no re-implementation needed. Evidence: ${evidence}`],
|
|
8770
|
+
worktreeDir,
|
|
8771
|
+
{ env: env2, timeout: 6e4 }
|
|
8772
|
+
);
|
|
8773
|
+
log3(`no-changes: closed superseded source PR #${prNumber} (intent already on main)`);
|
|
8774
|
+
return true;
|
|
8775
|
+
} catch (err) {
|
|
8776
|
+
log3(`no-changes: close of superseded source PR #${prNumber} failed (left open): ${String(err?.message || err).slice(0, 200)}`);
|
|
8777
|
+
return false;
|
|
8778
|
+
}
|
|
8779
|
+
}
|
|
8780
|
+
async function finalizeNoChangesOutcome({
|
|
8781
|
+
client,
|
|
8782
|
+
id,
|
|
8783
|
+
task,
|
|
8784
|
+
partial,
|
|
8785
|
+
run = {},
|
|
8786
|
+
maxTurns,
|
|
8787
|
+
worktreeDir,
|
|
8788
|
+
githubToken,
|
|
8789
|
+
safeProgress: safeProgress2,
|
|
8790
|
+
log: log3 = () => {
|
|
8791
|
+
},
|
|
8792
|
+
runCommand = defaultRunCommand3
|
|
8793
|
+
} = {}) {
|
|
8794
|
+
const numOrUndef2 = (x) => typeof x === "number" ? x : void 0;
|
|
8795
|
+
const terminal = decideNoChangesTerminalStatus({ partial, run, maxTurns });
|
|
8796
|
+
await safeProgress2(client, id, {
|
|
8797
|
+
...terminal,
|
|
8798
|
+
cost_usd: numOrUndef2(run.costUsd),
|
|
8799
|
+
num_turns: numOrUndef2(run.numTurns)
|
|
8800
|
+
});
|
|
8801
|
+
if (terminal.status === "no_changes_needed") {
|
|
8802
|
+
log3(`task ${id}: agent completed successfully with no changes (already fixed)`);
|
|
8803
|
+
await closeSupersededSourceOnNoChanges({ task, run, worktreeDir, githubToken, log: log3, runCommand });
|
|
8804
|
+
} else if (!partial) {
|
|
8805
|
+
log3(`task ${id}: agent reported a blocker with no changes; preserving failure honestly`);
|
|
8806
|
+
}
|
|
8807
|
+
return terminal;
|
|
8808
|
+
}
|
|
8645
8809
|
var RESULT_LIMIT;
|
|
8646
8810
|
var init_no_changes_terminal_status = __esm({
|
|
8647
8811
|
"../../scripts/virtual-office/code-runner/no-changes-terminal-status.mjs"() {
|
|
8648
8812
|
"use strict";
|
|
8813
|
+
init_process_runner2();
|
|
8814
|
+
init_publish();
|
|
8815
|
+
init_superseded_pr_source();
|
|
8649
8816
|
RESULT_LIMIT = 2e3;
|
|
8650
8817
|
}
|
|
8651
8818
|
});
|
|
@@ -8657,7 +8824,7 @@ __export(code_runner_daemon_exports, {
|
|
|
8657
8824
|
});
|
|
8658
8825
|
import os4 from "node:os";
|
|
8659
8826
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
8660
|
-
import { fileURLToPath as
|
|
8827
|
+
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
8661
8828
|
function log2(msg) {
|
|
8662
8829
|
console.log(`[code-runner ${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}`);
|
|
8663
8830
|
}
|
|
@@ -8801,14 +8968,18 @@ async function processOneTask(client, task, cfg) {
|
|
|
8801
8968
|
log2(`task ${id}: dropped ${scratch.length} scratch file(s): ${scratch.join(", ")}`);
|
|
8802
8969
|
}
|
|
8803
8970
|
if (files.length === 0) {
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
|
|
8971
|
+
await finalizeNoChangesOutcome({
|
|
8972
|
+
client,
|
|
8973
|
+
id,
|
|
8974
|
+
task,
|
|
8975
|
+
partial,
|
|
8976
|
+
run,
|
|
8977
|
+
maxTurns: effectiveMaxTurns,
|
|
8978
|
+
worktreeDir: wt.worktreeDir,
|
|
8979
|
+
githubToken,
|
|
8980
|
+
safeProgress,
|
|
8981
|
+
log: log2
|
|
8809
8982
|
});
|
|
8810
|
-
if (terminal.status === "no_changes_needed") log2(`task ${id}: agent completed successfully with no changes (already fixed)`);
|
|
8811
|
-
else if (!partial) log2(`task ${id}: agent reported a blocker with no changes; preserving failure honestly`);
|
|
8812
8983
|
return;
|
|
8813
8984
|
}
|
|
8814
8985
|
const fresh = await client.getTask(id).catch(() => null);
|
|
@@ -9015,7 +9186,7 @@ var init_code_runner_daemon = __esm({
|
|
|
9015
9186
|
sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
9016
9187
|
numOrUndef = (x) => typeof x === "number" ? x : void 0;
|
|
9017
9188
|
safeProgress = makeSafeProgress(log2);
|
|
9018
|
-
invokedDirectly = process.argv[1] &&
|
|
9189
|
+
invokedDirectly = process.argv[1] && fileURLToPath5(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).
|
|
9019
9190
|
import.meta.url.endsWith("code-runner-daemon.mjs");
|
|
9020
9191
|
if (invokedDirectly) {
|
|
9021
9192
|
const once2 = process.argv.includes("--once");
|