@algosuite/vo-mcp 0.2.0-beta.17 → 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 +253 -31
- package/dist/cli.js.map +4 -4
- package/dist/index.js +241 -17
- 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 +605 -137
- package/dist/runner-cli.js.map +4 -4
- package/dist/runner-supervisor.js +41 -8
- 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 {
|
|
@@ -3187,6 +3200,65 @@ var init_orphan_agent_reaper = __esm({
|
|
|
3187
3200
|
}
|
|
3188
3201
|
});
|
|
3189
3202
|
|
|
3203
|
+
// ../../scripts/virtual-office/code-runner/cli-version-floor.mjs
|
|
3204
|
+
function parseCliVersion(output) {
|
|
3205
|
+
const match = /\b(\d+)\.(\d+)\.(\d+)(?:-[0-9A-Za-z.-]+)?\b/.exec(String(output ?? ""));
|
|
3206
|
+
return match ? `${match[1]}.${match[2]}.${match[3]}` : null;
|
|
3207
|
+
}
|
|
3208
|
+
function compareSemver(a, b) {
|
|
3209
|
+
const pa = a.split(".").map(Number);
|
|
3210
|
+
const pb = b.split(".").map(Number);
|
|
3211
|
+
for (let i = 0; i < 3; i += 1) {
|
|
3212
|
+
if (pa[i] !== pb[i]) return pa[i] < pb[i] ? -1 : 1;
|
|
3213
|
+
}
|
|
3214
|
+
return 0;
|
|
3215
|
+
}
|
|
3216
|
+
function checkCliVersionFloor(versionOutput, { floor = MIN_CLAUDE_CLI_VERSION } = {}) {
|
|
3217
|
+
const version = parseCliVersion(versionOutput);
|
|
3218
|
+
if (!version) {
|
|
3219
|
+
const seen = String(versionOutput ?? "").trim().slice(0, 120) || "<empty>";
|
|
3220
|
+
return {
|
|
3221
|
+
ok: false,
|
|
3222
|
+
version: null,
|
|
3223
|
+
floor,
|
|
3224
|
+
message: `could not parse a semver from \`claude --version\` output ("${seen}") \u2014 cannot prove the CLI meets the ${floor} security floor. ${SECURITY_RATIONALE}`
|
|
3225
|
+
};
|
|
3226
|
+
}
|
|
3227
|
+
if (compareSemver(version, floor) < 0) {
|
|
3228
|
+
return {
|
|
3229
|
+
ok: false,
|
|
3230
|
+
version,
|
|
3231
|
+
floor,
|
|
3232
|
+
message: `claude CLI ${version} is BELOW the minimum security floor ${floor}. ` + SECURITY_RATIONALE
|
|
3233
|
+
};
|
|
3234
|
+
}
|
|
3235
|
+
return {
|
|
3236
|
+
ok: true,
|
|
3237
|
+
version,
|
|
3238
|
+
floor,
|
|
3239
|
+
message: `claude CLI ${version} meets the minimum security floor ${floor}`
|
|
3240
|
+
};
|
|
3241
|
+
}
|
|
3242
|
+
function applyCliVersionFloor({ versionOutput, env: env2 = process.env, log: log3 = console.error } = {}) {
|
|
3243
|
+
const check = checkCliVersionFloor(versionOutput);
|
|
3244
|
+
if (check.ok) return { refused: false, check, message: check.message };
|
|
3245
|
+
const enforce = String(env2?.VO_CLI_FLOOR_ENFORCE ?? "") === "1";
|
|
3246
|
+
const message = `[cli-version-floor] ${enforce ? "REFUSING (VO_CLI_FLOOR_ENFORCE=1)" : "WARNING (warn-only)"}: ` + check.message;
|
|
3247
|
+
try {
|
|
3248
|
+
log3(message);
|
|
3249
|
+
} catch {
|
|
3250
|
+
}
|
|
3251
|
+
return { refused: enforce, check, message };
|
|
3252
|
+
}
|
|
3253
|
+
var MIN_CLAUDE_CLI_VERSION, SECURITY_RATIONALE;
|
|
3254
|
+
var init_cli_version_floor = __esm({
|
|
3255
|
+
"../../scripts/virtual-office/code-runner/cli-version-floor.mjs"() {
|
|
3256
|
+
"use strict";
|
|
3257
|
+
MIN_CLAUDE_CLI_VERSION = "2.1.216";
|
|
3258
|
+
SECURITY_RATIONALE = "Claude Code 2.1.211/2.1.213 fixed a PreToolUse-hook bypass on unsandboxed Bash (our destructive-fs/git/cloud tripwires DO NOT FIRE on older CLIs) and worktree-subagents mutating the main checkout. Update: npm install -g @anthropic-ai/claude-code (or the native installer).";
|
|
3259
|
+
}
|
|
3260
|
+
});
|
|
3261
|
+
|
|
3190
3262
|
// ../../scripts/virtual-office/code-runner/claude-runner.mjs
|
|
3191
3263
|
import { spawn as spawn2 } from "node:child_process";
|
|
3192
3264
|
function extractText(content) {
|
|
@@ -3409,6 +3481,7 @@ var init_claude_runner = __esm({
|
|
|
3409
3481
|
init_windows_claude_launch();
|
|
3410
3482
|
init_terminal_process_cleanup();
|
|
3411
3483
|
init_orphan_agent_reaper();
|
|
3484
|
+
init_cli_version_floor();
|
|
3412
3485
|
ClaudeRunner = class {
|
|
3413
3486
|
get binary() {
|
|
3414
3487
|
return "claude";
|
|
@@ -3445,7 +3518,7 @@ var init_claude_runner = __esm({
|
|
|
3445
3518
|
*/
|
|
3446
3519
|
async checkAuth() {
|
|
3447
3520
|
try {
|
|
3448
|
-
const probe = spawnClaudeSync(["--version"], { timeout: 3e3,
|
|
3521
|
+
const probe = spawnClaudeSync(["--version"], { timeout: 3e3, encoding: "utf8" });
|
|
3449
3522
|
if (probe.error) {
|
|
3450
3523
|
return {
|
|
3451
3524
|
installed: false,
|
|
@@ -3456,6 +3529,8 @@ var init_claude_runner = __esm({
|
|
|
3456
3529
|
if (probe.status !== 0) {
|
|
3457
3530
|
return { installed: true, authenticated: false, message: "claude binary exists but --version failed (auth unclear)" };
|
|
3458
3531
|
}
|
|
3532
|
+
const floorGate = applyCliVersionFloor({ versionOutput: probe.stdout, env: process.env });
|
|
3533
|
+
if (floorGate.refused) return { installed: true, authenticated: false, message: floorGate.message };
|
|
3459
3534
|
const loggedIn = probeClaudeLoginState();
|
|
3460
3535
|
if (loggedIn === false) {
|
|
3461
3536
|
return {
|
|
@@ -3598,7 +3673,7 @@ function resolveCodexBinary({
|
|
|
3598
3673
|
return "codex";
|
|
3599
3674
|
}
|
|
3600
3675
|
function buildCodexArgs({ model, effort } = {}) {
|
|
3601
|
-
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"];
|
|
3602
3677
|
if (model) {
|
|
3603
3678
|
args.push("--model", String(model));
|
|
3604
3679
|
}
|
|
@@ -3667,11 +3742,25 @@ var init_codex_runner = __esm({
|
|
|
3667
3742
|
parseEvent(line) {
|
|
3668
3743
|
return parseCodexEvent(line);
|
|
3669
3744
|
}
|
|
3670
|
-
|
|
3671
|
-
|
|
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() {
|
|
3672
3760
|
return {
|
|
3673
|
-
shell:
|
|
3674
|
-
windowsHide: true
|
|
3761
|
+
shell: false,
|
|
3762
|
+
windowsHide: true,
|
|
3763
|
+
windowsVerbatimArguments: false
|
|
3675
3764
|
};
|
|
3676
3765
|
}
|
|
3677
3766
|
/**
|
|
@@ -3805,10 +3894,26 @@ var init_cursor_runner = __esm({
|
|
|
3805
3894
|
parseEvent(line) {
|
|
3806
3895
|
return parseCursorEvent(line);
|
|
3807
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
|
+
*/
|
|
3808
3912
|
getSpawnOptions() {
|
|
3809
3913
|
return {
|
|
3810
|
-
shell:
|
|
3811
|
-
windowsHide: true
|
|
3914
|
+
shell: false,
|
|
3915
|
+
windowsHide: true,
|
|
3916
|
+
windowsVerbatimArguments: false
|
|
3812
3917
|
};
|
|
3813
3918
|
}
|
|
3814
3919
|
/**
|
|
@@ -3823,7 +3928,7 @@ var init_cursor_runner = __esm({
|
|
|
3823
3928
|
async checkAuth() {
|
|
3824
3929
|
try {
|
|
3825
3930
|
const { status, error } = spawnSync7("cursor-agent", ["--version"], {
|
|
3826
|
-
shell:
|
|
3931
|
+
shell: false,
|
|
3827
3932
|
windowsHide: true,
|
|
3828
3933
|
timeout: 3e3,
|
|
3829
3934
|
stdio: "ignore"
|
|
@@ -3880,11 +3985,13 @@ var init_meta_runner = __esm({
|
|
|
3880
3985
|
parseEvent(line) {
|
|
3881
3986
|
return parseCodexEvent(line);
|
|
3882
3987
|
}
|
|
3883
|
-
|
|
3884
|
-
|
|
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() {
|
|
3885
3991
|
return {
|
|
3886
|
-
shell:
|
|
3887
|
-
windowsHide: true
|
|
3992
|
+
shell: false,
|
|
3993
|
+
windowsHide: true,
|
|
3994
|
+
windowsVerbatimArguments: false
|
|
3888
3995
|
};
|
|
3889
3996
|
}
|
|
3890
3997
|
applyAuthEnv(env2 = process.env) {
|
|
@@ -3933,11 +4040,13 @@ var init_openai_compatible_runner = __esm({
|
|
|
3933
4040
|
parseEvent(line) {
|
|
3934
4041
|
return parseCodexEvent(line);
|
|
3935
4042
|
}
|
|
3936
|
-
|
|
3937
|
-
|
|
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() {
|
|
3938
4046
|
return {
|
|
3939
|
-
shell:
|
|
3940
|
-
windowsHide: true
|
|
4047
|
+
shell: false,
|
|
4048
|
+
windowsHide: true,
|
|
4049
|
+
windowsVerbatimArguments: false
|
|
3941
4050
|
};
|
|
3942
4051
|
}
|
|
3943
4052
|
/** Fill the BYO key env var from the OS keychain when not already set. */
|
|
@@ -4182,6 +4291,16 @@ var init_rate_limit_resume = __esm({
|
|
|
4182
4291
|
}
|
|
4183
4292
|
});
|
|
4184
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
|
+
|
|
4185
4304
|
// ../../scripts/virtual-office/code-runner/git-resilience.mjs
|
|
4186
4305
|
function isTransientGitError(err) {
|
|
4187
4306
|
if (!err) return false;
|
|
@@ -4193,7 +4312,7 @@ function isTransientGitError(err) {
|
|
|
4193
4312
|
}
|
|
4194
4313
|
return TRANSIENT_RE.test(msg);
|
|
4195
4314
|
}
|
|
4196
|
-
function computeGitBackoffMs(attempt, { baseMs = 5e3, capMs = 3e4, rng =
|
|
4315
|
+
function computeGitBackoffMs(attempt, { baseMs = 5e3, capMs = 3e4, rng = secureUnitRandom } = {}) {
|
|
4197
4316
|
const exp = Math.min(capMs, baseMs * Math.pow(2, Math.max(0, attempt)));
|
|
4198
4317
|
return Math.floor(exp / 2 + rng() * (exp / 2));
|
|
4199
4318
|
}
|
|
@@ -4201,6 +4320,7 @@ var TRANSIENT_CODES, TRANSIENT_RE;
|
|
|
4201
4320
|
var init_git_resilience = __esm({
|
|
4202
4321
|
"../../scripts/virtual-office/code-runner/git-resilience.mjs"() {
|
|
4203
4322
|
"use strict";
|
|
4323
|
+
init_secure_random();
|
|
4204
4324
|
TRANSIENT_CODES = /* @__PURE__ */ new Set([
|
|
4205
4325
|
"ETIMEDOUT",
|
|
4206
4326
|
"ECONNRESET",
|
|
@@ -4227,10 +4347,39 @@ var init_auto_merge = __esm({
|
|
|
4227
4347
|
|
|
4228
4348
|
// ../../scripts/virtual-office/code-runner/pr-overlap-gate.mjs
|
|
4229
4349
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
4230
|
-
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;
|
|
4231
4367
|
var init_pr_overlap_gate = __esm({
|
|
4232
4368
|
"../../scripts/virtual-office/code-runner/pr-overlap-gate.mjs"() {
|
|
4233
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
|
+
]);
|
|
4234
4383
|
}
|
|
4235
4384
|
});
|
|
4236
4385
|
|
|
@@ -4274,14 +4423,14 @@ function parsePorcelainZ(out) {
|
|
|
4274
4423
|
for (let i = 0; i < tokens.length; i += 1) {
|
|
4275
4424
|
const tok = tokens[i];
|
|
4276
4425
|
if (!tok) continue;
|
|
4277
|
-
const
|
|
4278
|
-
if (
|
|
4426
|
+
const path16 = tok.slice(3);
|
|
4427
|
+
if (path16) files.push(path16);
|
|
4279
4428
|
if (tok[0] === "R" || tok[0] === "C") i += 1;
|
|
4280
4429
|
}
|
|
4281
4430
|
return files;
|
|
4282
4431
|
}
|
|
4283
|
-
function isAgentScratch(
|
|
4284
|
-
const p = String(
|
|
4432
|
+
function isAgentScratch(path16) {
|
|
4433
|
+
const p = String(path16 || "");
|
|
4285
4434
|
return SCRATCH_PATTERNS.some((re) => re.test(p));
|
|
4286
4435
|
}
|
|
4287
4436
|
function isMaxTurnsResult(summary) {
|
|
@@ -4488,7 +4637,6 @@ var init_partial_pr_continuation = __esm({
|
|
|
4488
4637
|
});
|
|
4489
4638
|
|
|
4490
4639
|
// ../../scripts/virtual-office/code-runner/publish-async.mjs
|
|
4491
|
-
import path12 from "node:path";
|
|
4492
4640
|
function compactTitle(value, max = 100) {
|
|
4493
4641
|
return String(value || "").replace(/\s+/g, " ").trim().slice(0, max) || "code-task";
|
|
4494
4642
|
}
|
|
@@ -4498,7 +4646,7 @@ function gitRetryLog(op) {
|
|
|
4498
4646
|
console.error(`[publish] transient ${op} failure (attempt ${attempt}): ${why} \u2014 retrying in ${Math.round(delayMs / 1e3)}s`);
|
|
4499
4647
|
};
|
|
4500
4648
|
}
|
|
4501
|
-
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 } = {}) {
|
|
4502
4650
|
let lastErr;
|
|
4503
4651
|
for (let i = 0; i < attempts; i += 1) {
|
|
4504
4652
|
try {
|
|
@@ -4529,8 +4677,12 @@ async function resolveOrCreateBranchAsync(worktreeDir, branchPrefix, runCommand
|
|
|
4529
4677
|
}
|
|
4530
4678
|
return branch;
|
|
4531
4679
|
}
|
|
4532
|
-
async function runLocalPrOverlapGateAsync(worktreeDir, files, { branch = "", env: env2 = process.env, excludePrNumber = null } = {}) {
|
|
4533
|
-
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
|
+
}
|
|
4534
4686
|
try {
|
|
4535
4687
|
const output = await runProcess2("node", [
|
|
4536
4688
|
scriptPath,
|
|
@@ -4539,7 +4691,7 @@ async function runLocalPrOverlapGateAsync(worktreeDir, files, { branch = "", env
|
|
|
4539
4691
|
...excludePrNumber ? ["--exclude-pr", String(excludePrNumber)] : []
|
|
4540
4692
|
], {
|
|
4541
4693
|
cwd: worktreeDir,
|
|
4542
|
-
env:
|
|
4694
|
+
env: childEnv,
|
|
4543
4695
|
input: JSON.stringify([...new Set((files || []).map((file) => String(file || "").trim()).filter(Boolean))]),
|
|
4544
4696
|
timeout: 12e4
|
|
4545
4697
|
});
|
|
@@ -4821,6 +4973,8 @@ var init_publish_async = __esm({
|
|
|
4821
4973
|
init_auto_merge();
|
|
4822
4974
|
init_git_resilience();
|
|
4823
4975
|
init_process_runner2();
|
|
4976
|
+
init_secure_random();
|
|
4977
|
+
init_pr_overlap_gate();
|
|
4824
4978
|
init_existing_pr_publication();
|
|
4825
4979
|
init_partial_pr_continuation();
|
|
4826
4980
|
sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
@@ -4837,7 +4991,7 @@ function defaultRunCommand2(cmd, args, cwd, opts = {}) {
|
|
|
4837
4991
|
function buildResumeLocalBranchName(remoteBranch, {
|
|
4838
4992
|
now = () => /* @__PURE__ */ new Date(),
|
|
4839
4993
|
pid = process.pid,
|
|
4840
|
-
random =
|
|
4994
|
+
random = secureUnitRandom
|
|
4841
4995
|
} = {}) {
|
|
4842
4996
|
const stamp = now().toISOString().replace(/[:.]/g, "-");
|
|
4843
4997
|
const unique = `${pid}-${random().toString(36).slice(2, 8)}`;
|
|
@@ -4929,6 +5083,85 @@ var init_resume_branch = __esm({
|
|
|
4929
5083
|
"use strict";
|
|
4930
5084
|
init_publish();
|
|
4931
5085
|
init_process_runner2();
|
|
5086
|
+
init_secure_random();
|
|
5087
|
+
}
|
|
5088
|
+
});
|
|
5089
|
+
|
|
5090
|
+
// ../../scripts/virtual-office/code-runner/skill-catalog.mjs
|
|
5091
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync3, statSync } from "node:fs";
|
|
5092
|
+
import { dirname as dirname3, join as join3 } from "node:path";
|
|
5093
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
5094
|
+
function parseFrontmatterNameDescription(raw) {
|
|
5095
|
+
const text = String(raw).replace(/\r\n/g, "\n");
|
|
5096
|
+
if (!text.startsWith("---\n")) return null;
|
|
5097
|
+
const end = text.indexOf("\n---\n", 4);
|
|
5098
|
+
if (end === -1) return null;
|
|
5099
|
+
let name = "";
|
|
5100
|
+
let description = "";
|
|
5101
|
+
for (const line of text.slice(4, end).split("\n")) {
|
|
5102
|
+
const idx = line.indexOf(":");
|
|
5103
|
+
if (idx === -1) continue;
|
|
5104
|
+
const key = line.slice(0, idx).trim();
|
|
5105
|
+
const value = line.slice(idx + 1).trim();
|
|
5106
|
+
if (key === "name") name = value;
|
|
5107
|
+
else if (key === "description") description = value;
|
|
5108
|
+
}
|
|
5109
|
+
return name && description ? { name, description } : null;
|
|
5110
|
+
}
|
|
5111
|
+
function resolveDefaultRepoRoot() {
|
|
5112
|
+
const starts = [dirname3(fileURLToPath2(import.meta.url)), process.cwd()];
|
|
5113
|
+
for (const start of starts) {
|
|
5114
|
+
let dir = start;
|
|
5115
|
+
for (let i = 0; i < 8; i += 1) {
|
|
5116
|
+
try {
|
|
5117
|
+
if (statSync(join3(dir, ".claude", "skills")).isDirectory()) return dir;
|
|
5118
|
+
} catch {
|
|
5119
|
+
}
|
|
5120
|
+
const parent = dirname3(dir);
|
|
5121
|
+
if (parent === dir) break;
|
|
5122
|
+
dir = parent;
|
|
5123
|
+
}
|
|
5124
|
+
}
|
|
5125
|
+
return process.cwd();
|
|
5126
|
+
}
|
|
5127
|
+
function loadSkillCatalog({ repoRoot: repoRoot2 = resolveDefaultRepoRoot() } = {}) {
|
|
5128
|
+
try {
|
|
5129
|
+
const skillsDir = join3(repoRoot2, ".claude", "skills");
|
|
5130
|
+
const catalog = [];
|
|
5131
|
+
for (const entry of readdirSync2(skillsDir)) {
|
|
5132
|
+
const dir = join3(skillsDir, entry);
|
|
5133
|
+
try {
|
|
5134
|
+
if (!statSync(dir).isDirectory()) continue;
|
|
5135
|
+
const parsed = parseFrontmatterNameDescription(
|
|
5136
|
+
readFileSync3(join3(dir, "SKILL.md"), "utf8")
|
|
5137
|
+
);
|
|
5138
|
+
if (parsed) catalog.push(parsed);
|
|
5139
|
+
} catch {
|
|
5140
|
+
}
|
|
5141
|
+
}
|
|
5142
|
+
return catalog.sort((a, b) => a.name.localeCompare(b.name)).slice(0, CATALOG_CAP);
|
|
5143
|
+
} catch {
|
|
5144
|
+
return [];
|
|
5145
|
+
}
|
|
5146
|
+
}
|
|
5147
|
+
function buildSkillCatalogBlock(catalog) {
|
|
5148
|
+
if (!Array.isArray(catalog) || catalog.length === 0) return "";
|
|
5149
|
+
const lines = catalog.map((s) => ` - ${s.name}: ${s.description}`);
|
|
5150
|
+
return [
|
|
5151
|
+
"\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 ALGOSUITE SKILL CATALOG \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550",
|
|
5152
|
+
"The repo ships a skill corpus (same one Claude Code loads natively). When a",
|
|
5153
|
+
"task matches a skill below, LOAD ITS FULL INSTRUCTIONS FIRST and follow them:",
|
|
5154
|
+
" - via MCP: call vo_skill_get with the skill name (any vendor with AlgoHQ MCP tools), or",
|
|
5155
|
+
" - via file: read .claude/skills/<name>/SKILL.md in this worktree.",
|
|
5156
|
+
lines.join("\n"),
|
|
5157
|
+
""
|
|
5158
|
+
].join("\n");
|
|
5159
|
+
}
|
|
5160
|
+
var CATALOG_CAP;
|
|
5161
|
+
var init_skill_catalog = __esm({
|
|
5162
|
+
"../../scripts/virtual-office/code-runner/skill-catalog.mjs"() {
|
|
5163
|
+
"use strict";
|
|
5164
|
+
CATALOG_CAP = 60;
|
|
4932
5165
|
}
|
|
4933
5166
|
});
|
|
4934
5167
|
|
|
@@ -4976,9 +5209,13 @@ function buildKnowledgeContextBlock(contextMarkdown) {
|
|
|
4976
5209
|
}
|
|
4977
5210
|
function composeDispatchPrompt(taskPrompt, opts = {}) {
|
|
4978
5211
|
const knowledge = buildKnowledgeContextBlock(opts.knowledgeContextMarkdown);
|
|
5212
|
+
const catalog = opts.includeSkillCatalog === false ? "" : buildSkillCatalogBlock(
|
|
5213
|
+
opts.skillCatalog ?? loadSkillCatalog({ repoRoot: opts.repoRoot })
|
|
5214
|
+
);
|
|
4979
5215
|
const task = String(taskPrompt ?? "").trim();
|
|
4980
5216
|
return [
|
|
4981
5217
|
buildDispatchOnboarding(opts),
|
|
5218
|
+
catalog,
|
|
4982
5219
|
knowledge,
|
|
4983
5220
|
"\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 YOUR TASK \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550",
|
|
4984
5221
|
task,
|
|
@@ -4989,6 +5226,7 @@ var MANDATORY_READS, NON_NEGOTIABLES;
|
|
|
4989
5226
|
var init_dispatch_onboarding = __esm({
|
|
4990
5227
|
"../../scripts/virtual-office/code-runner/dispatch-onboarding.mjs"() {
|
|
4991
5228
|
"use strict";
|
|
5229
|
+
init_skill_catalog();
|
|
4992
5230
|
MANDATORY_READS = [
|
|
4993
5231
|
"CLAUDE.md (repo root \u2014 Claude-specific rules; auto-loaded, but READ it)",
|
|
4994
5232
|
'AGENTS.md (repo root \u2014 cross-vendor rules + "Onboarding for a lane"; NOT auto-loaded)',
|
|
@@ -5126,7 +5364,7 @@ var init_task_prompt = __esm({
|
|
|
5126
5364
|
import { createHash as createHash3, randomUUID } from "node:crypto";
|
|
5127
5365
|
import { chmod, mkdir, mkdtemp, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
5128
5366
|
import os2 from "node:os";
|
|
5129
|
-
import
|
|
5367
|
+
import path11 from "node:path";
|
|
5130
5368
|
function safeTaskToken(taskId) {
|
|
5131
5369
|
return String(taskId || "task").replace(/[^0-9A-Za-z_-]/gu, "_").slice(0, 48) || "task";
|
|
5132
5370
|
}
|
|
@@ -5136,25 +5374,25 @@ function sanitizeTaskAttachmentName(name, index = 0) {
|
|
|
5136
5374
|
return `${String(index + 1).padStart(2, "0")}-${normalized}`;
|
|
5137
5375
|
}
|
|
5138
5376
|
function assertGeneratedDirectory(directory, tempRoot) {
|
|
5139
|
-
const resolvedDirectory =
|
|
5140
|
-
const resolvedRoot =
|
|
5141
|
-
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)) {
|
|
5142
5380
|
throw new Error("refusing to clean an unverified task-attachment directory");
|
|
5143
5381
|
}
|
|
5144
5382
|
return resolvedDirectory;
|
|
5145
5383
|
}
|
|
5146
5384
|
async function createAttachmentDirectory(taskId, tempRoot) {
|
|
5147
|
-
const root =
|
|
5385
|
+
const root = path11.resolve(tempRoot);
|
|
5148
5386
|
await mkdir(root, { recursive: true });
|
|
5149
|
-
const directory = await mkdtemp(
|
|
5150
|
-
const marker = JSON.stringify({ owner: MARKER_OWNER, token: randomUUID(), directory:
|
|
5151
|
-
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 });
|
|
5152
5390
|
return { directory, marker, tempRoot: root };
|
|
5153
5391
|
}
|
|
5154
5392
|
async function cleanupGeneratedDirectory(state) {
|
|
5155
5393
|
if (!state || state.cleaned) return;
|
|
5156
5394
|
const directory = assertGeneratedDirectory(state.directory, state.tempRoot);
|
|
5157
|
-
const marker = await readFile(
|
|
5395
|
+
const marker = await readFile(path11.join(directory, MARKER_FILE), "utf8").catch(() => "");
|
|
5158
5396
|
if (marker !== state.marker) throw new Error("refusing to clean a task-attachment directory without its exact marker");
|
|
5159
5397
|
await rm(directory, { recursive: true, force: true });
|
|
5160
5398
|
state.cleaned = true;
|
|
@@ -5173,7 +5411,7 @@ async function sweepStaleTaskAttachmentDirectories({
|
|
|
5173
5411
|
now = Date.now(),
|
|
5174
5412
|
maxAgeMs = DEFAULT_STALE_AGE_MS
|
|
5175
5413
|
} = {}) {
|
|
5176
|
-
const root =
|
|
5414
|
+
const root = path11.resolve(tempRoot);
|
|
5177
5415
|
if (!Number.isFinite(maxAgeMs) || maxAgeMs <= 0) throw new Error("stale attachment age must be positive");
|
|
5178
5416
|
const entries = await readdir(root, { withFileTypes: true }).catch((error) => {
|
|
5179
5417
|
if (error?.code === "ENOENT") return [];
|
|
@@ -5182,8 +5420,8 @@ async function sweepStaleTaskAttachmentDirectories({
|
|
|
5182
5420
|
let removed = 0;
|
|
5183
5421
|
for (const entry of entries) {
|
|
5184
5422
|
if (!entry.isDirectory() || !entry.name.startsWith(DIRECTORY_PREFIX)) continue;
|
|
5185
|
-
const directory = assertGeneratedDirectory(
|
|
5186
|
-
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(() => "");
|
|
5187
5425
|
const marker = parseOwnedMarker(markerRaw, entry.name);
|
|
5188
5426
|
if (!marker) continue;
|
|
5189
5427
|
const directoryStat = await stat(directory);
|
|
@@ -5226,10 +5464,10 @@ async function materializeTaskAttachments(client, task, { tempRoot = os2.tmpdir(
|
|
|
5226
5464
|
const sha256 = createHash3("sha256").update(content).digest("hex");
|
|
5227
5465
|
if (sha256 !== ref.sha256) throw new Error(`attachment ${ref.attachment_id} sha256 mismatch`);
|
|
5228
5466
|
const name = sanitizeTaskAttachmentName(ref.name, index);
|
|
5229
|
-
const filePath =
|
|
5467
|
+
const filePath = path11.join(state.directory, name);
|
|
5230
5468
|
await writeFile(filePath, content, { flag: "wx", mode: 384 });
|
|
5231
5469
|
await chmod(filePath, 384);
|
|
5232
|
-
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) });
|
|
5233
5471
|
}
|
|
5234
5472
|
return { directory: state.directory, files, manifestMarkdown: buildManifest(files), cleanup: () => cleanupGeneratedDirectory(state) };
|
|
5235
5473
|
} catch (error) {
|
|
@@ -5252,7 +5490,7 @@ var init_task_attachments = __esm({
|
|
|
5252
5490
|
|
|
5253
5491
|
// ../../scripts/virtual-office/code-runner/session-spool-forwarder.mjs
|
|
5254
5492
|
import { homedir as homedir3 } from "node:os";
|
|
5255
|
-
import { join as
|
|
5493
|
+
import { join as join4 } from "node:path";
|
|
5256
5494
|
import { readdir as readdir2, readFile as readFile2, unlink, writeFile as writeFile2 } from "node:fs/promises";
|
|
5257
5495
|
import { createHash as createHash4 } from "node:crypto";
|
|
5258
5496
|
function deriveUuid(seed) {
|
|
@@ -5284,18 +5522,18 @@ async function readSpool(spoolDir = SPOOL_DIR) {
|
|
|
5284
5522
|
for (const f of files) {
|
|
5285
5523
|
if (!f.endsWith(".json")) continue;
|
|
5286
5524
|
try {
|
|
5287
|
-
const record = JSON.parse(await readFile2(
|
|
5525
|
+
const record = JSON.parse(await readFile2(join4(spoolDir, f), "utf8"));
|
|
5288
5526
|
if (record && typeof record.session_key === "string") {
|
|
5289
|
-
out.push({ full:
|
|
5527
|
+
out.push({ full: join4(spoolDir, f), record });
|
|
5290
5528
|
}
|
|
5291
5529
|
} catch {
|
|
5292
5530
|
}
|
|
5293
5531
|
}
|
|
5294
5532
|
return out;
|
|
5295
5533
|
}
|
|
5296
|
-
async function readCloudMap(
|
|
5534
|
+
async function readCloudMap(path16) {
|
|
5297
5535
|
try {
|
|
5298
|
-
return JSON.parse(await readFile2(
|
|
5536
|
+
return JSON.parse(await readFile2(path16, "utf8"));
|
|
5299
5537
|
} catch {
|
|
5300
5538
|
return {};
|
|
5301
5539
|
}
|
|
@@ -5368,8 +5606,8 @@ var SPOOL_DIR, CLOUD_MAP_FILE, STALE_MS, ACTIVE_SILENCE_MS;
|
|
|
5368
5606
|
var init_session_spool_forwarder = __esm({
|
|
5369
5607
|
"../../scripts/virtual-office/code-runner/session-spool-forwarder.mjs"() {
|
|
5370
5608
|
"use strict";
|
|
5371
|
-
SPOOL_DIR =
|
|
5372
|
-
CLOUD_MAP_FILE =
|
|
5609
|
+
SPOOL_DIR = join4(homedir3(), ".vo", "session-spool");
|
|
5610
|
+
CLOUD_MAP_FILE = join4(homedir3(), ".vo", "session-cloud-map.json");
|
|
5373
5611
|
STALE_MS = 60 * 60 * 1e3;
|
|
5374
5612
|
ACTIVE_SILENCE_MS = 10 * 60 * 1e3;
|
|
5375
5613
|
}
|
|
@@ -5438,14 +5676,14 @@ var init_rate_limit_resume_scheduler_core = __esm({
|
|
|
5438
5676
|
});
|
|
5439
5677
|
|
|
5440
5678
|
// ../../scripts/virtual-office/code-runner/rate-limit-resume-scheduler.mjs
|
|
5441
|
-
import { readFileSync as
|
|
5442
|
-
import { dirname as
|
|
5679
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "node:fs";
|
|
5680
|
+
import { dirname as dirname4, join as join5, resolve } from "node:path";
|
|
5443
5681
|
function log(msg) {
|
|
5444
5682
|
console.log(`[rate-limit-scheduler ${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}`);
|
|
5445
5683
|
}
|
|
5446
5684
|
function readQueue(queuePath) {
|
|
5447
|
-
if (!
|
|
5448
|
-
const content =
|
|
5685
|
+
if (!existsSync6(queuePath)) return [];
|
|
5686
|
+
const content = readFileSync4(queuePath, "utf-8");
|
|
5449
5687
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
5450
5688
|
const entries = [];
|
|
5451
5689
|
for (const line of lines) {
|
|
@@ -5458,18 +5696,18 @@ function readQueue(queuePath) {
|
|
|
5458
5696
|
return entries;
|
|
5459
5697
|
}
|
|
5460
5698
|
function writeQueue(queuePath, entries) {
|
|
5461
|
-
mkdirSync4(
|
|
5699
|
+
mkdirSync4(dirname4(queuePath), { recursive: true });
|
|
5462
5700
|
const lines = entries.map((e) => JSON.stringify(e)).join("\n");
|
|
5463
5701
|
writeFileSync3(queuePath, lines + (entries.length > 0 ? "\n" : ""), "utf-8");
|
|
5464
5702
|
}
|
|
5465
5703
|
function attemptsStorePath() {
|
|
5466
|
-
return
|
|
5704
|
+
return join5(dirname4(resumeQueuePath()), "resume-attempts.json");
|
|
5467
5705
|
}
|
|
5468
5706
|
function readAttemptsStore() {
|
|
5469
5707
|
const p = attemptsStorePath();
|
|
5470
|
-
if (!
|
|
5708
|
+
if (!existsSync6(p)) return {};
|
|
5471
5709
|
try {
|
|
5472
|
-
const parsed = JSON.parse(
|
|
5710
|
+
const parsed = JSON.parse(readFileSync4(p, "utf-8"));
|
|
5473
5711
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
5474
5712
|
} catch {
|
|
5475
5713
|
return {};
|
|
@@ -5477,7 +5715,7 @@ function readAttemptsStore() {
|
|
|
5477
5715
|
}
|
|
5478
5716
|
function writeAttemptsStore(store) {
|
|
5479
5717
|
const p = attemptsStorePath();
|
|
5480
|
-
mkdirSync4(
|
|
5718
|
+
mkdirSync4(dirname4(p), { recursive: true });
|
|
5481
5719
|
writeFileSync3(p, JSON.stringify(store, null, 2), "utf-8");
|
|
5482
5720
|
}
|
|
5483
5721
|
function countsFromStore(store) {
|
|
@@ -5863,7 +6101,7 @@ var init_agent_availability = __esm({
|
|
|
5863
6101
|
import { spawn as spawn4 } from "node:child_process";
|
|
5864
6102
|
import fs6 from "node:fs";
|
|
5865
6103
|
import os3 from "node:os";
|
|
5866
|
-
import
|
|
6104
|
+
import path12 from "node:path";
|
|
5867
6105
|
function readClaudeUsage({ homeDir = os3.homedir(), read: rawRead = readJson } = {}) {
|
|
5868
6106
|
const read = (p) => {
|
|
5869
6107
|
try {
|
|
@@ -5872,7 +6110,7 @@ function readClaudeUsage({ homeDir = os3.homedir(), read: rawRead = readJson } =
|
|
|
5872
6110
|
return null;
|
|
5873
6111
|
}
|
|
5874
6112
|
};
|
|
5875
|
-
const status = read(
|
|
6113
|
+
const status = read(path12.join(homeDir, ".claude", "claude-usage.json"));
|
|
5876
6114
|
if (status && (status.seven_day || status.five_hour)) {
|
|
5877
6115
|
const entry = {
|
|
5878
6116
|
agent: "claude",
|
|
@@ -5881,7 +6119,7 @@ function readClaudeUsage({ homeDir = os3.homedir(), read: rawRead = readJson } =
|
|
|
5881
6119
|
};
|
|
5882
6120
|
if (entry.seven_day_used_pct !== null || entry.five_hour_used_pct !== null) return entry;
|
|
5883
6121
|
}
|
|
5884
|
-
const weekly = read(
|
|
6122
|
+
const weekly = read(path12.join(homeDir, ".claude", "claude-weekly-usage.json"));
|
|
5885
6123
|
if (weekly) {
|
|
5886
6124
|
const entry = {
|
|
5887
6125
|
agent: "claude",
|
|
@@ -6203,7 +6441,7 @@ var init_superseded_pr_source = __esm({
|
|
|
6203
6441
|
|
|
6204
6442
|
// ../../scripts/virtual-office/code-runner/pr-watcher.mjs
|
|
6205
6443
|
import { homedir as homedir4 } from "node:os";
|
|
6206
|
-
import { join as
|
|
6444
|
+
import { join as join6 } from "node:path";
|
|
6207
6445
|
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir2 } from "node:fs/promises";
|
|
6208
6446
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
6209
6447
|
function ghViewPr(prNumber, repo) {
|
|
@@ -6298,7 +6536,7 @@ async function readState(stateFile) {
|
|
|
6298
6536
|
}
|
|
6299
6537
|
async function writeState(stateFile, state) {
|
|
6300
6538
|
try {
|
|
6301
|
-
await mkdir2(
|
|
6539
|
+
await mkdir2(join6(stateFile, ".."), { recursive: true });
|
|
6302
6540
|
await writeFile3(stateFile, JSON.stringify(state, null, 2), "utf8");
|
|
6303
6541
|
} catch {
|
|
6304
6542
|
}
|
|
@@ -6503,7 +6741,7 @@ var init_pr_watcher = __esm({
|
|
|
6503
6741
|
init_pr_watcher_failure_confirmation();
|
|
6504
6742
|
init_superseded_pr_source();
|
|
6505
6743
|
init_superseded_pr_source();
|
|
6506
|
-
DEFAULT_STATE_FILE =
|
|
6744
|
+
DEFAULT_STATE_FILE = join6(homedir4(), ".vo", "dispatched-prs.json");
|
|
6507
6745
|
FAIL_CONCLUSIONS = /* @__PURE__ */ new Set([
|
|
6508
6746
|
"FAILURE",
|
|
6509
6747
|
"TIMED_OUT",
|
|
@@ -6619,9 +6857,9 @@ function buildControlHandler({ getStatus, requestStop, allowedOrigin }) {
|
|
|
6619
6857
|
res.end();
|
|
6620
6858
|
return;
|
|
6621
6859
|
}
|
|
6622
|
-
const
|
|
6860
|
+
const path16 = String(req.url || "").split("?")[0];
|
|
6623
6861
|
res.setHeader("content-type", "application/json");
|
|
6624
|
-
if (req.method === "GET" &&
|
|
6862
|
+
if (req.method === "GET" && path16 === "/status") {
|
|
6625
6863
|
let status;
|
|
6626
6864
|
try {
|
|
6627
6865
|
status = getStatus();
|
|
@@ -6632,7 +6870,7 @@ function buildControlHandler({ getStatus, requestStop, allowedOrigin }) {
|
|
|
6632
6870
|
res.end(JSON.stringify({ ok: true, ...status }));
|
|
6633
6871
|
return;
|
|
6634
6872
|
}
|
|
6635
|
-
if (req.method === "POST" &&
|
|
6873
|
+
if (req.method === "POST" && path16 === "/stop") {
|
|
6636
6874
|
if (!isControlOriginAllowed(req.headers.origin, allowedOrigin) || !req.headers["x-vo-control"]) {
|
|
6637
6875
|
res.statusCode = 403;
|
|
6638
6876
|
res.end(JSON.stringify({ ok: false, error: "forbidden" }));
|
|
@@ -6743,44 +6981,45 @@ ${effortConfig.multiAgentInstruction}
|
|
|
6743
6981
|
parts.push(String(basePrompt || "").trim());
|
|
6744
6982
|
return parts.join("\n");
|
|
6745
6983
|
}
|
|
6746
|
-
var EFFORT_MODE_CONFIG, DEFAULT_MODE;
|
|
6984
|
+
var RED_TEAM_DIRECTIVE, EFFORT_MODE_CONFIG, DEFAULT_MODE;
|
|
6747
6985
|
var init_effort_mode_config = __esm({
|
|
6748
6986
|
"../../scripts/virtual-office/code-runner/effort-mode-config.mjs"() {
|
|
6749
6987
|
"use strict";
|
|
6988
|
+
RED_TEAM_DIRECTIVE = "Before declaring done, red-team your own work: name the top ways it could be wrong \u2014 especially code that is correct but silently not wired into production callers \u2014 give the failure scenario for each, and state the evidence that rules it out.";
|
|
6750
6989
|
EFFORT_MODE_CONFIG = {
|
|
6751
6990
|
fast: {
|
|
6752
6991
|
tier: "cheap",
|
|
6753
6992
|
permissionMode: "acceptEdits",
|
|
6754
6993
|
maxTurns: 80,
|
|
6755
|
-
thinkingDirective:
|
|
6994
|
+
thinkingDirective: RED_TEAM_DIRECTIVE,
|
|
6756
6995
|
multiAgentInstruction: ""
|
|
6757
6996
|
},
|
|
6758
6997
|
standard: {
|
|
6759
6998
|
tier: "mid",
|
|
6760
6999
|
permissionMode: "acceptEdits",
|
|
6761
7000
|
maxTurns: 200,
|
|
6762
|
-
thinkingDirective:
|
|
7001
|
+
thinkingDirective: RED_TEAM_DIRECTIVE,
|
|
6763
7002
|
multiAgentInstruction: ""
|
|
6764
7003
|
},
|
|
6765
7004
|
deep: {
|
|
6766
7005
|
tier: "best",
|
|
6767
7006
|
permissionMode: "acceptEdits",
|
|
6768
7007
|
maxTurns: 300,
|
|
6769
|
-
thinkingDirective:
|
|
7008
|
+
thinkingDirective: `Think step-by-step. Verify assumptions against source code. Check edge cases. ${RED_TEAM_DIRECTIVE}`,
|
|
6770
7009
|
multiAgentInstruction: ""
|
|
6771
7010
|
},
|
|
6772
7011
|
ultra: {
|
|
6773
7012
|
tier: "best",
|
|
6774
7013
|
permissionMode: "acceptEdits",
|
|
6775
7014
|
maxTurns: 500,
|
|
6776
|
-
thinkingDirective:
|
|
7015
|
+
thinkingDirective: `Think step-by-step. Exhaustively verify every assumption against source code and documentation. ${RED_TEAM_DIRECTIVE}`,
|
|
6777
7016
|
multiAgentInstruction: "If this task needs multiple phases (research, build, verify), propose a plan first."
|
|
6778
7017
|
},
|
|
6779
7018
|
ultracode: {
|
|
6780
7019
|
tier: "best",
|
|
6781
7020
|
permissionMode: "acceptEdits",
|
|
6782
7021
|
maxTurns: 800,
|
|
6783
|
-
thinkingDirective:
|
|
7022
|
+
thinkingDirective: `Think step-by-step. Exhaustively verify every assumption against source code and documentation. Build worked examples to validate correctness. ${RED_TEAM_DIRECTIVE}`,
|
|
6784
7023
|
multiAgentInstruction: "Decompose this work into parallel research, build, and verification streams; use workflow orchestration where it helps."
|
|
6785
7024
|
}
|
|
6786
7025
|
};
|
|
@@ -6790,8 +7029,8 @@ var init_effort_mode_config = __esm({
|
|
|
6790
7029
|
|
|
6791
7030
|
// ../../scripts/virtual-office/model-registry.mjs
|
|
6792
7031
|
import fs7 from "node:fs";
|
|
6793
|
-
import
|
|
6794
|
-
import { fileURLToPath } from "node:url";
|
|
7032
|
+
import path13 from "node:path";
|
|
7033
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
6795
7034
|
function uniqueModels(models = []) {
|
|
6796
7035
|
return [...new Set(models.map((model) => String(model || "").trim()).filter(Boolean))];
|
|
6797
7036
|
}
|
|
@@ -6913,7 +7152,7 @@ function readCache(cacheFile = DEFAULT_CACHE_FILE, nowMs = Date.now(), ttlMs = D
|
|
|
6913
7152
|
}
|
|
6914
7153
|
}
|
|
6915
7154
|
function writeCache(cacheFile = DEFAULT_CACHE_FILE, payload) {
|
|
6916
|
-
fs7.mkdirSync(
|
|
7155
|
+
fs7.mkdirSync(path13.dirname(cacheFile), { recursive: true });
|
|
6917
7156
|
fs7.writeFileSync(cacheFile, JSON.stringify(payload, null, 2));
|
|
6918
7157
|
}
|
|
6919
7158
|
async function fetchRegistryCatalog({
|
|
@@ -6971,10 +7210,10 @@ var __dirname, ROOT, DEFAULT_CACHE_DIR, DEFAULT_CACHE_FILE, DEFAULT_TTL_MS3, ANT
|
|
|
6971
7210
|
var init_model_registry = __esm({
|
|
6972
7211
|
"../../scripts/virtual-office/model-registry.mjs"() {
|
|
6973
7212
|
"use strict";
|
|
6974
|
-
__dirname =
|
|
6975
|
-
ROOT =
|
|
6976
|
-
DEFAULT_CACHE_DIR =
|
|
6977
|
-
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");
|
|
6978
7217
|
DEFAULT_TTL_MS3 = 60 * 60 * 1e3;
|
|
6979
7218
|
ANTHROPIC_API_VERSION = "2023-06-01";
|
|
6980
7219
|
FAMILY_DEFINITIONS = {
|
|
@@ -7175,10 +7414,18 @@ var init_model_router = __esm({
|
|
|
7175
7414
|
}
|
|
7176
7415
|
};
|
|
7177
7416
|
AGENT_MODEL_COMPATIBILITY = {
|
|
7178
|
-
|
|
7179
|
-
|
|
7180
|
-
|
|
7181
|
-
|
|
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 || ""))
|
|
7182
7429
|
};
|
|
7183
7430
|
}
|
|
7184
7431
|
});
|
|
@@ -7551,9 +7798,9 @@ var init_classify_task = __esm({
|
|
|
7551
7798
|
});
|
|
7552
7799
|
|
|
7553
7800
|
// ../../scripts/virtual-office/code-runner/auto-router/effort-policy.mjs
|
|
7554
|
-
import { readFileSync as
|
|
7801
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
7555
7802
|
import { homedir as homedir5 } from "node:os";
|
|
7556
|
-
import { join as
|
|
7803
|
+
import { join as join7 } from "node:path";
|
|
7557
7804
|
function difficultyToRung(difficulty, thresholds) {
|
|
7558
7805
|
const b = thresholds.rungBounds;
|
|
7559
7806
|
if (difficulty >= b.R5) return "R5";
|
|
@@ -7578,9 +7825,9 @@ function operatorTierToRung(tier, difficulty, thresholds) {
|
|
|
7578
7825
|
if (tier === "best" && difficulty >= thresholds.rungBounds.R5) return "R5";
|
|
7579
7826
|
return base;
|
|
7580
7827
|
}
|
|
7581
|
-
function readCodexModelsCache({ path:
|
|
7828
|
+
function readCodexModelsCache({ path: path16 = DEFAULT_CODEX_MODELS_CACHE, read = readFileSync5 } = {}) {
|
|
7582
7829
|
try {
|
|
7583
|
-
const parsed = JSON.parse(read(
|
|
7830
|
+
const parsed = JSON.parse(read(path16, "utf8"));
|
|
7584
7831
|
return Array.isArray(parsed?.models) ? parsed : null;
|
|
7585
7832
|
} catch {
|
|
7586
7833
|
return null;
|
|
@@ -7630,23 +7877,148 @@ var init_effort_policy = __esm({
|
|
|
7630
7877
|
init_meta_model_catalog();
|
|
7631
7878
|
RUNG_ORDER = ["R1", "R2", "R3", "R4", "R5"];
|
|
7632
7879
|
rungIndex = (rung) => RUNG_ORDER.indexOf(rung);
|
|
7633
|
-
DEFAULT_CODEX_MODELS_CACHE =
|
|
7880
|
+
DEFAULT_CODEX_MODELS_CACHE = join7(homedir5(), ".codex", "models_cache.json");
|
|
7881
|
+
}
|
|
7882
|
+
});
|
|
7883
|
+
|
|
7884
|
+
// ../../scripts/virtual-office/code-runner/auto-router/role-cost-shadow.mjs
|
|
7885
|
+
function attributeRoleCosts({ plannerTokens, workerTokens, plannerModelRate, workerModelRate } = {}) {
|
|
7886
|
+
const inputs = { plannerTokens, workerTokens, plannerModelRate, workerModelRate };
|
|
7887
|
+
for (const [name, value] of Object.entries(inputs)) {
|
|
7888
|
+
if (!isNonNegativeFinite(value)) {
|
|
7889
|
+
return {
|
|
7890
|
+
valid: false,
|
|
7891
|
+
reason: `invalid ${name} (${String(value)}) \u2014 fail-open, no attribution`,
|
|
7892
|
+
...EMPTY_ATTRIBUTION
|
|
7893
|
+
};
|
|
7894
|
+
}
|
|
7895
|
+
}
|
|
7896
|
+
const plannerCostUsd = plannerTokens * plannerModelRate;
|
|
7897
|
+
const workerCostUsd = workerTokens * workerModelRate;
|
|
7898
|
+
const totalCostUsd = plannerCostUsd + workerCostUsd;
|
|
7899
|
+
const totalTokens = plannerTokens + workerTokens;
|
|
7900
|
+
const plannerCostShare = totalCostUsd > 0 ? plannerCostUsd / totalCostUsd : null;
|
|
7901
|
+
const workerCostShare = totalCostUsd > 0 ? workerCostUsd / totalCostUsd : null;
|
|
7902
|
+
const plannerTokenShare = totalTokens > 0 ? plannerTokens / totalTokens : null;
|
|
7903
|
+
const workerTokenShare = totalTokens > 0 ? workerTokens / totalTokens : null;
|
|
7904
|
+
const plannerCostShareRatio = plannerCostShare !== null && plannerTokenShare !== null && plannerTokenShare > 0 ? plannerCostShare / plannerTokenShare : null;
|
|
7905
|
+
return {
|
|
7906
|
+
valid: true,
|
|
7907
|
+
plannerCostUsd,
|
|
7908
|
+
workerCostUsd,
|
|
7909
|
+
totalCostUsd,
|
|
7910
|
+
plannerCostShare,
|
|
7911
|
+
workerCostShare,
|
|
7912
|
+
plannerTokenShare,
|
|
7913
|
+
workerTokenShare,
|
|
7914
|
+
plannerCostShareRatio
|
|
7915
|
+
};
|
|
7916
|
+
}
|
|
7917
|
+
function shadowFanOutGate({ taskClass, disagreementSignal, confidence, panelSize } = {}, { thresholds } = {}) {
|
|
7918
|
+
const cfg = { ...DEFAULT_SHADOW_FAN_OUT, ...thresholds?.shadowFanOut ?? {} };
|
|
7919
|
+
const defaultsUsed = !thresholds?.shadowFanOut;
|
|
7920
|
+
const no = (reason) => ({ wouldFanOut: false, reason, criterion: SHADOW_CRITERION, defaultsUsed });
|
|
7921
|
+
if (!isUnitInterval(disagreementSignal)) {
|
|
7922
|
+
return no(`invalid disagreementSignal (${String(disagreementSignal)}) \u2014 fail-open, single-model`);
|
|
7923
|
+
}
|
|
7924
|
+
if (!isUnitInterval(confidence)) {
|
|
7925
|
+
return no(`invalid confidence (${String(confidence)}) \u2014 fail-open, single-model`);
|
|
7926
|
+
}
|
|
7927
|
+
const size = panelSize === void 0 || panelSize === null ? cfg.defaultPanelSize : panelSize;
|
|
7928
|
+
if (!Number.isInteger(size) || size < 1) {
|
|
7929
|
+
return no(`invalid panelSize (${String(panelSize)}) \u2014 fail-open, single-model`);
|
|
7930
|
+
}
|
|
7931
|
+
const never = Array.isArray(cfg.neverFanOutClasses) ? cfg.neverFanOutClasses : [];
|
|
7932
|
+
if (typeof taskClass === "string" && never.includes(taskClass)) {
|
|
7933
|
+
return no(`class=${taskClass} in neverFanOutClasses \u2014 fan-out never pays on low-stakes classes`);
|
|
7934
|
+
}
|
|
7935
|
+
if (size < cfg.minPanelSize) {
|
|
7936
|
+
return no(`panelSize ${size} < ${cfg.minPanelSize} \u2014 too small to contribute independent signal`);
|
|
7937
|
+
}
|
|
7938
|
+
if (disagreementSignal < cfg.minDisagreementSignal) {
|
|
7939
|
+
return no(`disagreement ${disagreementSignal} < ${cfg.minDisagreementSignal} \u2014 extra models would confirm, not inform`);
|
|
7940
|
+
}
|
|
7941
|
+
if (confidence > cfg.maxSingleModelConfidence) {
|
|
7942
|
+
return no(`confidence ${confidence} > ${cfg.maxSingleModelConfidence} \u2014 single model already confident; fan-out adds cost, not signal`);
|
|
7943
|
+
}
|
|
7944
|
+
return {
|
|
7945
|
+
wouldFanOut: true,
|
|
7946
|
+
reason: `disagreement ${disagreementSignal} \u2265 ${cfg.minDisagreementSignal} AND confidence ${confidence} \u2264 ${cfg.maxSingleModelConfidence} (panel ${size})`,
|
|
7947
|
+
criterion: SHADOW_CRITERION,
|
|
7948
|
+
defaultsUsed
|
|
7949
|
+
};
|
|
7950
|
+
}
|
|
7951
|
+
function buildShadowRecords({ decision, task = {}, thresholds, roleCostInputs = null } = {}) {
|
|
7952
|
+
if (!decision || typeof decision !== "object") return [];
|
|
7953
|
+
const base = {
|
|
7954
|
+
shadow: true,
|
|
7955
|
+
routerVersion: decision.routerVersion ?? null,
|
|
7956
|
+
ts: decision.ts ?? null,
|
|
7957
|
+
taskId: task?.id ?? null
|
|
7958
|
+
};
|
|
7959
|
+
const roleCost = roleCostInputs ? attributeRoleCosts(roleCostInputs) : { valid: false, reason: "planner/worker token telemetry unavailable at routing time", ...EMPTY_ATTRIBUTION };
|
|
7960
|
+
const hasTaskSignal = typeof task?.disagreement_signal === "number";
|
|
7961
|
+
const disagreementSignal = hasTaskSignal ? task.disagreement_signal : typeof decision.difficulty === "number" ? decision.difficulty / 100 : void 0;
|
|
7962
|
+
const gate = shadowFanOutGate(
|
|
7963
|
+
{
|
|
7964
|
+
taskClass: decision.taskClass,
|
|
7965
|
+
disagreementSignal,
|
|
7966
|
+
confidence: decision.confidence,
|
|
7967
|
+
panelSize: task?.panel_size
|
|
7968
|
+
},
|
|
7969
|
+
{ thresholds }
|
|
7970
|
+
);
|
|
7971
|
+
return [
|
|
7972
|
+
{ kind: "shadow_role_cost", ...base, roleCost },
|
|
7973
|
+
{
|
|
7974
|
+
kind: "shadow_fan_out",
|
|
7975
|
+
...base,
|
|
7976
|
+
disagreementSignal: disagreementSignal ?? null,
|
|
7977
|
+
disagreementSource: hasTaskSignal ? "task.disagreement_signal" : "difficulty-proxy-v0",
|
|
7978
|
+
...gate
|
|
7979
|
+
}
|
|
7980
|
+
];
|
|
7981
|
+
}
|
|
7982
|
+
var SHADOW_CRITERION, DEFAULT_SHADOW_FAN_OUT, isNonNegativeFinite, isUnitInterval, EMPTY_ATTRIBUTION;
|
|
7983
|
+
var init_role_cost_shadow = __esm({
|
|
7984
|
+
"../../scripts/virtual-office/code-runner/auto-router/role-cost-shadow.mjs"() {
|
|
7985
|
+
"use strict";
|
|
7986
|
+
SHADOW_CRITERION = "info-bottleneck-v0";
|
|
7987
|
+
DEFAULT_SHADOW_FAN_OUT = Object.freeze({
|
|
7988
|
+
minDisagreementSignal: 0.4,
|
|
7989
|
+
maxSingleModelConfidence: 0.6,
|
|
7990
|
+
minPanelSize: 2,
|
|
7991
|
+
defaultPanelSize: 3,
|
|
7992
|
+
neverFanOutClasses: Object.freeze(["chore", "docs"])
|
|
7993
|
+
});
|
|
7994
|
+
isNonNegativeFinite = (n) => typeof n === "number" && Number.isFinite(n) && n >= 0;
|
|
7995
|
+
isUnitInterval = (n) => isNonNegativeFinite(n) && n <= 1;
|
|
7996
|
+
EMPTY_ATTRIBUTION = Object.freeze({
|
|
7997
|
+
plannerCostUsd: null,
|
|
7998
|
+
workerCostUsd: null,
|
|
7999
|
+
totalCostUsd: null,
|
|
8000
|
+
plannerCostShare: null,
|
|
8001
|
+
workerCostShare: null,
|
|
8002
|
+
plannerTokenShare: null,
|
|
8003
|
+
workerTokenShare: null,
|
|
8004
|
+
plannerCostShareRatio: null
|
|
8005
|
+
});
|
|
7634
8006
|
}
|
|
7635
8007
|
});
|
|
7636
8008
|
|
|
7637
8009
|
// ../../scripts/virtual-office/code-runner/auto-router/auto-router.mjs
|
|
7638
|
-
import { readFileSync as
|
|
8010
|
+
import { readFileSync as readFileSync6, appendFileSync as appendFileSync2, mkdirSync as mkdirSync5 } from "node:fs";
|
|
7639
8011
|
import { homedir as homedir6 } from "node:os";
|
|
7640
|
-
import { join as
|
|
7641
|
-
import { fileURLToPath as
|
|
8012
|
+
import { join as join8, dirname as dirname5 } from "node:path";
|
|
8013
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
7642
8014
|
function getAutoRouterMode(env2 = process.env) {
|
|
7643
8015
|
const raw = String(env2.VO_CODE_RUNNER_AUTO_ROUTER || "").trim().toLowerCase();
|
|
7644
8016
|
return MODES.has(raw) ? raw : "off";
|
|
7645
8017
|
}
|
|
7646
8018
|
function loadThresholds() {
|
|
7647
8019
|
if (!cachedThresholds) {
|
|
7648
|
-
const here =
|
|
7649
|
-
cachedThresholds = JSON.parse(
|
|
8020
|
+
const here = dirname5(fileURLToPath4(import.meta.url));
|
|
8021
|
+
cachedThresholds = JSON.parse(readFileSync6(join8(here, "thresholds.json"), "utf8"));
|
|
7650
8022
|
}
|
|
7651
8023
|
return cachedThresholds;
|
|
7652
8024
|
}
|
|
@@ -7712,16 +8084,36 @@ function formatDecisionReason(decision, maxLen = 480) {
|
|
|
7712
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("; ")}`;
|
|
7713
8085
|
return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
|
|
7714
8086
|
}
|
|
7715
|
-
|
|
8087
|
+
function appendDecisionFallback(decision, { path: path16 = DECISION_FALLBACK_PATH, append = appendFileSync2, mkdir: mkdir3 = mkdirSync5, task, thresholds, roleCostInputs } = {}) {
|
|
8088
|
+
try {
|
|
8089
|
+
mkdir3(dirname5(path16), { recursive: true });
|
|
8090
|
+
append(path16, `${JSON.stringify(decision)}
|
|
8091
|
+
`, "utf8");
|
|
8092
|
+
if (isRouterDecision(decision)) {
|
|
8093
|
+
try {
|
|
8094
|
+
const records = buildShadowRecords({ decision, task, thresholds: thresholds || loadThresholds(), roleCostInputs });
|
|
8095
|
+
for (const record of records) append(path16, `${JSON.stringify(record)}
|
|
8096
|
+
`, "utf8");
|
|
8097
|
+
} catch {
|
|
8098
|
+
}
|
|
8099
|
+
}
|
|
8100
|
+
return true;
|
|
8101
|
+
} catch {
|
|
8102
|
+
return false;
|
|
8103
|
+
}
|
|
8104
|
+
}
|
|
8105
|
+
var ROUTER_VERSION, DECISION_FALLBACK_PATH, MODES, cachedThresholds, isRouterDecision;
|
|
7716
8106
|
var init_auto_router = __esm({
|
|
7717
8107
|
"../../scripts/virtual-office/code-runner/auto-router/auto-router.mjs"() {
|
|
7718
8108
|
"use strict";
|
|
7719
8109
|
init_classify_task();
|
|
7720
8110
|
init_effort_policy();
|
|
8111
|
+
init_role_cost_shadow();
|
|
7721
8112
|
ROUTER_VERSION = "0.1.0";
|
|
7722
|
-
DECISION_FALLBACK_PATH =
|
|
8113
|
+
DECISION_FALLBACK_PATH = join8(homedir6(), ".claude", "vo-auto-router-decisions.jsonl");
|
|
7723
8114
|
MODES = /* @__PURE__ */ new Set(["off", "shadow", "on"]);
|
|
7724
8115
|
cachedThresholds = null;
|
|
8116
|
+
isRouterDecision = (d) => Boolean(d && typeof d === "object" && typeof d.taskClass === "string" && typeof d.confidence === "number");
|
|
7725
8117
|
}
|
|
7726
8118
|
});
|
|
7727
8119
|
|
|
@@ -7753,7 +8145,7 @@ function resolveAgentEffort({ agent, tier, env: env2, applying, decision }) {
|
|
|
7753
8145
|
}
|
|
7754
8146
|
return applying ? decision.effort ?? null : null;
|
|
7755
8147
|
}
|
|
7756
|
-
async function resolveEffortDispatch({ client, task, agent = "claude", env: env2, basePrompt, resolveModel = resolveTaskModel, route = routeTask }) {
|
|
8148
|
+
async function resolveEffortDispatch({ client, task, agent = "claude", env: env2, basePrompt, resolveModel = resolveTaskModel, route = routeTask, appendDecision = appendDecisionFallback }) {
|
|
7757
8149
|
const dispatchMode = task.dispatch_mode ?? await client.getDispatchMode().catch(() => "standard");
|
|
7758
8150
|
const effortConfig = resolveEffortMode(dispatchMode);
|
|
7759
8151
|
const routerMode = getAutoRouterMode(env2);
|
|
@@ -7772,6 +8164,12 @@ async function resolveEffortDispatch({ client, task, agent = "claude", env: env2
|
|
|
7772
8164
|
{ agent }
|
|
7773
8165
|
);
|
|
7774
8166
|
const effort = resolveAgentEffort({ agent, tier, env: env2, applying, decision });
|
|
8167
|
+
if (decision) {
|
|
8168
|
+
try {
|
|
8169
|
+
appendDecision(decision, { task });
|
|
8170
|
+
} catch {
|
|
8171
|
+
}
|
|
8172
|
+
}
|
|
7775
8173
|
return {
|
|
7776
8174
|
dispatchMode,
|
|
7777
8175
|
routerMode,
|
|
@@ -7851,7 +8249,7 @@ function makeReconnectBackoff({
|
|
|
7851
8249
|
jitter = 0.2,
|
|
7852
8250
|
log: log3 = () => {
|
|
7853
8251
|
},
|
|
7854
|
-
random =
|
|
8252
|
+
random = secureUnitRandom
|
|
7855
8253
|
} = {}) {
|
|
7856
8254
|
let consecutiveFailures = 0;
|
|
7857
8255
|
return {
|
|
@@ -7911,6 +8309,7 @@ function installProcessSafetyNet({ log: log3 = () => {
|
|
|
7911
8309
|
var init_reconnect_backoff = __esm({
|
|
7912
8310
|
"../../scripts/virtual-office/code-runner/reconnect-backoff.mjs"() {
|
|
7913
8311
|
"use strict";
|
|
8312
|
+
init_secure_random();
|
|
7914
8313
|
}
|
|
7915
8314
|
});
|
|
7916
8315
|
|
|
@@ -8025,7 +8424,7 @@ var init_agent_process_env = __esm({
|
|
|
8025
8424
|
// ../../scripts/virtual-office/code-runner/isolation-audit.mjs
|
|
8026
8425
|
import fs8 from "node:fs";
|
|
8027
8426
|
import fsp9 from "node:fs/promises";
|
|
8028
|
-
import
|
|
8427
|
+
import path14 from "node:path";
|
|
8029
8428
|
async function defaultRun(command, args, cwd, options = {}) {
|
|
8030
8429
|
return runProcess2(command, args, { cwd, timeout: 6e4, ...options });
|
|
8031
8430
|
}
|
|
@@ -8038,7 +8437,7 @@ async function canonicalRootForWorktree(worktreeDir, run) {
|
|
|
8038
8437
|
"--path-format=absolute",
|
|
8039
8438
|
"--git-common-dir"
|
|
8040
8439
|
])).trim();
|
|
8041
|
-
const root =
|
|
8440
|
+
const root = path14.dirname(commonDir);
|
|
8042
8441
|
return samePath2(root, worktreeDir) ? null : root;
|
|
8043
8442
|
}
|
|
8044
8443
|
async function snapshot(root, run) {
|
|
@@ -8080,21 +8479,21 @@ async function changedPaths(root, run) {
|
|
|
8080
8479
|
}
|
|
8081
8480
|
async function quarantineCanonicalWrites({ baseline, worktreeDir, taskId, run, now }) {
|
|
8082
8481
|
const paths = await changedPaths(baseline.root, run);
|
|
8083
|
-
const quarantineDir =
|
|
8084
|
-
|
|
8482
|
+
const quarantineDir = path14.join(
|
|
8483
|
+
path14.dirname(worktreeDir),
|
|
8085
8484
|
".canonical-recovery",
|
|
8086
8485
|
`${String(taskId || "unknown").replace(/[^a-z0-9-]/gi, "-")}-${now().toISOString().replace(/[:.]/g, "-")}`
|
|
8087
8486
|
);
|
|
8088
8487
|
await fsp9.mkdir(quarantineDir, { recursive: true });
|
|
8089
8488
|
const patch = await git2(run, baseline.root, ["diff", "--binary", "HEAD"], { raw: true });
|
|
8090
|
-
await fsp9.writeFile(
|
|
8489
|
+
await fsp9.writeFile(path14.join(quarantineDir, "tracked.patch"), patch, "utf8");
|
|
8091
8490
|
for (const relative of paths.untracked) {
|
|
8092
|
-
const source =
|
|
8093
|
-
const target =
|
|
8094
|
-
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 });
|
|
8095
8494
|
await fsp9.copyFile(source, target);
|
|
8096
8495
|
}
|
|
8097
|
-
await fsp9.writeFile(
|
|
8496
|
+
await fsp9.writeFile(path14.join(quarantineDir, "manifest.json"), `${JSON.stringify({
|
|
8098
8497
|
taskId,
|
|
8099
8498
|
canonicalRoot: baseline.root,
|
|
8100
8499
|
canonicalHead: baseline.head,
|
|
@@ -8116,8 +8515,8 @@ async function restoreExactCanonicalPaths(baseline, evidence, run) {
|
|
|
8116
8515
|
]);
|
|
8117
8516
|
}
|
|
8118
8517
|
for (const relative of evidence.untracked) {
|
|
8119
|
-
const target =
|
|
8120
|
-
const prefix = `${
|
|
8518
|
+
const target = path14.resolve(baseline.root, relative);
|
|
8519
|
+
const prefix = `${path14.resolve(baseline.root)}${path14.sep}`;
|
|
8121
8520
|
if (!target.startsWith(prefix) || !fs8.existsSync(target)) continue;
|
|
8122
8521
|
await fsp9.rm(target, { force: true });
|
|
8123
8522
|
}
|
|
@@ -8154,7 +8553,7 @@ var init_isolation_audit = __esm({
|
|
|
8154
8553
|
init_process_runner2();
|
|
8155
8554
|
splitZ2 = (value) => String(value || "").split("\0").map((item) => item.trim()).filter(Boolean);
|
|
8156
8555
|
samePath2 = (left, right) => {
|
|
8157
|
-
const [a, b] = [left, right].map((value) =>
|
|
8556
|
+
const [a, b] = [left, right].map((value) => path14.resolve(value));
|
|
8158
8557
|
return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
8159
8558
|
};
|
|
8160
8559
|
}
|
|
@@ -8163,7 +8562,7 @@ var init_isolation_audit = __esm({
|
|
|
8163
8562
|
// ../../scripts/virtual-office/code-runner/recovery-ledger.mjs
|
|
8164
8563
|
import fs9 from "node:fs";
|
|
8165
8564
|
import fsp10 from "node:fs/promises";
|
|
8166
|
-
import
|
|
8565
|
+
import path15 from "node:path";
|
|
8167
8566
|
function recoveryTaskId(prompt) {
|
|
8168
8567
|
const match = String(prompt || "").match(/VO_RECOVERY_FROM_CODE_TASK:\s*([0-9a-f-]{36})/i);
|
|
8169
8568
|
return match ? match[1].toLowerCase() : null;
|
|
@@ -8177,10 +8576,10 @@ function cloneLeaf(repo) {
|
|
|
8177
8576
|
function recoveryLedgerCandidates(repo, clonesRoot2) {
|
|
8178
8577
|
const leaf = cloneLeaf(repo);
|
|
8179
8578
|
if (!leaf || !clonesRoot2) return [];
|
|
8180
|
-
const canonical =
|
|
8579
|
+
const canonical = path15.join(clonesRoot2, leaf);
|
|
8181
8580
|
return [
|
|
8182
|
-
|
|
8183
|
-
|
|
8581
|
+
path15.join(clonesRoot2, ".agent-worktrees", leaf, "recovery-ledger.jsonl"),
|
|
8582
|
+
path15.join(canonical, ".agent-worktrees", "recovery-ledger.jsonl")
|
|
8184
8583
|
];
|
|
8185
8584
|
}
|
|
8186
8585
|
async function readLedger(file, readFile4) {
|
|
@@ -8302,6 +8701,9 @@ var init_recovery_ledger = __esm({
|
|
|
8302
8701
|
});
|
|
8303
8702
|
|
|
8304
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
|
+
}
|
|
8305
8707
|
function explicitTaskOutcome(summary) {
|
|
8306
8708
|
const matches = [...String(summary || "").matchAll(/ALGOSUITE_TASK_OUTCOME\s*:\s*(NO_CHANGES|BLOCKED|FAILED)\b/gi)];
|
|
8307
8709
|
return matches.at(-1)?.[1]?.toUpperCase() || "";
|
|
@@ -8339,16 +8741,78 @@ function decideNoChangesTerminalStatus({ partial, run = {}, maxTurns } = {}) {
|
|
|
8339
8741
|
result: "inconclusive_max_turns"
|
|
8340
8742
|
};
|
|
8341
8743
|
}
|
|
8744
|
+
const cause = String(run.summary || "").trim();
|
|
8342
8745
|
return {
|
|
8343
8746
|
status: "failed",
|
|
8344
|
-
message: "agent made no file changes",
|
|
8345
|
-
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)
|
|
8346
8749
|
};
|
|
8347
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
|
+
}
|
|
8348
8809
|
var RESULT_LIMIT;
|
|
8349
8810
|
var init_no_changes_terminal_status = __esm({
|
|
8350
8811
|
"../../scripts/virtual-office/code-runner/no-changes-terminal-status.mjs"() {
|
|
8351
8812
|
"use strict";
|
|
8813
|
+
init_process_runner2();
|
|
8814
|
+
init_publish();
|
|
8815
|
+
init_superseded_pr_source();
|
|
8352
8816
|
RESULT_LIMIT = 2e3;
|
|
8353
8817
|
}
|
|
8354
8818
|
});
|
|
@@ -8360,7 +8824,7 @@ __export(code_runner_daemon_exports, {
|
|
|
8360
8824
|
});
|
|
8361
8825
|
import os4 from "node:os";
|
|
8362
8826
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
8363
|
-
import { fileURLToPath as
|
|
8827
|
+
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
8364
8828
|
function log2(msg) {
|
|
8365
8829
|
console.log(`[code-runner ${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}`);
|
|
8366
8830
|
}
|
|
@@ -8504,14 +8968,18 @@ async function processOneTask(client, task, cfg) {
|
|
|
8504
8968
|
log2(`task ${id}: dropped ${scratch.length} scratch file(s): ${scratch.join(", ")}`);
|
|
8505
8969
|
}
|
|
8506
8970
|
if (files.length === 0) {
|
|
8507
|
-
|
|
8508
|
-
|
|
8509
|
-
|
|
8510
|
-
|
|
8511
|
-
|
|
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
|
|
8512
8982
|
});
|
|
8513
|
-
if (terminal.status === "no_changes_needed") log2(`task ${id}: agent completed successfully with no changes (already fixed)`);
|
|
8514
|
-
else if (!partial) log2(`task ${id}: agent reported a blocker with no changes; preserving failure honestly`);
|
|
8515
8983
|
return;
|
|
8516
8984
|
}
|
|
8517
8985
|
const fresh = await client.getTask(id).catch(() => null);
|
|
@@ -8718,7 +9186,7 @@ var init_code_runner_daemon = __esm({
|
|
|
8718
9186
|
sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
8719
9187
|
numOrUndef = (x) => typeof x === "number" ? x : void 0;
|
|
8720
9188
|
safeProgress = makeSafeProgress(log2);
|
|
8721
|
-
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).
|
|
8722
9190
|
import.meta.url.endsWith("code-runner-daemon.mjs");
|
|
8723
9191
|
if (invokedDirectly) {
|
|
8724
9192
|
const once2 = process.argv.includes("--once");
|