@algosuite/vo-mcp 0.2.0-beta.26 → 0.2.0-beta.28

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.
@@ -3026,23 +3026,42 @@ function isTruthyFlag(v) {
3026
3026
  const s = String(v ?? "").trim().toLowerCase();
3027
3027
  return s === "1" || s === "true" || s === "yes" || s === "on";
3028
3028
  }
3029
- function withAnthropicKey(baseEnv = {}, { getKey = getAnthropicKey } = {}) {
3030
- if (isTruthyFlag(baseEnv[CLAUDE_PREFER_LOGIN_ENV]) || isTruthyFlag(baseEnv[PREFER_LOGIN_ENV])) {
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
+ function withAnthropicKey(baseEnv = {}, { getKey = getAnthropicKey, probeLogin = probeClaudeLoginState } = {}) {
3036
+ const preferKey = wantsKey(baseEnv);
3037
+ if (!preferKey && wantsLogin(baseEnv)) {
3031
3038
  const next = { ...baseEnv };
3032
3039
  delete next.ANTHROPIC_API_KEY;
3033
3040
  return next;
3034
3041
  }
3035
3042
  if (baseEnv.ANTHROPIC_API_KEY) return { ...baseEnv };
3036
3043
  const key = getKey();
3037
- return key ? { ...baseEnv, ANTHROPIC_API_KEY: key } : { ...baseEnv };
3044
+ if (!key) return { ...baseEnv };
3045
+ if (!preferKey && probeLogin() === true) return { ...baseEnv };
3046
+ return { ...baseEnv, ANTHROPIC_API_KEY: key };
3047
+ }
3048
+ function claudeCostBasis(env2 = process.env) {
3049
+ return String(env2.ANTHROPIC_API_KEY || "").trim() ? "vendor_billed" : "subscription_api_equivalent";
3038
3050
  }
3039
- function describeAnthropicAuthSource(baseEnv = {}, { getKey = getAnthropicKey } = {}) {
3040
- if (isTruthyFlag(baseEnv[CLAUDE_PREFER_LOGIN_ENV]) || isTruthyFlag(baseEnv[PREFER_LOGIN_ENV])) {
3051
+ function describeAnthropicAuthSource(baseEnv = {}, { getKey = getAnthropicKey, probeLogin = probeClaudeLoginState } = {}) {
3052
+ const preferKey = wantsKey(baseEnv);
3053
+ if (!preferKey && wantsLogin(baseEnv)) {
3041
3054
  return "claude auth login (VO_RUNNER_PREFER_LOGIN set \u2014 any API key ignored)";
3042
3055
  }
3043
3056
  if (baseEnv.ANTHROPIC_API_KEY) return "ANTHROPIC_API_KEY from environment";
3044
- if (getKey()) return "ANTHROPIC_API_KEY from OS keychain";
3045
- return "claude auth login session (no API key set)";
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";
3046
3065
  }
3047
3066
  function augmentAuthError(summary) {
3048
3067
  const s = String(summary ?? "");
@@ -3064,7 +3083,7 @@ function probeClaudeLoginState({
3064
3083
  return null;
3065
3084
  }
3066
3085
  }
3067
- var require2, KEY_SERVICE, KEY_ACCOUNT, _entryCtor, _loadTried, PREFER_LOGIN_ENV, CLAUDE_PREFER_LOGIN_ENV, AUTH_ERROR_RE;
3086
+ var require2, KEY_SERVICE, KEY_ACCOUNT, _entryCtor, _loadTried, PREFER_LOGIN_ENV, CLAUDE_PREFER_LOGIN_ENV, PREFER_KEY_ENV, CLAUDE_PREFER_KEY_ENV, AUTH_ERROR_RE;
3068
3087
  var init_anthropic_key_store = __esm({
3069
3088
  "../../scripts/virtual-office/code-runner/anthropic-key-store.mjs"() {
3070
3089
  "use strict";
@@ -3075,6 +3094,8 @@ var init_anthropic_key_store = __esm({
3075
3094
  _loadTried = false;
3076
3095
  PREFER_LOGIN_ENV = "VO_RUNNER_PREFER_LOGIN";
3077
3096
  CLAUDE_PREFER_LOGIN_ENV = "VO_RUNNER_CLAUDE_PREFER_LOGIN";
3097
+ PREFER_KEY_ENV = "VO_RUNNER_PREFER_KEY";
3098
+ CLAUDE_PREFER_KEY_ENV = "VO_RUNNER_CLAUDE_PREFER_KEY";
3078
3099
  AUTH_ERROR_RE = /\b401\b|invalid[^.]{0,24}(authentication|credential)|authentication_error|unauthorized|not[ _-]?authenticated/i;
3079
3100
  }
3080
3101
  });
@@ -3734,6 +3755,46 @@ var init_claude_stream_event = __esm({
3734
3755
  }
3735
3756
  });
3736
3757
 
3758
+ // ../../scripts/virtual-office/code-runner/agent-auth-tier.mjs
3759
+ function authTierFromCostBasis(costBasis) {
3760
+ return COST_BASIS_TO_TIER[String(costBasis ?? "")] ?? AUTH_TIER_UNKNOWN;
3761
+ }
3762
+ function safeAuthTier(compute) {
3763
+ try {
3764
+ return normalizeAuthTier(compute());
3765
+ } catch {
3766
+ return AUTH_TIER_UNKNOWN;
3767
+ }
3768
+ }
3769
+ function normalizeAuthTier(value) {
3770
+ return AUTH_TIERS.includes(value) ? value : AUTH_TIER_UNKNOWN;
3771
+ }
3772
+ function resolveReportedAuthTier({ authTier, installed, authenticated } = {}) {
3773
+ if (installed !== true || authenticated !== true) return AUTH_TIER_UNKNOWN;
3774
+ return normalizeAuthTier(authTier);
3775
+ }
3776
+ var AUTH_TIER_SUBSCRIPTION, AUTH_TIER_API_KEY, AUTH_TIER_LOCAL, AUTH_TIER_UNKNOWN, AUTH_TIERS, COST_BASIS_TO_TIER;
3777
+ var init_agent_auth_tier = __esm({
3778
+ "../../scripts/virtual-office/code-runner/agent-auth-tier.mjs"() {
3779
+ "use strict";
3780
+ AUTH_TIER_SUBSCRIPTION = "subscription";
3781
+ AUTH_TIER_API_KEY = "api_key";
3782
+ AUTH_TIER_LOCAL = "local";
3783
+ AUTH_TIER_UNKNOWN = "unknown";
3784
+ AUTH_TIERS = Object.freeze([
3785
+ AUTH_TIER_SUBSCRIPTION,
3786
+ AUTH_TIER_API_KEY,
3787
+ AUTH_TIER_LOCAL,
3788
+ AUTH_TIER_UNKNOWN
3789
+ ]);
3790
+ COST_BASIS_TO_TIER = Object.freeze({
3791
+ subscription_api_equivalent: AUTH_TIER_SUBSCRIPTION,
3792
+ vendor_billed: AUTH_TIER_API_KEY,
3793
+ local_zero: AUTH_TIER_LOCAL
3794
+ });
3795
+ }
3796
+ });
3797
+
3737
3798
  // ../../scripts/virtual-office/code-runner/cli-version-floor.mjs
3738
3799
  function parseCliVersion(output) {
3739
3800
  const match = /\b(\d+)\.(\d+)\.(\d+)(?:-[0-9A-Za-z.-]+)?\b/.exec(String(output ?? ""));
@@ -3803,9 +3864,22 @@ function isTimeout(probe) {
3803
3864
  function notFound(probe) {
3804
3865
  return errorCode(probe?.error) === "ENOENT";
3805
3866
  }
3867
+ function resolveClaudeAuthTier({
3868
+ env: env2 = process.env,
3869
+ loggedIn = null,
3870
+ getStoredKey = getAnthropicKey
3871
+ } = {}) {
3872
+ return safeAuthTier(() => {
3873
+ const spawnEnv = withAnthropicKey(env2, { getKey: getStoredKey, probeLogin: () => loggedIn });
3874
+ const tier = authTierFromCostBasis(claudeCostBasis(spawnEnv));
3875
+ if (tier === AUTH_TIER_SUBSCRIPTION && loggedIn !== true) return AUTH_TIER_UNKNOWN;
3876
+ return tier;
3877
+ });
3878
+ }
3806
3879
  async function checkClaudeAuth({
3807
3880
  spawnVersion = spawnClaudeSync,
3808
3881
  probeLogin = probeClaudeLoginState,
3882
+ getStoredKey = getAnthropicKey,
3809
3883
  env: env2 = process.env
3810
3884
  } = {}) {
3811
3885
  try {
@@ -3855,6 +3929,10 @@ async function checkClaudeAuth({
3855
3929
  return {
3856
3930
  installed: true,
3857
3931
  authenticated: true,
3932
+ // Dispatch-time billing signal, carried on the same probe that already
3933
+ // paid for the login read. Never sent for a non-authenticated result:
3934
+ // there is no tier without a working credential.
3935
+ authTier: resolveClaudeAuthTier({ env: env2, loggedIn, getStoredKey }),
3858
3936
  message: loggedIn === true ? "claude CLI installed and logged in (claude auth status)" : "claude binary found (login state unknown \u2014 auth check is best-effort)"
3859
3937
  };
3860
3938
  } catch (error) {
@@ -3870,6 +3948,7 @@ var init_claude_auth_check = __esm({
3870
3948
  "../../scripts/virtual-office/code-runner/claude-auth-check.mjs"() {
3871
3949
  "use strict";
3872
3950
  init_anthropic_key_store();
3951
+ init_agent_auth_tier();
3873
3952
  init_cli_version_floor();
3874
3953
  init_windows_claude_launch();
3875
3954
  FIRST_VERSION_TIMEOUT_MS = 4500;
@@ -4134,7 +4213,7 @@ var init_claude_runner = __esm({
4134
4213
  return withAnthropicKey(env2);
4135
4214
  }
4136
4215
  costBasis(env2 = process.env) {
4137
- return String(env2.ANTHROPIC_API_KEY || "").trim() ? "vendor_billed" : "subscription_api_equivalent";
4216
+ return claudeCostBasis(env2);
4138
4217
  }
4139
4218
  /** Describe which Anthropic auth source the spawn will use (for runner logs). */
4140
4219
  describeAuth(env2 = process.env) {
@@ -4369,6 +4448,7 @@ var init_codex_runner = __esm({
4369
4448
  "../../scripts/virtual-office/code-runner/codex-runner.mjs"() {
4370
4449
  "use strict";
4371
4450
  init_agent_key_store();
4451
+ init_agent_auth_tier();
4372
4452
  init_flat_token_usage();
4373
4453
  CODEX_PREFER_LOGIN_ENV = "VO_RUNNER_CODEX_PREFER_LOGIN";
4374
4454
  LEGACY_PREFER_LOGIN_ENV = "VO_RUNNER_PREFER_LOGIN";
@@ -4426,6 +4506,19 @@ var init_codex_runner = __esm({
4426
4506
  costBasis(env2 = process.env) {
4427
4507
  return String(env2.OPENAI_API_KEY || env2.CODEX_API_KEY || "").trim() ? "vendor_billed" : "subscription_api_equivalent";
4428
4508
  }
4509
+ /**
4510
+ * Dispatch-time billing tier for the NEXT codex spawn, from the SAME facts
4511
+ * checkAuth() already gathered — no extra subprocess. applyAuthEnv() is a
4512
+ * keychain read in this process (@napi-rs/keyring), not a spawn.
4513
+ *
4514
+ * Codex is the agent where this signal was already computed and then thrown
4515
+ * away: checkAuth() distinguished "API key available (no persisted ChatGPT
4516
+ * login)" from a real login, but only inside a `message` string that the
4517
+ * heartbeat schema strips before storage. This gives that fact a typed home.
4518
+ */
4519
+ authTier(env2 = this.env) {
4520
+ return safeAuthTier(() => authTierFromCostBasis(this.costBasis(this.applyAuthEnv(env2))));
4521
+ }
4429
4522
  /** Best-effort binary + persisted-login probe. Never throws or spends tokens. */
4430
4523
  async checkAuth() {
4431
4524
  try {
@@ -4459,6 +4552,7 @@ ${login.stderr || ""}`.trim();
4459
4552
  installed: true,
4460
4553
  authenticated: true,
4461
4554
  ...versionField,
4555
+ authTier: this.authTier(),
4462
4556
  message: "codex API key available (no persisted ChatGPT login)"
4463
4557
  };
4464
4558
  }
@@ -4473,6 +4567,7 @@ ${login.stderr || ""}`.trim();
4473
4567
  installed: true,
4474
4568
  authenticated: true,
4475
4569
  ...versionField,
4570
+ authTier: this.authTier(),
4476
4571
  message: output || "codex login status succeeded"
4477
4572
  };
4478
4573
  } catch (err) {
@@ -4539,6 +4634,7 @@ var init_cursor_runner = __esm({
4539
4634
  "../../scripts/virtual-office/code-runner/cursor-runner.mjs"() {
4540
4635
  "use strict";
4541
4636
  init_agent_key_store();
4637
+ init_agent_auth_tier();
4542
4638
  init_flat_token_usage();
4543
4639
  CursorRunner = class {
4544
4640
  get binary() {
@@ -4583,6 +4679,16 @@ var init_cursor_runner = __esm({
4583
4679
  costBasis(env2 = process.env) {
4584
4680
  return env2.CURSOR_API_KEY ? "vendor_billed" : "unknown";
4585
4681
  }
4682
+ /**
4683
+ * Dispatch-time billing tier. Inherits costBasis()'s deliberate refusal to
4684
+ * guess: a prior interactive `cursor-agent login` has undocumented
4685
+ * subscription semantics, so it reports 'unknown' rather than claiming a
4686
+ * flat-cost seat the runner cannot actually prove. Keychain read only — the
4687
+ * heartbeat never pays for a subprocess to answer this.
4688
+ */
4689
+ authTier(env2 = process.env) {
4690
+ return safeAuthTier(() => authTierFromCostBasis(this.costBasis(this.applyAuthEnv(env2))));
4691
+ }
4586
4692
  /** Best-effort: is `cursor-agent` on PATH? Never throws. */
4587
4693
  async checkAuth() {
4588
4694
  try {
@@ -4608,6 +4714,7 @@ var init_cursor_runner = __esm({
4608
4714
  return {
4609
4715
  installed: true,
4610
4716
  authenticated: true,
4717
+ authTier: this.authTier(),
4611
4718
  message: "cursor-agent found (EXPERIMENTAL: headless mode may need a TTY; auth check is best-effort)"
4612
4719
  };
4613
4720
  } catch (err) {
@@ -4704,6 +4811,7 @@ var init_local_model_runner = __esm({
4704
4811
  "use strict";
4705
4812
  init_codex_runner();
4706
4813
  init_agent_key_store();
4814
+ init_agent_auth_tier();
4707
4815
  LOCAL_API_KEY_ENV = "VO_CODE_RUNNER_LOCAL_API_KEY";
4708
4816
  LOCAL_PROVIDERS = ["ollama", "lmstudio"];
4709
4817
  DEFAULT_LOCAL_PROVIDER = "ollama";
@@ -4756,6 +4864,10 @@ var init_local_model_runner = __esm({
4756
4864
  costBasis() {
4757
4865
  return "local_zero";
4758
4866
  }
4867
+ /** Dispatch-time billing tier: local inference is never billed by a vendor. */
4868
+ authTier() {
4869
+ return safeAuthTier(() => authTierFromCostBasis(this.costBasis()));
4870
+ }
4759
4871
  describeAuth(env2 = process.env) {
4760
4872
  const authEnv = this.applyAuthEnv(env2);
4761
4873
  const provider = resolveLocalProvider(this.env);
@@ -4813,6 +4925,7 @@ var init_local_model_runner = __esm({
4813
4925
  return {
4814
4926
  installed: true,
4815
4927
  authenticated: true,
4928
+ authTier: this.authTier(),
4816
4929
  message: `${provider} reachable; model "${model}" configured (local-only, no cloud spend)`
4817
4930
  };
4818
4931
  }
@@ -4901,7 +5014,7 @@ var init_openai_compatible_runner = __esm({
4901
5014
  buildArgs(opts = {}) {
4902
5015
  void opts;
4903
5016
  throw new Error(
4904
- "OpenAI-compatible full-repository coding is disabled. Use an explicit sanitized task capsule through the AlgoSuite Model Firewall."
5017
+ "OpenAI-compatible full-repository coding is disabled. The `oai` runner lane is RETIRED (PR #8742) and executes nothing. Set VO_CODE_RUNNER_AGENT to claude, codex, cursor, local, or meta. Use an explicit sanitized task capsule through the AlgoSuite Model Firewall."
4905
5018
  );
4906
5019
  }
4907
5020
  /** Codex JSONL events map identically → reuse the proven parser. */
@@ -4924,14 +5037,14 @@ var init_openai_compatible_runner = __esm({
4924
5037
  describeAuth(env2 = process.env) {
4925
5038
  const hasKey = Boolean(String(env2[OAI_API_KEY_ENV] || "").trim());
4926
5039
  const baseUrl = resolveOaiBaseUrl(env2);
4927
- return `oai-compat endpoint=${baseUrl || "<unset>"} key=${hasKey ? "set" : "MISSING"}`;
5040
+ return `oai-compat RETIRED endpoint=${baseUrl || "<unset>"} key=${hasKey ? "set" : "MISSING"}`;
4928
5041
  }
4929
- /** Best-effort: base URL chosen AND the codex transport is installed. */
5042
+ /** Always unavailable: the lane is retired, so nothing can authenticate it. */
4930
5043
  async checkAuth() {
4931
5044
  return {
4932
5045
  installed: false,
4933
5046
  authenticated: false,
4934
- message: "OpenAI-compatible coding is disabled; sanitized Model Firewall task capsules only"
5047
+ message: "the `oai` lane is RETIRED (PR #8742); OpenAI-compatible coding is disabled \u2014 sanitized Model Firewall task capsules only"
4935
5048
  };
4936
5049
  }
4937
5050
  };
@@ -7080,11 +7193,18 @@ async function collectAgentAvailability({
7080
7193
  new Promise((resolve2) => setTimeout(() => resolve2(null), probeTimeoutMs))
7081
7194
  ]);
7082
7195
  if (!r) return degraded;
7196
+ const installed = Boolean(r?.installed);
7197
+ const authenticated = Boolean(r?.authenticated);
7198
+ const authTier = resolveReportedAuthTier({ authTier: r?.authTier, installed, authenticated });
7083
7199
  return {
7084
7200
  agent,
7085
- installed: Boolean(r?.installed),
7086
- authenticated: Boolean(r?.authenticated),
7087
- ...typeof r?.version === "string" && r.version ? { version: r.version } : {}
7201
+ installed,
7202
+ authenticated,
7203
+ ...typeof r?.version === "string" && r.version ? { version: r.version } : {},
7204
+ // Omitted when unknown, which is what an older daemon's silence already
7205
+ // means — the control-plane schema resolves BOTH to 'unknown'. Never
7206
+ // invent a tier to fill the gap.
7207
+ ...authTier !== AUTH_TIER_UNKNOWN ? { auth_tier: authTier } : {}
7088
7208
  };
7089
7209
  } catch {
7090
7210
  return { agent, installed: false, authenticated: false };
@@ -7147,6 +7267,7 @@ var init_agent_availability = __esm({
7147
7267
  "use strict";
7148
7268
  init_resolve_runner();
7149
7269
  init_agent_auth_probe_process();
7270
+ init_agent_auth_tier();
7150
7271
  DEFAULT_TTL_MS = 5 * 60 * 1e3;
7151
7272
  PROBE_TIMEOUT_MS = 1e4;
7152
7273
  }
@@ -7273,26 +7394,183 @@ var init_local_model_remote_config = __esm({
7273
7394
  }
7274
7395
  });
7275
7396
 
7276
- // ../../scripts/virtual-office/code-runner/account-usage.mjs
7277
- import { spawn as spawn4 } from "node:child_process";
7397
+ // ../../scripts/virtual-office/code-runner/account-usage/shared.mjs
7398
+ import crypto from "node:crypto";
7278
7399
  import fs6 from "node:fs";
7400
+ function accountKey(agent, rawId) {
7401
+ const id = typeof rawId === "string" ? rawId.trim() : "";
7402
+ if (!id) return null;
7403
+ try {
7404
+ return crypto.scryptSync(`${agent}:${id}`, ACCOUNT_KEY_SALT, 8).toString("hex");
7405
+ } catch {
7406
+ return null;
7407
+ }
7408
+ }
7409
+ function makeUsageRow({
7410
+ agent,
7411
+ sevenDay,
7412
+ fiveHour,
7413
+ monthly,
7414
+ source,
7415
+ capturedAt,
7416
+ accountId,
7417
+ sevenDayResetsAt,
7418
+ fiveHourResetsAt,
7419
+ monthlyResetsAt
7420
+ }) {
7421
+ const seven = clampPct(sevenDay);
7422
+ const five = clampPct(fiveHour);
7423
+ const month = clampPct(monthly);
7424
+ if (seven === null && five === null && month === null) return null;
7425
+ const row = {
7426
+ agent,
7427
+ seven_day_used_pct: seven,
7428
+ five_hour_used_pct: five,
7429
+ source
7430
+ };
7431
+ if (month !== null) row.monthly_used_pct = month;
7432
+ if (typeof monthlyResetsAt === "string" && monthlyResetsAt) {
7433
+ row.monthly_resets_at = monthlyResetsAt;
7434
+ }
7435
+ if (typeof capturedAt === "string" && capturedAt) row.captured_at = capturedAt;
7436
+ const key = accountKey(agent, accountId);
7437
+ if (key) row.account_key = key;
7438
+ if (typeof sevenDayResetsAt === "string" && sevenDayResetsAt) {
7439
+ row.seven_day_resets_at = sevenDayResetsAt;
7440
+ }
7441
+ if (typeof fiveHourResetsAt === "string" && fiveHourResetsAt) {
7442
+ row.five_hour_resets_at = fiveHourResetsAt;
7443
+ }
7444
+ return row;
7445
+ }
7446
+ var clampPct, readJson, ACCOUNT_KEY_SALT;
7447
+ var init_shared = __esm({
7448
+ "../../scripts/virtual-office/code-runner/account-usage/shared.mjs"() {
7449
+ "use strict";
7450
+ clampPct = (v) => {
7451
+ const n = typeof v === "number" ? v : typeof v === "string" && v.trim() !== "" ? Number(v) : NaN;
7452
+ return Number.isFinite(n) ? Math.min(100, Math.max(0, Math.round(n))) : null;
7453
+ };
7454
+ readJson = (p) => {
7455
+ try {
7456
+ return JSON.parse(fs6.readFileSync(p, "utf8"));
7457
+ } catch {
7458
+ return null;
7459
+ }
7460
+ };
7461
+ ACCOUNT_KEY_SALT = "algohq/account-usage/v1";
7462
+ }
7463
+ });
7464
+
7465
+ // ../../scripts/virtual-office/code-runner/account-usage/claude.mjs
7279
7466
  import os3 from "node:os";
7280
7467
  import path14 from "node:path";
7281
- function snapshotAgeMs(payload, filePath, now, mtimeMs) {
7282
- const iso = typeof payload?.capturedAt === "string" ? payload.capturedAt : null;
7283
- if (iso) {
7284
- const capturedMs = new Date(iso).getTime();
7285
- if (Number.isFinite(capturedMs)) return now - capturedMs;
7468
+ function usageBaseUrl(env2 = process.env) {
7469
+ const raw = env2.ANTHROPIC_BASE_URL || "https://api.anthropic.com";
7470
+ return String(raw).replace(/\/+$/, "");
7471
+ }
7472
+ function readOAuthToken({ homeDir = os3.homedir(), read = readJson, now = Date.now() } = {}) {
7473
+ const creds = read(path14.join(homeDir, ".claude", ".credentials.json"));
7474
+ const oauth = creds && typeof creds === "object" ? creds.claudeAiOauth : null;
7475
+ if (!oauth || typeof oauth !== "object") return null;
7476
+ const token2 = typeof oauth.accessToken === "string" ? oauth.accessToken.trim() : "";
7477
+ if (!token2) return null;
7478
+ const expiresAt = Number(oauth.expiresAt);
7479
+ if (Number.isFinite(expiresAt) && expiresAt > 0 && expiresAt <= now) return null;
7480
+ return token2;
7481
+ }
7482
+ function readAccountId({ homeDir = os3.homedir(), read = readJson } = {}) {
7483
+ const cfg = read(path14.join(homeDir, ".claude.json"));
7484
+ const account = cfg && typeof cfg === "object" ? cfg.oauthAccount : null;
7485
+ return account && typeof account.accountUuid === "string" ? account.accountUuid : null;
7486
+ }
7487
+ function entryPct(entry) {
7488
+ if (!entry || typeof entry !== "object") return null;
7489
+ for (const field of ["used_percentage", "utilization", "percent"]) {
7490
+ const pct = clampPct(entry[field]);
7491
+ if (pct !== null) return pct;
7286
7492
  }
7287
- const fileMs = mtimeMs(filePath);
7288
- return Number.isFinite(fileMs) ? now - fileMs : null;
7493
+ return null;
7494
+ }
7495
+ function entryResetsAt(entry) {
7496
+ if (!entry || typeof entry !== "object") return null;
7497
+ for (const field of ["resets_at", "reset_at", "resetsAt"]) {
7498
+ const value = entry[field];
7499
+ if (typeof value === "string" && value) return value;
7500
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) {
7501
+ return new Date(value * 1e3).toISOString();
7502
+ }
7503
+ }
7504
+ return null;
7289
7505
  }
7290
- function readClaudeUsage({
7506
+ function parseOAuthUsage(body) {
7507
+ if (!body || typeof body !== "object") return null;
7508
+ let fiveHour = null;
7509
+ let sevenDay = null;
7510
+ const list = Array.isArray(body) ? body : Array.isArray(body.limits) ? body.limits : null;
7511
+ if (list) {
7512
+ for (const entry of list) {
7513
+ const kind = entry && typeof entry.kind === "string" ? entry.kind : "";
7514
+ if ((kind === "five_hour" || kind === "session") && !fiveHour) fiveHour = entry;
7515
+ else if ((kind === "seven_day" || kind === "weekly_all") && !sevenDay) sevenDay = entry;
7516
+ }
7517
+ }
7518
+ if (!fiveHour && body.five_hour) fiveHour = body.five_hour;
7519
+ if (!sevenDay && body.seven_day) sevenDay = body.seven_day;
7520
+ const five = entryPct(fiveHour);
7521
+ const seven = entryPct(sevenDay);
7522
+ if (five === null && seven === null) return null;
7523
+ return {
7524
+ five_hour_used_pct: five,
7525
+ seven_day_used_pct: seven,
7526
+ five_hour_resets_at: entryResetsAt(fiveHour),
7527
+ seven_day_resets_at: entryResetsAt(sevenDay)
7528
+ };
7529
+ }
7530
+ async function readClaudeOAuthUsage({
7531
+ fetchImpl = fetch,
7532
+ env: env2 = process.env,
7533
+ timeoutMs = DEFAULT_TIMEOUT_MS,
7291
7534
  homeDir = os3.homedir(),
7292
- read: rawRead = readJson,
7293
- now = Date.now(),
7294
- mtimeMs = mtimeMsOf
7535
+ read = readJson,
7536
+ now = () => Date.now()
7295
7537
  } = {}) {
7538
+ const token2 = readOAuthToken({ homeDir, read, now: now() });
7539
+ if (!token2) return null;
7540
+ const controller = new AbortController();
7541
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
7542
+ try {
7543
+ const res = await fetchImpl(`${usageBaseUrl(env2)}${USAGE_PATH}`, {
7544
+ method: "GET",
7545
+ headers: {
7546
+ authorization: `Bearer ${token2}`,
7547
+ "anthropic-beta": OAUTH_BETA,
7548
+ "content-type": "application/json",
7549
+ accept: "application/json"
7550
+ },
7551
+ signal: controller.signal
7552
+ });
7553
+ if (!res || !res.ok) return null;
7554
+ const parsed = parseOAuthUsage(await res.json());
7555
+ if (!parsed) return null;
7556
+ return makeUsageRow({
7557
+ agent: "claude",
7558
+ source: "oauth",
7559
+ // The response describes the account AS OF NOW, so the reading time is now.
7560
+ capturedAt: new Date(now()).toISOString(),
7561
+ accountId: readAccountId({ homeDir, read }),
7562
+ sevenDay: parsed.seven_day_used_pct,
7563
+ fiveHour: parsed.five_hour_used_pct,
7564
+ sevenDayResetsAt: parsed.seven_day_resets_at,
7565
+ fiveHourResetsAt: parsed.five_hour_resets_at
7566
+ });
7567
+ } catch {
7568
+ return null;
7569
+ } finally {
7570
+ clearTimeout(timer);
7571
+ }
7572
+ }
7573
+ function readClaudeFileUsage({ homeDir = os3.homedir(), read: rawRead = readJson } = {}) {
7296
7574
  const read = (p) => {
7297
7575
  try {
7298
7576
  return rawRead(p);
@@ -7300,55 +7578,89 @@ function readClaudeUsage({
7300
7578
  return null;
7301
7579
  }
7302
7580
  };
7303
- const statusPath = path14.join(homeDir, ".claude", "claude-usage.json");
7304
- const status = read(statusPath);
7305
- if (status && (status.seven_day || status.five_hour) && isFresh(snapshotAgeMs(status, statusPath, now, mtimeMs))) {
7306
- const entry = {
7581
+ const accountId = readAccountId({ homeDir, read });
7582
+ const status = read(path14.join(homeDir, ".claude", "claude-usage.json"));
7583
+ if (status && (status.seven_day || status.five_hour)) {
7584
+ const row = makeUsageRow({
7307
7585
  agent: "claude",
7308
- seven_day_used_pct: clampPct(status.seven_day?.used_percentage),
7309
- five_hour_used_pct: clampPct(status.five_hour?.used_percentage)
7310
- };
7311
- if (entry.seven_day_used_pct !== null || entry.five_hour_used_pct !== null) return entry;
7586
+ source: "statusline",
7587
+ capturedAt: typeof status.capturedAt === "string" ? status.capturedAt : null,
7588
+ accountId,
7589
+ sevenDay: status.seven_day?.used_percentage,
7590
+ fiveHour: status.five_hour?.used_percentage,
7591
+ sevenDayResetsAt: status.seven_day?.resets_at ?? null,
7592
+ fiveHourResetsAt: status.five_hour?.resets_at ?? null
7593
+ });
7594
+ if (row) return row;
7312
7595
  }
7313
- const weeklyPath = path14.join(homeDir, ".claude", "claude-weekly-usage.json");
7314
- const weekly = read(weeklyPath);
7315
- if (weekly && isFresh(snapshotAgeMs(weekly, weeklyPath, now, mtimeMs))) {
7316
- const entry = {
7596
+ const weekly = read(path14.join(homeDir, ".claude", "claude-weekly-usage.json"));
7597
+ if (weekly) {
7598
+ const row = makeUsageRow({
7317
7599
  agent: "claude",
7318
- seven_day_used_pct: clampPct(weekly.sevenDayPct),
7319
- five_hour_used_pct: clampPct(weekly.fiveHourPct)
7320
- };
7321
- if (entry.seven_day_used_pct !== null || entry.five_hour_used_pct !== null) return entry;
7600
+ source: "file",
7601
+ capturedAt: typeof weekly.capturedAt === "string" ? weekly.capturedAt : null,
7602
+ accountId,
7603
+ sevenDay: weekly.sevenDayPct,
7604
+ fiveHour: weekly.fiveHourPct,
7605
+ sevenDayResetsAt: weekly.sevenDayResetsAt ?? null
7606
+ });
7607
+ if (row) return row;
7322
7608
  }
7323
7609
  return null;
7324
7610
  }
7325
- function collectAccountUsage(opts = {}) {
7326
- const claude = readClaudeUsage(opts);
7327
- return claude ? [claude] : [];
7611
+ async function readClaudeUsage(opts = {}) {
7612
+ try {
7613
+ const live = await readClaudeOAuthUsage(opts);
7614
+ if (live) return live;
7615
+ } catch {
7616
+ }
7617
+ return readClaudeFileUsage(opts);
7328
7618
  }
7619
+ var USAGE_PATH, OAUTH_BETA, DEFAULT_TIMEOUT_MS;
7620
+ var init_claude = __esm({
7621
+ "../../scripts/virtual-office/code-runner/account-usage/claude.mjs"() {
7622
+ "use strict";
7623
+ init_shared();
7624
+ USAGE_PATH = "/api/oauth/usage";
7625
+ OAUTH_BETA = "oauth-2025-04-20";
7626
+ DEFAULT_TIMEOUT_MS = 5e3;
7627
+ }
7628
+ });
7629
+
7630
+ // ../../scripts/virtual-office/code-runner/account-usage/codex.mjs
7631
+ import { spawn as spawn4 } from "node:child_process";
7329
7632
  function weeklyWindow(snapshot2) {
7330
7633
  if (!snapshot2 || typeof snapshot2 !== "object") return null;
7331
7634
  const windows = [snapshot2.primary, snapshot2.secondary].filter(Boolean);
7332
7635
  return windows.find((window) => Number(window?.windowDurationMins) === 7 * 24 * 60) ?? windows.find((window) => Number(window?.windowDurationMins) >= 6 * 24 * 60) ?? null;
7333
7636
  }
7334
- function parseCodexUsage(response) {
7637
+ function resetsAtIso(window) {
7638
+ const raw = Number(window?.resetsAt);
7639
+ if (!Number.isFinite(raw) || raw <= 0) return null;
7640
+ return new Date(raw * 1e3).toISOString();
7641
+ }
7642
+ function parseCodexUsage(response, { now = () => Date.now() } = {}) {
7335
7643
  const result = response?.result;
7336
7644
  const snapshot2 = result?.rateLimitsByLimitId?.codex ?? result?.rateLimits;
7337
7645
  const weekly = weeklyWindow(snapshot2);
7338
7646
  const used = clampPct(weekly?.usedPercent);
7339
7647
  if (used === null) return null;
7340
- return {
7648
+ return makeUsageRow({
7341
7649
  agent: "codex",
7342
- seven_day_used_pct: used,
7343
- five_hour_used_pct: null
7344
- };
7650
+ source: "app-server",
7651
+ capturedAt: new Date(now()).toISOString(),
7652
+ sevenDay: used,
7653
+ fiveHour: null,
7654
+ sevenDayResetsAt: resetsAtIso(weekly)
7655
+ });
7345
7656
  }
7346
7657
  function readCodexUsage({
7347
7658
  spawnImpl = spawn4,
7348
7659
  resolveBinary = resolveCodexBinary,
7349
7660
  timeoutMs = 8e3,
7350
7661
  env: env2 = process.env,
7351
- platform = process.platform
7662
+ platform = process.platform,
7663
+ now = () => Date.now()
7352
7664
  } = {}) {
7353
7665
  return new Promise((resolve2) => {
7354
7666
  let child;
@@ -7370,7 +7682,8 @@ function readCodexUsage({
7370
7682
  child = spawnImpl(binary, ["app-server", "--stdio"], {
7371
7683
  env: env2,
7372
7684
  windowsHide: true,
7373
- shell: platform === "win32" && !/\.exe$/iu.test(String(binary)),
7685
+ shell: false,
7686
+ windowsVerbatimArguments: false,
7374
7687
  stdio: ["pipe", "pipe", "ignore"]
7375
7688
  });
7376
7689
  child.on("error", () => finish(null));
@@ -7394,7 +7707,7 @@ function readCodexUsage({
7394
7707
  child.stdin?.write(`${JSON.stringify({ method: "account/rateLimits/read", id: 2 })}
7395
7708
  `);
7396
7709
  } else if (message?.id === 2) {
7397
- finish(parseCodexUsage(message));
7710
+ finish(parseCodexUsage(message, { now }));
7398
7711
  }
7399
7712
  }
7400
7713
  });
@@ -7412,14 +7725,29 @@ function readCodexUsage({
7412
7725
  }
7413
7726
  });
7414
7727
  }
7415
- async function collectConnectedAccountUsage({ readCodex = readCodexUsage, ...claudeOptions } = {}) {
7416
- const claude = readClaudeUsage(claudeOptions);
7417
- let codex = null;
7418
- try {
7419
- codex = await readCodex();
7420
- } catch {
7728
+ var init_codex = __esm({
7729
+ "../../scripts/virtual-office/code-runner/account-usage/codex.mjs"() {
7730
+ "use strict";
7731
+ init_shared();
7732
+ init_codex_runner();
7421
7733
  }
7422
- return [claude, codex].filter(Boolean);
7734
+ });
7735
+
7736
+ // ../../scripts/virtual-office/code-runner/account-usage/index.mjs
7737
+ function collectAccountUsage(opts = {}) {
7738
+ const claude = readClaudeFileUsage(opts);
7739
+ return claude ? [claude] : [];
7740
+ }
7741
+ async function collectConnectedAccountUsage({
7742
+ readClaude = readClaudeUsage,
7743
+ readCodex = readCodexUsage,
7744
+ ...agentOptions
7745
+ } = {}) {
7746
+ const settled = await Promise.allSettled([
7747
+ readClaude(agentOptions),
7748
+ readCodex(agentOptions)
7749
+ ]);
7750
+ return settled.map((outcome) => outcome.status === "fulfilled" ? outcome.value : null).filter(Boolean);
7423
7751
  }
7424
7752
  function makeAccountUsageProvider({
7425
7753
  ttlMs = DEFAULT_TTL_MS2,
@@ -7447,35 +7775,31 @@ function makeAccountUsageProvider({
7447
7775
  }
7448
7776
  };
7449
7777
  }
7450
- var clampPct, readJson, STALE_AFTER_MS, mtimeMsOf, isFresh, DEFAULT_TTL_MS2;
7778
+ var AGENT_USAGE_CAPABILITY, DEFAULT_TTL_MS2;
7451
7779
  var init_account_usage = __esm({
7452
- "../../scripts/virtual-office/code-runner/account-usage.mjs"() {
7780
+ "../../scripts/virtual-office/code-runner/account-usage/index.mjs"() {
7453
7781
  "use strict";
7454
- init_codex_runner();
7455
- clampPct = (v) => {
7456
- const n = Number(v);
7457
- return Number.isFinite(n) ? Math.min(100, Math.max(0, Math.round(n))) : null;
7458
- };
7459
- readJson = (p) => {
7460
- try {
7461
- return JSON.parse(fs6.readFileSync(p, "utf8"));
7462
- } catch {
7463
- return null;
7464
- }
7465
- };
7466
- STALE_AFTER_MS = 2 * 60 * 60 * 1e3;
7467
- mtimeMsOf = (p) => {
7468
- try {
7469
- return fs6.statSync(p).mtimeMs;
7470
- } catch {
7471
- return null;
7472
- }
7473
- };
7474
- isFresh = (ageMs) => ageMs !== null && ageMs <= STALE_AFTER_MS;
7782
+ init_claude();
7783
+ init_codex();
7784
+ init_shared();
7785
+ AGENT_USAGE_CAPABILITY = Object.freeze({
7786
+ claude: Object.freeze({ five_hour: true, seven_day: true, monthly: false }),
7787
+ codex: Object.freeze({ five_hour: false, seven_day: true, monthly: false }),
7788
+ cursor: Object.freeze({ five_hour: false, seven_day: false, monthly: false }),
7789
+ gemini: Object.freeze({ five_hour: false, seven_day: false, monthly: false })
7790
+ });
7475
7791
  DEFAULT_TTL_MS2 = 5 * 60 * 1e3;
7476
7792
  }
7477
7793
  });
7478
7794
 
7795
+ // ../../scripts/virtual-office/code-runner/account-usage.mjs
7796
+ var init_account_usage2 = __esm({
7797
+ "../../scripts/virtual-office/code-runner/account-usage.mjs"() {
7798
+ "use strict";
7799
+ init_account_usage();
7800
+ }
7801
+ });
7802
+
7479
7803
  // ../../scripts/virtual-office/code-runner/ci-repair-evidence.mjs
7480
7804
  function extractWorkflowRunIds(links = []) {
7481
7805
  const ids = [];
@@ -8268,7 +8592,6 @@ function makeWatchRunner({
8268
8592
  repairAttempt: chain.attempt + 1
8269
8593
  }),
8270
8594
  ...operatorId && operatorId !== "admin" ? { on_behalf_of_operator_id: operatorId } : {},
8271
- agent: "claude",
8272
8595
  dispatch_mode: "fast",
8273
8596
  max_turns: 80,
8274
8597
  max_budget_usd: chain.per_attempt_budget_usd,
@@ -10180,7 +10503,15 @@ var init_agent_process_env = __esm({
10180
10503
  "WINDIR",
10181
10504
  "VO_RUNNER_PREFER_LOGIN",
10182
10505
  "VO_RUNNER_CLAUDE_PREFER_LOGIN",
10183
- "VO_RUNNER_CODEX_PREFER_LOGIN"
10506
+ "VO_RUNNER_CODEX_PREFER_LOGIN",
10507
+ // The tier-1 opt-out (PR #9242). Auth-preference flags are only visible to a
10508
+ // runner's applyAuthEnv() if they are listed HERE: the daemon builds the child
10509
+ // env with buildAgentProcessEnv(process.env) (code-runner-daemon.mjs:117), so
10510
+ // anything absent from this set is stripped before withAnthropicKey ever sees
10511
+ // it. #9242 added VO_RUNNER_PREFER_KEY without this line, which left the
10512
+ // escape hatch inert — an operator who set it still got the subscription.
10513
+ "VO_RUNNER_PREFER_KEY",
10514
+ "VO_RUNNER_CLAUDE_PREFER_KEY"
10184
10515
  ]);
10185
10516
  }
10186
10517
  });
@@ -12444,7 +12775,7 @@ var init_code_runner_daemon = __esm({
12444
12775
  init_runner_capacity();
12445
12776
  init_agent_availability();
12446
12777
  init_local_model_remote_config();
12447
- init_account_usage();
12778
+ init_account_usage2();
12448
12779
  init_pr_watcher();
12449
12780
  init_existing_pr_target();
12450
12781
  init_watch_cycle_coordinator();