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

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.
@@ -177,27 +177,46 @@ function getAnthropicKey({ EntryCtor = defaultEntryCtor() } = {}) {
177
177
  }
178
178
  var PREFER_LOGIN_ENV = "VO_RUNNER_PREFER_LOGIN";
179
179
  var CLAUDE_PREFER_LOGIN_ENV = "VO_RUNNER_CLAUDE_PREFER_LOGIN";
180
+ var PREFER_KEY_ENV = "VO_RUNNER_PREFER_KEY";
181
+ var CLAUDE_PREFER_KEY_ENV = "VO_RUNNER_CLAUDE_PREFER_KEY";
180
182
  function isTruthyFlag(v) {
181
183
  const s = String(v ?? "").trim().toLowerCase();
182
184
  return s === "1" || s === "true" || s === "yes" || s === "on";
183
185
  }
184
- function withAnthropicKey(baseEnv = {}, { getKey = getAnthropicKey } = {}) {
185
- if (isTruthyFlag(baseEnv[CLAUDE_PREFER_LOGIN_ENV]) || isTruthyFlag(baseEnv[PREFER_LOGIN_ENV])) {
186
+ function wantsLogin(env) {
187
+ return isTruthyFlag(env[CLAUDE_PREFER_LOGIN_ENV]) || isTruthyFlag(env[PREFER_LOGIN_ENV]);
188
+ }
189
+ function wantsKey(env) {
190
+ return isTruthyFlag(env[CLAUDE_PREFER_KEY_ENV]) || isTruthyFlag(env[PREFER_KEY_ENV]);
191
+ }
192
+ function withAnthropicKey(baseEnv = {}, { getKey = getAnthropicKey, probeLogin = probeClaudeLoginState } = {}) {
193
+ if (wantsLogin(baseEnv)) {
186
194
  const next = { ...baseEnv };
187
195
  delete next.ANTHROPIC_API_KEY;
188
196
  return next;
189
197
  }
190
198
  if (baseEnv.ANTHROPIC_API_KEY) return { ...baseEnv };
191
199
  const key = getKey();
192
- return key ? { ...baseEnv, ANTHROPIC_API_KEY: key } : { ...baseEnv };
200
+ if (!key) return { ...baseEnv };
201
+ if (!wantsKey(baseEnv) && probeLogin() === true) return { ...baseEnv };
202
+ return { ...baseEnv, ANTHROPIC_API_KEY: key };
203
+ }
204
+ function claudeCostBasis(env = process.env) {
205
+ return String(env.ANTHROPIC_API_KEY || "").trim() ? "vendor_billed" : "subscription_api_equivalent";
193
206
  }
194
- function describeAnthropicAuthSource(baseEnv = {}, { getKey = getAnthropicKey } = {}) {
195
- if (isTruthyFlag(baseEnv[CLAUDE_PREFER_LOGIN_ENV]) || isTruthyFlag(baseEnv[PREFER_LOGIN_ENV])) {
207
+ function describeAnthropicAuthSource(baseEnv = {}, { getKey = getAnthropicKey, probeLogin = probeClaudeLoginState } = {}) {
208
+ if (wantsLogin(baseEnv)) {
196
209
  return "claude auth login (VO_RUNNER_PREFER_LOGIN set \u2014 any API key ignored)";
197
210
  }
198
211
  if (baseEnv.ANTHROPIC_API_KEY) return "ANTHROPIC_API_KEY from environment";
199
- if (getKey()) return "ANTHROPIC_API_KEY from OS keychain";
200
- return "claude auth login session (no API key set)";
212
+ if (!getKey()) return "claude auth login session (no API key set)";
213
+ if (wantsKey(baseEnv)) {
214
+ return "ANTHROPIC_API_KEY from OS keychain (VO_RUNNER_PREFER_KEY set \u2014 subscription ignored)";
215
+ }
216
+ if (probeLogin() === true) {
217
+ return "claude auth login session (subscription beats the stored keychain key)";
218
+ }
219
+ return "ANTHROPIC_API_KEY from OS keychain";
201
220
  }
202
221
  function probeClaudeLoginState({
203
222
  spawn: spawn2 = spawnSync2,
@@ -375,6 +394,36 @@ function parseClaudeStreamEvent(line) {
375
394
  return event.type === "result" ? buildResultEvent(event) : null;
376
395
  }
377
396
 
397
+ // ../../scripts/virtual-office/code-runner/agent-auth-tier.mjs
398
+ var AUTH_TIER_SUBSCRIPTION = "subscription";
399
+ var AUTH_TIER_API_KEY = "api_key";
400
+ var AUTH_TIER_LOCAL = "local";
401
+ var AUTH_TIER_UNKNOWN = "unknown";
402
+ var AUTH_TIERS = Object.freeze([
403
+ AUTH_TIER_SUBSCRIPTION,
404
+ AUTH_TIER_API_KEY,
405
+ AUTH_TIER_LOCAL,
406
+ AUTH_TIER_UNKNOWN
407
+ ]);
408
+ var COST_BASIS_TO_TIER = Object.freeze({
409
+ subscription_api_equivalent: AUTH_TIER_SUBSCRIPTION,
410
+ vendor_billed: AUTH_TIER_API_KEY,
411
+ local_zero: AUTH_TIER_LOCAL
412
+ });
413
+ function authTierFromCostBasis(costBasis) {
414
+ return COST_BASIS_TO_TIER[String(costBasis ?? "")] ?? AUTH_TIER_UNKNOWN;
415
+ }
416
+ function safeAuthTier(compute) {
417
+ try {
418
+ return normalizeAuthTier(compute());
419
+ } catch {
420
+ return AUTH_TIER_UNKNOWN;
421
+ }
422
+ }
423
+ function normalizeAuthTier(value) {
424
+ return AUTH_TIERS.includes(value) ? value : AUTH_TIER_UNKNOWN;
425
+ }
426
+
378
427
  // ../../scripts/virtual-office/code-runner/cli-version-floor.mjs
379
428
  var MIN_CLAUDE_CLI_VERSION = "2.1.218";
380
429
  var 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. 2.1.218 fixed Windows paths with a lowercase-\\u segment (e.g. ...\\utils\\, ...\\ui\\) being corrupted into CJK in tool inputs, making those files silently inaccessible \u2014 the fleet is Windows and 1,376 tracked files sit under utils/ alone. Update: npm install -g @anthropic-ai/claude-code (or the native installer).";
@@ -440,9 +489,22 @@ function isTimeout(probe) {
440
489
  function notFound(probe) {
441
490
  return errorCode(probe?.error) === "ENOENT";
442
491
  }
492
+ function resolveClaudeAuthTier({
493
+ env = process.env,
494
+ loggedIn = null,
495
+ getStoredKey = getAnthropicKey
496
+ } = {}) {
497
+ return safeAuthTier(() => {
498
+ const spawnEnv = withAnthropicKey(env, { getKey: getStoredKey, probeLogin: () => loggedIn });
499
+ const tier = authTierFromCostBasis(claudeCostBasis(spawnEnv));
500
+ if (tier === AUTH_TIER_SUBSCRIPTION && loggedIn !== true) return AUTH_TIER_UNKNOWN;
501
+ return tier;
502
+ });
503
+ }
443
504
  async function checkClaudeAuth({
444
505
  spawnVersion = spawnClaudeSync,
445
506
  probeLogin = probeClaudeLoginState,
507
+ getStoredKey = getAnthropicKey,
446
508
  env = process.env
447
509
  } = {}) {
448
510
  try {
@@ -492,6 +554,10 @@ async function checkClaudeAuth({
492
554
  return {
493
555
  installed: true,
494
556
  authenticated: true,
557
+ // Dispatch-time billing signal, carried on the same probe that already
558
+ // paid for the login read. Never sent for a non-authenticated result:
559
+ // there is no tier without a working credential.
560
+ authTier: resolveClaudeAuthTier({ env, loggedIn, getStoredKey }),
495
561
  message: loggedIn === true ? "claude CLI installed and logged in (claude auth status)" : "claude binary found (login state unknown \u2014 auth check is best-effort)"
496
562
  };
497
563
  } catch (error) {
@@ -536,7 +602,7 @@ var ClaudeRunner = class {
536
602
  return withAnthropicKey(env);
537
603
  }
538
604
  costBasis(env = process.env) {
539
- return String(env.ANTHROPIC_API_KEY || "").trim() ? "vendor_billed" : "subscription_api_equivalent";
605
+ return claudeCostBasis(env);
540
606
  }
541
607
  /** Describe which Anthropic auth source the spawn will use (for runner logs). */
542
608
  describeAuth(env = process.env) {
@@ -811,6 +877,19 @@ var CodexRunner = class {
811
877
  costBasis(env = process.env) {
812
878
  return String(env.OPENAI_API_KEY || env.CODEX_API_KEY || "").trim() ? "vendor_billed" : "subscription_api_equivalent";
813
879
  }
880
+ /**
881
+ * Dispatch-time billing tier for the NEXT codex spawn, from the SAME facts
882
+ * checkAuth() already gathered — no extra subprocess. applyAuthEnv() is a
883
+ * keychain read in this process (@napi-rs/keyring), not a spawn.
884
+ *
885
+ * Codex is the agent where this signal was already computed and then thrown
886
+ * away: checkAuth() distinguished "API key available (no persisted ChatGPT
887
+ * login)" from a real login, but only inside a `message` string that the
888
+ * heartbeat schema strips before storage. This gives that fact a typed home.
889
+ */
890
+ authTier(env = this.env) {
891
+ return safeAuthTier(() => authTierFromCostBasis(this.costBasis(this.applyAuthEnv(env))));
892
+ }
814
893
  /** Best-effort binary + persisted-login probe. Never throws or spends tokens. */
815
894
  async checkAuth() {
816
895
  try {
@@ -844,6 +923,7 @@ ${login.stderr || ""}`.trim();
844
923
  installed: true,
845
924
  authenticated: true,
846
925
  ...versionField,
926
+ authTier: this.authTier(),
847
927
  message: "codex API key available (no persisted ChatGPT login)"
848
928
  };
849
929
  }
@@ -858,6 +938,7 @@ ${login.stderr || ""}`.trim();
858
938
  installed: true,
859
939
  authenticated: true,
860
940
  ...versionField,
941
+ authTier: this.authTier(),
861
942
  message: output || "codex login status succeeded"
862
943
  };
863
944
  } catch (err) {
@@ -960,6 +1041,16 @@ var CursorRunner = class {
960
1041
  costBasis(env = process.env) {
961
1042
  return env.CURSOR_API_KEY ? "vendor_billed" : "unknown";
962
1043
  }
1044
+ /**
1045
+ * Dispatch-time billing tier. Inherits costBasis()'s deliberate refusal to
1046
+ * guess: a prior interactive `cursor-agent login` has undocumented
1047
+ * subscription semantics, so it reports 'unknown' rather than claiming a
1048
+ * flat-cost seat the runner cannot actually prove. Keychain read only — the
1049
+ * heartbeat never pays for a subprocess to answer this.
1050
+ */
1051
+ authTier(env = process.env) {
1052
+ return safeAuthTier(() => authTierFromCostBasis(this.costBasis(this.applyAuthEnv(env))));
1053
+ }
963
1054
  /** Best-effort: is `cursor-agent` on PATH? Never throws. */
964
1055
  async checkAuth() {
965
1056
  try {
@@ -985,6 +1076,7 @@ var CursorRunner = class {
985
1076
  return {
986
1077
  installed: true,
987
1078
  authenticated: true,
1079
+ authTier: this.authTier(),
988
1080
  message: "cursor-agent found (EXPERIMENTAL: headless mode may need a TTY; auth check is best-effort)"
989
1081
  };
990
1082
  } catch (err) {
@@ -1121,6 +1213,10 @@ var LocalModelRunner = class {
1121
1213
  costBasis() {
1122
1214
  return "local_zero";
1123
1215
  }
1216
+ /** Dispatch-time billing tier: local inference is never billed by a vendor. */
1217
+ authTier() {
1218
+ return safeAuthTier(() => authTierFromCostBasis(this.costBasis()));
1219
+ }
1124
1220
  describeAuth(env = process.env) {
1125
1221
  const authEnv = this.applyAuthEnv(env);
1126
1222
  const provider = resolveLocalProvider(this.env);
@@ -1178,6 +1274,7 @@ var LocalModelRunner = class {
1178
1274
  return {
1179
1275
  installed: true,
1180
1276
  authenticated: true,
1277
+ authTier: this.authTier(),
1181
1278
  message: `${provider} reachable; model "${model}" configured (local-only, no cloud spend)`
1182
1279
  };
1183
1280
  }