@algosuite/vo-mcp 0.2.0-beta.28 → 0.2.0-beta.29
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/agent-auth-probe-cli.mjs +65 -35
- package/dist/cli.js +476 -42
- package/dist/cli.js.map +4 -4
- package/dist/index.js +467 -33
- package/dist/index.js.map +4 -4
- package/dist/runner-cli.js +247 -60
- package/dist/runner-cli.js.map +4 -4
- package/package.json +1 -1
package/dist/runner-cli.js
CHANGED
|
@@ -3001,6 +3001,62 @@ var init_windows_claude_launch = __esm({
|
|
|
3001
3001
|
}
|
|
3002
3002
|
});
|
|
3003
3003
|
|
|
3004
|
+
// ../../scripts/virtual-office/code-runner/claude-credential-choice.mjs
|
|
3005
|
+
function isTruthyFlag(v) {
|
|
3006
|
+
const s = String(v ?? "").trim().toLowerCase();
|
|
3007
|
+
return s === "1" || s === "true" || s === "yes" || s === "on";
|
|
3008
|
+
}
|
|
3009
|
+
function wantsLogin(env2) {
|
|
3010
|
+
return isTruthyFlag(env2[CLAUDE_PREFER_LOGIN_ENV]) || isTruthyFlag(env2[PREFER_LOGIN_ENV]);
|
|
3011
|
+
}
|
|
3012
|
+
function wantsKey(env2) {
|
|
3013
|
+
return isTruthyFlag(env2[CLAUDE_PREFER_KEY_ENV]) || isTruthyFlag(env2[PREFER_KEY_ENV]);
|
|
3014
|
+
}
|
|
3015
|
+
function classifyClaudeCredential(baseEnv = {}, { getKey, probeLogin } = {}) {
|
|
3016
|
+
const preferKey = wantsKey(baseEnv);
|
|
3017
|
+
if (!preferKey && wantsLogin(baseEnv)) {
|
|
3018
|
+
return { source: CLAUDE_CREDENTIAL_SOURCE.PREFER_LOGIN, key: null };
|
|
3019
|
+
}
|
|
3020
|
+
if (baseEnv.ANTHROPIC_API_KEY) {
|
|
3021
|
+
return { source: CLAUDE_CREDENTIAL_SOURCE.ENV_KEY, key: null };
|
|
3022
|
+
}
|
|
3023
|
+
const key = getKey();
|
|
3024
|
+
if (!key) {
|
|
3025
|
+
return { source: CLAUDE_CREDENTIAL_SOURCE.NO_KEY, key: null };
|
|
3026
|
+
}
|
|
3027
|
+
if (preferKey) {
|
|
3028
|
+
return { source: CLAUDE_CREDENTIAL_SOURCE.KEYCHAIN_PREFER_KEY, key };
|
|
3029
|
+
}
|
|
3030
|
+
if (probeLogin() === true) {
|
|
3031
|
+
return { source: CLAUDE_CREDENTIAL_SOURCE.SUBSCRIPTION_WINS, key: null };
|
|
3032
|
+
}
|
|
3033
|
+
return { source: CLAUDE_CREDENTIAL_SOURCE.KEYCHAIN, key };
|
|
3034
|
+
}
|
|
3035
|
+
var PREFER_LOGIN_ENV, CLAUDE_PREFER_LOGIN_ENV, PREFER_KEY_ENV, CLAUDE_PREFER_KEY_ENV, CLAUDE_CREDENTIAL_SOURCE;
|
|
3036
|
+
var init_claude_credential_choice = __esm({
|
|
3037
|
+
"../../scripts/virtual-office/code-runner/claude-credential-choice.mjs"() {
|
|
3038
|
+
"use strict";
|
|
3039
|
+
PREFER_LOGIN_ENV = "VO_RUNNER_PREFER_LOGIN";
|
|
3040
|
+
CLAUDE_PREFER_LOGIN_ENV = "VO_RUNNER_CLAUDE_PREFER_LOGIN";
|
|
3041
|
+
PREFER_KEY_ENV = "VO_RUNNER_PREFER_KEY";
|
|
3042
|
+
CLAUDE_PREFER_KEY_ENV = "VO_RUNNER_CLAUDE_PREFER_KEY";
|
|
3043
|
+
CLAUDE_CREDENTIAL_SOURCE = Object.freeze({
|
|
3044
|
+
/** PREFER_LOGIN set (and not overridden): any API key is ignored. */
|
|
3045
|
+
PREFER_LOGIN: "prefer_login",
|
|
3046
|
+
/** An explicit ANTHROPIC_API_KEY in the environment — the manual override. */
|
|
3047
|
+
ENV_KEY: "env_key",
|
|
3048
|
+
/** No key anywhere; the spawn falls through to the login session. */
|
|
3049
|
+
NO_KEY: "no_key",
|
|
3050
|
+
/** A stored key, used because the operator explicitly opted out of tier 1. */
|
|
3051
|
+
KEYCHAIN_PREFER_KEY: "keychain_prefer_key",
|
|
3052
|
+
/** A stored key exists but a proven live subscription outranks it. */
|
|
3053
|
+
SUBSCRIPTION_WINS: "subscription_wins",
|
|
3054
|
+
/** A stored key, used because no live subscription was proven. */
|
|
3055
|
+
KEYCHAIN: "keychain"
|
|
3056
|
+
});
|
|
3057
|
+
}
|
|
3058
|
+
});
|
|
3059
|
+
|
|
3004
3060
|
// ../../scripts/virtual-office/code-runner/anthropic-key-store.mjs
|
|
3005
3061
|
import { createRequire as createRequire2 } from "node:module";
|
|
3006
3062
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
@@ -3022,46 +3078,22 @@ function getAnthropicKey({ EntryCtor = defaultEntryCtor() } = {}) {
|
|
|
3022
3078
|
return null;
|
|
3023
3079
|
}
|
|
3024
3080
|
}
|
|
3025
|
-
function isTruthyFlag(v) {
|
|
3026
|
-
const s = String(v ?? "").trim().toLowerCase();
|
|
3027
|
-
return s === "1" || s === "true" || s === "yes" || s === "on";
|
|
3028
|
-
}
|
|
3029
|
-
function wantsLogin(env2) {
|
|
3030
|
-
return isTruthyFlag(env2[CLAUDE_PREFER_LOGIN_ENV]) || isTruthyFlag(env2[PREFER_LOGIN_ENV]);
|
|
3031
|
-
}
|
|
3032
|
-
function wantsKey(env2) {
|
|
3033
|
-
return isTruthyFlag(env2[CLAUDE_PREFER_KEY_ENV]) || isTruthyFlag(env2[PREFER_KEY_ENV]);
|
|
3034
|
-
}
|
|
3035
3081
|
function withAnthropicKey(baseEnv = {}, { getKey = getAnthropicKey, probeLogin = probeClaudeLoginState } = {}) {
|
|
3036
|
-
const
|
|
3037
|
-
|
|
3038
|
-
|
|
3082
|
+
const { source, key } = classifyClaudeCredential(baseEnv, { getKey, probeLogin });
|
|
3083
|
+
const next = { ...baseEnv };
|
|
3084
|
+
if (source === CLAUDE_CREDENTIAL_SOURCE.PREFER_LOGIN) {
|
|
3039
3085
|
delete next.ANTHROPIC_API_KEY;
|
|
3040
3086
|
return next;
|
|
3041
3087
|
}
|
|
3042
|
-
if (
|
|
3043
|
-
|
|
3044
|
-
if (!key) return { ...baseEnv };
|
|
3045
|
-
if (!preferKey && probeLogin() === true) return { ...baseEnv };
|
|
3046
|
-
return { ...baseEnv, ANTHROPIC_API_KEY: key };
|
|
3088
|
+
if (key !== null) next.ANTHROPIC_API_KEY = key;
|
|
3089
|
+
return next;
|
|
3047
3090
|
}
|
|
3048
3091
|
function claudeCostBasis(env2 = process.env) {
|
|
3049
3092
|
return String(env2.ANTHROPIC_API_KEY || "").trim() ? "vendor_billed" : "subscription_api_equivalent";
|
|
3050
3093
|
}
|
|
3051
3094
|
function describeAnthropicAuthSource(baseEnv = {}, { getKey = getAnthropicKey, probeLogin = probeClaudeLoginState } = {}) {
|
|
3052
|
-
const
|
|
3053
|
-
|
|
3054
|
-
return "claude auth login (VO_RUNNER_PREFER_LOGIN set \u2014 any API key ignored)";
|
|
3055
|
-
}
|
|
3056
|
-
if (baseEnv.ANTHROPIC_API_KEY) return "ANTHROPIC_API_KEY from environment";
|
|
3057
|
-
if (!getKey()) return "claude auth login session (no API key set)";
|
|
3058
|
-
if (preferKey) {
|
|
3059
|
-
return "ANTHROPIC_API_KEY from OS keychain (VO_RUNNER_PREFER_KEY set \u2014 subscription ignored)";
|
|
3060
|
-
}
|
|
3061
|
-
if (probeLogin() === true) {
|
|
3062
|
-
return "claude auth login session (subscription beats the stored keychain key)";
|
|
3063
|
-
}
|
|
3064
|
-
return "ANTHROPIC_API_KEY from OS keychain";
|
|
3095
|
+
const { source } = classifyClaudeCredential(baseEnv, { getKey, probeLogin });
|
|
3096
|
+
return AUTH_SOURCE_DESCRIPTION[source];
|
|
3065
3097
|
}
|
|
3066
3098
|
function augmentAuthError(summary) {
|
|
3067
3099
|
const s = String(summary ?? "");
|
|
@@ -3083,19 +3115,25 @@ function probeClaudeLoginState({
|
|
|
3083
3115
|
return null;
|
|
3084
3116
|
}
|
|
3085
3117
|
}
|
|
3086
|
-
var require2, KEY_SERVICE, KEY_ACCOUNT, _entryCtor, _loadTried,
|
|
3118
|
+
var require2, KEY_SERVICE, KEY_ACCOUNT, _entryCtor, _loadTried, AUTH_SOURCE_DESCRIPTION, AUTH_ERROR_RE;
|
|
3087
3119
|
var init_anthropic_key_store = __esm({
|
|
3088
3120
|
"../../scripts/virtual-office/code-runner/anthropic-key-store.mjs"() {
|
|
3089
3121
|
"use strict";
|
|
3090
3122
|
init_windows_claude_launch();
|
|
3123
|
+
init_claude_credential_choice();
|
|
3124
|
+
init_claude_credential_choice();
|
|
3091
3125
|
require2 = createRequire2(import.meta.url);
|
|
3092
3126
|
KEY_SERVICE = "algosuite-vo";
|
|
3093
3127
|
KEY_ACCOUNT = "anthropic-api-key";
|
|
3094
3128
|
_loadTried = false;
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3129
|
+
AUTH_SOURCE_DESCRIPTION = Object.freeze({
|
|
3130
|
+
[CLAUDE_CREDENTIAL_SOURCE.PREFER_LOGIN]: "claude auth login (VO_RUNNER_PREFER_LOGIN set \u2014 any API key ignored)",
|
|
3131
|
+
[CLAUDE_CREDENTIAL_SOURCE.ENV_KEY]: "ANTHROPIC_API_KEY from environment",
|
|
3132
|
+
[CLAUDE_CREDENTIAL_SOURCE.NO_KEY]: "claude auth login session (no API key set)",
|
|
3133
|
+
[CLAUDE_CREDENTIAL_SOURCE.KEYCHAIN_PREFER_KEY]: "ANTHROPIC_API_KEY from OS keychain (VO_RUNNER_PREFER_KEY set \u2014 subscription ignored)",
|
|
3134
|
+
[CLAUDE_CREDENTIAL_SOURCE.SUBSCRIPTION_WINS]: "claude auth login session (subscription beats the stored keychain key)",
|
|
3135
|
+
[CLAUDE_CREDENTIAL_SOURCE.KEYCHAIN]: "ANTHROPIC_API_KEY from OS keychain"
|
|
3136
|
+
});
|
|
3099
3137
|
AUTH_ERROR_RE = /\b401\b|invalid[^.]{0,24}(authentication|credential)|authentication_error|unauthorized|not[ _-]?authenticated/i;
|
|
3100
3138
|
}
|
|
3101
3139
|
});
|
|
@@ -7443,6 +7481,13 @@ function makeUsageRow({
|
|
|
7443
7481
|
}
|
|
7444
7482
|
return row;
|
|
7445
7483
|
}
|
|
7484
|
+
function readingAgeMs(row, nowMs = Date.now()) {
|
|
7485
|
+
const at = row && typeof row.captured_at === "string" ? row.captured_at : null;
|
|
7486
|
+
if (!at) return null;
|
|
7487
|
+
const ms = new Date(at).getTime();
|
|
7488
|
+
if (!Number.isFinite(ms)) return null;
|
|
7489
|
+
return Math.max(0, nowMs - ms);
|
|
7490
|
+
}
|
|
7446
7491
|
var clampPct, readJson, ACCOUNT_KEY_SALT;
|
|
7447
7492
|
var init_shared = __esm({
|
|
7448
7493
|
"../../scripts/virtual-office/code-runner/account-usage/shared.mjs"() {
|
|
@@ -7463,8 +7508,17 @@ var init_shared = __esm({
|
|
|
7463
7508
|
});
|
|
7464
7509
|
|
|
7465
7510
|
// ../../scripts/virtual-office/code-runner/account-usage/claude.mjs
|
|
7511
|
+
import fs7 from "node:fs";
|
|
7466
7512
|
import os3 from "node:os";
|
|
7467
7513
|
import path14 from "node:path";
|
|
7514
|
+
function fileCaptureTime(filePath, explicit, statFn) {
|
|
7515
|
+
if (typeof explicit === "string" && explicit) return explicit;
|
|
7516
|
+
try {
|
|
7517
|
+
return statFn(filePath).mtime.toISOString();
|
|
7518
|
+
} catch {
|
|
7519
|
+
return null;
|
|
7520
|
+
}
|
|
7521
|
+
}
|
|
7468
7522
|
function usageBaseUrl(env2 = process.env) {
|
|
7469
7523
|
const raw = env2.ANTHROPIC_BASE_URL || "https://api.anthropic.com";
|
|
7470
7524
|
return String(raw).replace(/\/+$/, "");
|
|
@@ -7570,7 +7624,12 @@ async function readClaudeOAuthUsage({
|
|
|
7570
7624
|
clearTimeout(timer);
|
|
7571
7625
|
}
|
|
7572
7626
|
}
|
|
7573
|
-
function readClaudeFileUsage({
|
|
7627
|
+
function readClaudeFileUsage({
|
|
7628
|
+
homeDir = os3.homedir(),
|
|
7629
|
+
read: rawRead = readJson,
|
|
7630
|
+
statFn = fs7.statSync,
|
|
7631
|
+
now = () => Date.now()
|
|
7632
|
+
} = {}) {
|
|
7574
7633
|
const read = (p) => {
|
|
7575
7634
|
try {
|
|
7576
7635
|
return rawRead(p);
|
|
@@ -7579,31 +7638,39 @@ function readClaudeFileUsage({ homeDir = os3.homedir(), read: rawRead = readJson
|
|
|
7579
7638
|
}
|
|
7580
7639
|
};
|
|
7581
7640
|
const accountId = readAccountId({ homeDir, read });
|
|
7582
|
-
const
|
|
7641
|
+
const fresh = (row) => {
|
|
7642
|
+
if (!row) return null;
|
|
7643
|
+
const age = readingAgeMs(row, now());
|
|
7644
|
+
if (age === null || age > MAX_FILE_AGE_MS) return null;
|
|
7645
|
+
return row;
|
|
7646
|
+
};
|
|
7647
|
+
const statusPath = path14.join(homeDir, ".claude", "claude-usage.json");
|
|
7648
|
+
const status = read(statusPath);
|
|
7583
7649
|
if (status && (status.seven_day || status.five_hour)) {
|
|
7584
|
-
const row = makeUsageRow({
|
|
7650
|
+
const row = fresh(makeUsageRow({
|
|
7585
7651
|
agent: "claude",
|
|
7586
7652
|
source: "statusline",
|
|
7587
|
-
capturedAt:
|
|
7653
|
+
capturedAt: fileCaptureTime(statusPath, status.capturedAt, statFn),
|
|
7588
7654
|
accountId,
|
|
7589
7655
|
sevenDay: status.seven_day?.used_percentage,
|
|
7590
7656
|
fiveHour: status.five_hour?.used_percentage,
|
|
7591
7657
|
sevenDayResetsAt: status.seven_day?.resets_at ?? null,
|
|
7592
7658
|
fiveHourResetsAt: status.five_hour?.resets_at ?? null
|
|
7593
|
-
});
|
|
7659
|
+
}));
|
|
7594
7660
|
if (row) return row;
|
|
7595
7661
|
}
|
|
7596
|
-
const
|
|
7662
|
+
const weeklyPath = path14.join(homeDir, ".claude", "claude-weekly-usage.json");
|
|
7663
|
+
const weekly = read(weeklyPath);
|
|
7597
7664
|
if (weekly) {
|
|
7598
|
-
const row = makeUsageRow({
|
|
7665
|
+
const row = fresh(makeUsageRow({
|
|
7599
7666
|
agent: "claude",
|
|
7600
7667
|
source: "file",
|
|
7601
|
-
capturedAt:
|
|
7668
|
+
capturedAt: fileCaptureTime(weeklyPath, weekly.capturedAt, statFn),
|
|
7602
7669
|
accountId,
|
|
7603
7670
|
sevenDay: weekly.sevenDayPct,
|
|
7604
7671
|
fiveHour: weekly.fiveHourPct,
|
|
7605
7672
|
sevenDayResetsAt: weekly.sevenDayResetsAt ?? null
|
|
7606
|
-
});
|
|
7673
|
+
}));
|
|
7607
7674
|
if (row) return row;
|
|
7608
7675
|
}
|
|
7609
7676
|
return null;
|
|
@@ -7616,11 +7683,12 @@ async function readClaudeUsage(opts = {}) {
|
|
|
7616
7683
|
}
|
|
7617
7684
|
return readClaudeFileUsage(opts);
|
|
7618
7685
|
}
|
|
7619
|
-
var USAGE_PATH, OAUTH_BETA, DEFAULT_TIMEOUT_MS;
|
|
7686
|
+
var MAX_FILE_AGE_MS, USAGE_PATH, OAUTH_BETA, DEFAULT_TIMEOUT_MS;
|
|
7620
7687
|
var init_claude = __esm({
|
|
7621
7688
|
"../../scripts/virtual-office/code-runner/account-usage/claude.mjs"() {
|
|
7622
7689
|
"use strict";
|
|
7623
7690
|
init_shared();
|
|
7691
|
+
MAX_FILE_AGE_MS = 6 * 60 * 60 * 1e3;
|
|
7624
7692
|
USAGE_PATH = "/api/oauth/usage";
|
|
7625
7693
|
OAUTH_BETA = "oauth-2025-04-20";
|
|
7626
7694
|
DEFAULT_TIMEOUT_MS = 5e3;
|
|
@@ -9043,7 +9111,7 @@ var init_effort_mode_config = __esm({
|
|
|
9043
9111
|
});
|
|
9044
9112
|
|
|
9045
9113
|
// ../../scripts/virtual-office/model-registry.mjs
|
|
9046
|
-
import
|
|
9114
|
+
import fs8 from "node:fs";
|
|
9047
9115
|
import path15 from "node:path";
|
|
9048
9116
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
9049
9117
|
function uniqueModels(models = []) {
|
|
@@ -9156,9 +9224,9 @@ async function fetchGoogleModels(fetchImpl, env2 = process.env) {
|
|
|
9156
9224
|
return (data?.models || []).map((model) => normalizeCatalogModel({ ...model, provider: "google", source: "google" })).filter(Boolean);
|
|
9157
9225
|
}
|
|
9158
9226
|
function readCache(cacheFile = DEFAULT_CACHE_FILE, nowMs = Date.now(), ttlMs = DEFAULT_TTL_MS3) {
|
|
9159
|
-
if (!
|
|
9227
|
+
if (!fs8.existsSync(cacheFile)) return null;
|
|
9160
9228
|
try {
|
|
9161
|
-
const parsed = JSON.parse(
|
|
9229
|
+
const parsed = JSON.parse(fs8.readFileSync(cacheFile, "utf-8"));
|
|
9162
9230
|
if (nowMs - Number(parsed.checkedAtMs || 0) > ttlMs) return null;
|
|
9163
9231
|
if (!Array.isArray(parsed.models)) return null;
|
|
9164
9232
|
return parsed;
|
|
@@ -9167,8 +9235,8 @@ function readCache(cacheFile = DEFAULT_CACHE_FILE, nowMs = Date.now(), ttlMs = D
|
|
|
9167
9235
|
}
|
|
9168
9236
|
}
|
|
9169
9237
|
function writeCache(cacheFile = DEFAULT_CACHE_FILE, payload) {
|
|
9170
|
-
|
|
9171
|
-
|
|
9238
|
+
fs8.mkdirSync(path15.dirname(cacheFile), { recursive: true });
|
|
9239
|
+
fs8.writeFileSync(cacheFile, JSON.stringify(payload, null, 2));
|
|
9172
9240
|
}
|
|
9173
9241
|
async function fetchRegistryCatalog({
|
|
9174
9242
|
fetchImpl = fetch,
|
|
@@ -10440,6 +10508,93 @@ var init_task_helpers = __esm({
|
|
|
10440
10508
|
}
|
|
10441
10509
|
});
|
|
10442
10510
|
|
|
10511
|
+
// ../../scripts/virtual-office/code-runner/swarm-admission.mjs
|
|
10512
|
+
function canLaunchSuccessorAgent(agent, platform = process.platform) {
|
|
10513
|
+
const name = typeof agent === "string" ? agent.trim() : "";
|
|
10514
|
+
if (!Object.prototype.hasOwnProperty.call(SUCCESSOR_LAUNCH_SHAPES, name)) return false;
|
|
10515
|
+
if (platform === "win32" && !SUCCESSOR_LAUNCH_SHAPES[name]) return false;
|
|
10516
|
+
return true;
|
|
10517
|
+
}
|
|
10518
|
+
function isSubscriptionExhausted(usage) {
|
|
10519
|
+
if (!usage || typeof usage !== "object") return false;
|
|
10520
|
+
const readings = [usage.seven_day_used_pct, usage.five_hour_used_pct, usage.monthly_used_pct];
|
|
10521
|
+
return readings.some((v) => typeof v === "number" && Number.isFinite(v) && v >= SUBSCRIPTION_EXHAUSTED_PCT);
|
|
10522
|
+
}
|
|
10523
|
+
function clampSubagents(requested) {
|
|
10524
|
+
if (!Number.isFinite(requested) || requested < 1) return 0;
|
|
10525
|
+
return Math.min(Math.floor(requested), MAX_BOUND_SUBAGENTS);
|
|
10526
|
+
}
|
|
10527
|
+
function usageFor(accountUsage, agent) {
|
|
10528
|
+
if (!Array.isArray(accountUsage)) return null;
|
|
10529
|
+
return accountUsage.find((row) => row && row.agent === agent) ?? null;
|
|
10530
|
+
}
|
|
10531
|
+
function exhaustedAgents(availableAgents, accountUsage) {
|
|
10532
|
+
if (!Array.isArray(availableAgents)) return [];
|
|
10533
|
+
return availableAgents.filter((row) => row && row.installed === true && row.authenticated === true && row.auth_tier === AUTH_TIER_SUBSCRIPTION && isSubscriptionExhausted(usageFor(accountUsage, row.agent))).map((row) => row.agent);
|
|
10534
|
+
}
|
|
10535
|
+
function resolveRunnerSwarmBinding({
|
|
10536
|
+
swarmId,
|
|
10537
|
+
agent,
|
|
10538
|
+
availableAgents,
|
|
10539
|
+
accountUsage = [],
|
|
10540
|
+
requestedSubagents = MAX_BOUND_SUBAGENTS,
|
|
10541
|
+
nowIso,
|
|
10542
|
+
platform = process.platform
|
|
10543
|
+
} = {}) {
|
|
10544
|
+
const id = typeof swarmId === "string" ? swarmId.trim() : "";
|
|
10545
|
+
const boundAgent = typeof agent === "string" ? agent.trim() : "";
|
|
10546
|
+
if (id.length === 0 || boundAgent.length === 0) return null;
|
|
10547
|
+
if (!canLaunchSuccessorAgent(boundAgent, platform)) return null;
|
|
10548
|
+
const budget = clampSubagents(requestedSubagents);
|
|
10549
|
+
if (budget === 0) return null;
|
|
10550
|
+
const rows = Array.isArray(availableAgents) ? availableAgents : [];
|
|
10551
|
+
const row = rows.find((r) => r && r.agent === boundAgent) ?? null;
|
|
10552
|
+
if (!row || row.installed !== true || row.authenticated !== true) return null;
|
|
10553
|
+
const tier = AUTH_TIER_TO_SWARM_TIER[row.auth_tier];
|
|
10554
|
+
if (!tier) return null;
|
|
10555
|
+
const exhausted = exhaustedAgents(rows, accountUsage);
|
|
10556
|
+
const boundExhausted = tier === "tier1_subscription" && isSubscriptionExhausted(usageFor(accountUsage, boundAgent));
|
|
10557
|
+
const effectiveBudget = boundExhausted ? Math.min(budget, EXHAUSTED_SUBAGENT_BUDGET) : budget;
|
|
10558
|
+
const basis = `code-task dispatch admitted on '${boundAgent}' at auth tier '${row.auth_tier}' (runner-local capability probe)`;
|
|
10559
|
+
return {
|
|
10560
|
+
schema_version: 1,
|
|
10561
|
+
swarm_id: id,
|
|
10562
|
+
tier,
|
|
10563
|
+
agent: boundAgent,
|
|
10564
|
+
reason: boundExhausted ? `${basis}; that subscription window is >=${SUBSCRIPTION_EXHAUSTED_PCT}% spent, so the fan-out is bound at a REDUCED ceiling of ${effectiveBudget} instead of being left unbound and uncapped` : basis,
|
|
10565
|
+
exhausted_agents: exhausted,
|
|
10566
|
+
subagent_budget: effectiveBudget,
|
|
10567
|
+
// Never a platform-billed fan-out in this lane — see the module header.
|
|
10568
|
+
spend_cap_usd: null,
|
|
10569
|
+
resolved_at: typeof nowIso === "string" && nowIso ? nowIso : (/* @__PURE__ */ new Date()).toISOString()
|
|
10570
|
+
};
|
|
10571
|
+
}
|
|
10572
|
+
function mintSwarmTierBindingEnv(input) {
|
|
10573
|
+
const binding = resolveRunnerSwarmBinding(input);
|
|
10574
|
+
if (!binding) return {};
|
|
10575
|
+
return { [SWARM_TIER_BINDING_ENV]: JSON.stringify(binding) };
|
|
10576
|
+
}
|
|
10577
|
+
var SWARM_TIER_BINDING_ENV, MAX_BOUND_SUBAGENTS, SUBSCRIPTION_EXHAUSTED_PCT, EXHAUSTED_SUBAGENT_BUDGET, SUCCESSOR_LAUNCH_SHAPES, AUTH_TIER_TO_SWARM_TIER;
|
|
10578
|
+
var init_swarm_admission = __esm({
|
|
10579
|
+
"../../scripts/virtual-office/code-runner/swarm-admission.mjs"() {
|
|
10580
|
+
"use strict";
|
|
10581
|
+
init_agent_auth_tier();
|
|
10582
|
+
SWARM_TIER_BINDING_ENV = "VO_SWARM_TIER_BINDING";
|
|
10583
|
+
MAX_BOUND_SUBAGENTS = 20;
|
|
10584
|
+
SUBSCRIPTION_EXHAUSTED_PCT = 95;
|
|
10585
|
+
EXHAUSTED_SUBAGENT_BUDGET = 1;
|
|
10586
|
+
SUCCESSOR_LAUNCH_SHAPES = Object.freeze({
|
|
10587
|
+
claude: true,
|
|
10588
|
+
codex: false
|
|
10589
|
+
});
|
|
10590
|
+
AUTH_TIER_TO_SWARM_TIER = Object.freeze({
|
|
10591
|
+
[AUTH_TIER_SUBSCRIPTION]: "tier1_subscription",
|
|
10592
|
+
[AUTH_TIER_LOCAL]: "tier1_local",
|
|
10593
|
+
[AUTH_TIER_API_KEY]: "tier2_user_key"
|
|
10594
|
+
});
|
|
10595
|
+
}
|
|
10596
|
+
});
|
|
10597
|
+
|
|
10443
10598
|
// ../../scripts/virtual-office/code-runner/agent-process-env.mjs
|
|
10444
10599
|
function safeIdentityPart(value, fallback) {
|
|
10445
10600
|
const normalized = String(value || "").trim().replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -10452,8 +10607,14 @@ function safeBaseEnv(env2 = {}) {
|
|
|
10452
10607
|
}
|
|
10453
10608
|
return result;
|
|
10454
10609
|
}
|
|
10455
|
-
function buildAgentProcessEnv(env2, { agent = "agent", runnerId = "vo-runner", taskId = "task", githubReadToken = null } = {}) {
|
|
10610
|
+
function buildAgentProcessEnv(env2, { agent = "agent", runnerId = "vo-runner", taskId = "task", githubReadToken = null, swarmAdmission = null } = {}) {
|
|
10456
10611
|
const base = safeBaseEnv(env2);
|
|
10612
|
+
if (swarmAdmission) {
|
|
10613
|
+
for (const key of Object.keys(base)) {
|
|
10614
|
+
if (key.toUpperCase() === SWARM_TIER_BINDING_ENV) delete base[key];
|
|
10615
|
+
}
|
|
10616
|
+
Object.assign(base, mintSwarmTierBindingEnv({ ...swarmAdmission, agent, swarmId: taskId }));
|
|
10617
|
+
}
|
|
10457
10618
|
if (typeof githubReadToken === "string" && githubReadToken) {
|
|
10458
10619
|
base.GH_TOKEN = githubReadToken;
|
|
10459
10620
|
base.GITHUB_TOKEN = githubReadToken;
|
|
@@ -10471,6 +10632,7 @@ var SAFE_ENV_NAMES;
|
|
|
10471
10632
|
var init_agent_process_env = __esm({
|
|
10472
10633
|
"../../scripts/virtual-office/code-runner/agent-process-env.mjs"() {
|
|
10473
10634
|
"use strict";
|
|
10635
|
+
init_swarm_admission();
|
|
10474
10636
|
SAFE_ENV_NAMES = /* @__PURE__ */ new Set([
|
|
10475
10637
|
"AGENT_ID",
|
|
10476
10638
|
"APPDATA",
|
|
@@ -10511,7 +10673,31 @@ var init_agent_process_env = __esm({
|
|
|
10511
10673
|
// it. #9242 added VO_RUNNER_PREFER_KEY without this line, which left the
|
|
10512
10674
|
// escape hatch inert — an operator who set it still got the subscription.
|
|
10513
10675
|
"VO_RUNNER_PREFER_KEY",
|
|
10514
|
-
"VO_RUNNER_CLAUDE_PREFER_KEY"
|
|
10676
|
+
"VO_RUNNER_CLAUDE_PREFER_KEY",
|
|
10677
|
+
// The swarm tier binding (SWARM_TIER_BINDING_ENV in
|
|
10678
|
+
// packages/vo-mcp/src/swarm/tier-binding.ts). A fan-out resolves its billing
|
|
10679
|
+
// tier ONCE at admission and exports the binding so every subagent inherits
|
|
10680
|
+
// the same answer instead of re-resolving its own. This line is what lets it
|
|
10681
|
+
// cross the process boundary at all: the daemon builds each child env with
|
|
10682
|
+
// buildAgentProcessEnv(process.env) at
|
|
10683
|
+
// scripts/virtual-office/code-runner-daemon.mjs:117 (sibling dir, not this
|
|
10684
|
+
// one), so a name absent from this set is stripped and the binding binds
|
|
10685
|
+
// NOTHING —
|
|
10686
|
+
// exactly how #9242's VO_RUNNER_PREFER_KEY shipped inert until #9247.
|
|
10687
|
+
//
|
|
10688
|
+
// NOT a credential and NOT an authorization input: it names a tier, it never
|
|
10689
|
+
// grants one, and it carries no key material (see the module's header rule).
|
|
10690
|
+
"VO_SWARM_TIER_BINDING",
|
|
10691
|
+
// Where the swarm SPAWN LEDGER lives (SWARM_LEDGER_DIR_ENV in
|
|
10692
|
+
// packages/vo-mcp/src/swarm/spawn-ledger.ts). The ledger is the durable,
|
|
10693
|
+
// host-shared counter that bounds the TOTAL spawns under one swarm_id; the
|
|
10694
|
+
// binding above only bounds the depth of one chain. If a parent's override
|
|
10695
|
+
// were stripped here, the child would ledger into a DIFFERENT directory,
|
|
10696
|
+
// claim slot 0 again, and the shared ceiling would silently degrade back to a
|
|
10697
|
+
// per-process quota — which is precisely the defect the ledger closes.
|
|
10698
|
+
//
|
|
10699
|
+
// A path, not a credential. Absent means the default ~/.vo/swarm-ledger.
|
|
10700
|
+
"VO_SWARM_LEDGER_DIR"
|
|
10515
10701
|
]);
|
|
10516
10702
|
}
|
|
10517
10703
|
});
|
|
@@ -11002,7 +11188,7 @@ var init_inference_task_runner = __esm({
|
|
|
11002
11188
|
});
|
|
11003
11189
|
|
|
11004
11190
|
// ../../scripts/virtual-office/code-runner/isolation-audit.mjs
|
|
11005
|
-
import
|
|
11191
|
+
import fs9 from "node:fs";
|
|
11006
11192
|
import fsp11 from "node:fs/promises";
|
|
11007
11193
|
import path16 from "node:path";
|
|
11008
11194
|
async function defaultRun(command, args, cwd, options = {}) {
|
|
@@ -11097,7 +11283,7 @@ async function restoreExactCanonicalPaths(baseline, evidence, run) {
|
|
|
11097
11283
|
for (const relative of evidence.untracked) {
|
|
11098
11284
|
const target = path16.resolve(baseline.root, relative);
|
|
11099
11285
|
const prefix = `${path16.resolve(baseline.root)}${path16.sep}`;
|
|
11100
|
-
if (!target.startsWith(prefix) || !
|
|
11286
|
+
if (!target.startsWith(prefix) || !fs9.existsSync(target)) continue;
|
|
11101
11287
|
await fsp11.rm(target, { force: true });
|
|
11102
11288
|
}
|
|
11103
11289
|
}
|
|
@@ -11627,7 +11813,7 @@ var init_publication_scope = __esm({
|
|
|
11627
11813
|
});
|
|
11628
11814
|
|
|
11629
11815
|
// ../../scripts/virtual-office/code-runner/recovery-ledger.mjs
|
|
11630
|
-
import
|
|
11816
|
+
import fs10 from "node:fs";
|
|
11631
11817
|
import fsp13 from "node:fs/promises";
|
|
11632
11818
|
import path18 from "node:path";
|
|
11633
11819
|
function recoveryTaskId(prompt) {
|
|
@@ -11665,7 +11851,7 @@ async function readLedger(file, readFile5) {
|
|
|
11665
11851
|
async function findPreservedRecovery(task, {
|
|
11666
11852
|
clonesRoot: clonesRoot2 = process.env.VO_CODE_RUNNER_CLONES_ROOT || "",
|
|
11667
11853
|
readFile: readFile5 = fsp13.readFile,
|
|
11668
|
-
exists =
|
|
11854
|
+
exists = fs10.existsSync
|
|
11669
11855
|
} = {}) {
|
|
11670
11856
|
const resumedFrom = /^[0-9a-f-]{36}$/iu.test(String(task.resumed_from || "")) ? String(task.resumed_from).toLowerCase() : null;
|
|
11671
11857
|
const originalTaskId = recoveryTaskId(task.prompt) ?? resumedFrom;
|
|
@@ -12389,7 +12575,7 @@ import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
|
12389
12575
|
function log(msg) {
|
|
12390
12576
|
console.log(`[code-runner ${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}`);
|
|
12391
12577
|
}
|
|
12392
|
-
async function processOneTask(client, task, cfg, runnerInstanceId) {
|
|
12578
|
+
async function processOneTask(client, task, cfg, runnerInstanceId, swarmAdmission = null) {
|
|
12393
12579
|
const id = task.code_task_id;
|
|
12394
12580
|
let worktreeName = "";
|
|
12395
12581
|
let preserveReason = null;
|
|
@@ -12439,7 +12625,8 @@ async function processOneTask(client, task, cfg, runnerInstanceId) {
|
|
|
12439
12625
|
model,
|
|
12440
12626
|
effort: effectiveEffort,
|
|
12441
12627
|
maxBudgetUsd: sel.agent === "claude" ? effectiveMaxBudgetUsd : void 0,
|
|
12442
|
-
env: buildAgentProcessEnv(process.env, { agent: sel.agent, runnerId: cfg.runnerId, taskId: id, githubReadToken: agentGithubReadToken }),
|
|
12628
|
+
env: buildAgentProcessEnv(process.env, { agent: sel.agent, runnerId: cfg.runnerId, taskId: id, githubReadToken: agentGithubReadToken, swarmAdmission }),
|
|
12629
|
+
// swarmAdmission mints VO_SWARM_TIER_BINDING: ONE tier decision for this task's whole agent tree
|
|
12443
12630
|
sandbox,
|
|
12444
12631
|
onProgress: (text, checkpoint) => {
|
|
12445
12632
|
const usage = checkpoint?.tokenUsage ? { token_usage: checkpoint.tokenUsage } : {};
|
|
@@ -12725,7 +12912,7 @@ async function main({ env: env2 = process.env, once: once2 = false } = {}) {
|
|
|
12725
12912
|
}
|
|
12726
12913
|
log(`claimed task ${task.code_task_id} (${task.repo})`);
|
|
12727
12914
|
active += 1;
|
|
12728
|
-
const runTask = task.kind === "inference" ? processInferenceTask(client, task, cfg, { safeProgress, runnerStagePatch, log }) : processOneTask(client, task, cfg, runnerInstanceId);
|
|
12915
|
+
const runTask = task.kind === "inference" ? processInferenceTask(client, task, cfg, { safeProgress, runnerStagePatch, log }) : processOneTask(client, task, cfg, runnerInstanceId, { availableAgents: claimAgents.availableAgents, accountUsage: accountUsage.get() });
|
|
12729
12916
|
const done = runTask.catch(async (error) => {
|
|
12730
12917
|
log(`task ${task.code_task_id} unhandled runner error: ${error.message}`);
|
|
12731
12918
|
if (task.kind === "inference") await deliverTerminalRun({
|