@algosuite/vo-mcp 0.2.0-beta.71 → 0.2.0-beta.72

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.
@@ -300,7 +300,7 @@ function normalizeClaudePermissionMode(value) {
300
300
  }
301
301
  return normalized;
302
302
  }
303
- function buildClaudeArgs({ permissionMode = DEFAULT_PERMISSION_MODE, maxTurns, model, effort, maxBudgetUsd, researchHarness = false, toolPolicy = "default", env = process.env } = {}) {
303
+ function buildClaudeArgs({ permissionMode = DEFAULT_PERMISSION_MODE, maxTurns, model, effort, maxBudgetUsd, researchHarness = false, toolPolicy = "default", structuredOutputSchema, env = process.env } = {}) {
304
304
  const effectivePermissionMode = normalizeClaudePermissionMode(permissionMode);
305
305
  if (!["default", "skill_readonly", "frozen_inputs_only"].includes(toolPolicy)) {
306
306
  throw new Error(`unsupported Claude tool policy "${toolPolicy}"`);
@@ -338,6 +338,12 @@ function buildClaudeArgs({ permissionMode = DEFAULT_PERMISSION_MODE, maxTurns, m
338
338
  "none"
339
339
  );
340
340
  }
341
+ if (structuredOutputSchema !== void 0) {
342
+ if (!restrictedSkill || !structuredOutputSchema || typeof structuredOutputSchema !== "object" || Array.isArray(structuredOutputSchema)) {
343
+ throw new Error("structured output schema is allowed only for a restricted skill");
344
+ }
345
+ args.push("--json-schema", JSON.stringify(structuredOutputSchema));
346
+ }
341
347
  if (frozenInputsOnly) {
342
348
  args.push("--strict-mcp-config", "--safe-mode");
343
349
  }
@@ -425,13 +431,15 @@ function extractModelUsage(evt) {
425
431
  // ../../scripts/virtual-office/code-runner/claude-result-event.mjs
426
432
  var CAPPED_RESULT_SUBTYPES = Object.freeze(["error_max_budget_usd", "error_max_turns"]);
427
433
  function buildResultEvent(evt) {
428
- const isError = Boolean(evt.is_error) || evt.subtype === "error_max_turns" || evt.subtype === "error_during_execution";
434
+ const isError = Boolean(evt.is_error) || evt.subtype === "error_max_budget_usd" || evt.subtype === "error_max_turns" || evt.subtype === "error_max_structured_output_retries" || evt.subtype === "error_during_execution";
429
435
  return {
430
436
  kind: "result",
431
437
  isError,
432
438
  costUsd: typeof evt.total_cost_usd === "number" ? evt.total_cost_usd : null,
433
439
  summary: typeof evt.result === "string" && evt.result.length > 0 ? evt.result : evt.subtype || (isError ? "error" : "completed"),
440
+ terminalSubtype: typeof evt.subtype === "string" ? evt.subtype : null,
434
441
  numTurns: typeof evt.num_turns === "number" ? evt.num_turns : null,
442
+ structuredOutput: Object.hasOwn(evt, "structured_output") ? evt.structured_output : null,
435
443
  tokenUsage: extractTokenUsage(evt),
436
444
  modelUsage: extractModelUsage(evt)
437
445
  };
@@ -544,9 +552,145 @@ function applyCliVersionFloor({ versionOutput, env = process.env, log = console.
544
552
  return { refused: !allowUnsafe, check, message };
545
553
  }
546
554
 
555
+ // ../../scripts/virtual-office/code-runner/claude-skill-capability.mjs
556
+ import { spawnSync as spawnSync6 } from "node:child_process";
557
+ import { accessSync, constants, realpathSync as realpathSync2, statSync } from "node:fs";
558
+ import path3 from "node:path";
559
+ var VALIDATED_CLAUDE_SKILL_VERSIONS = Object.freeze(["2.1.263"]);
560
+ var REQUIRED_CLAUDE_SKILL_HELP = Object.freeze([
561
+ "--allowedTools",
562
+ "--disable-slash-commands",
563
+ "--json-schema",
564
+ "--max-budget-usd",
565
+ "--no-chrome",
566
+ "--no-session-persistence",
567
+ "--output-format",
568
+ "--permission-mode",
569
+ "--permission-prompts",
570
+ "--safe-mode",
571
+ "--strict-mcp-config",
572
+ "--tools"
573
+ ]);
574
+ var PROBE_TIMEOUT_MS = 2e3;
575
+ var CACHE_TTL_MS = 5 * 60 * 1e3;
576
+ var cache = /* @__PURE__ */ new Map();
577
+ function hasOption(help, option) {
578
+ const literal = option.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
579
+ return new RegExp(`(^|\\s)${literal}(?=\\s|,|=|<|$)`, "mu").test(help);
580
+ }
581
+ function assessClaudeSkillCapability({ versionOutput, helpOutput }) {
582
+ const version = parseCliVersion(versionOutput);
583
+ if (!version || !VALIDATED_CLAUDE_SKILL_VERSIONS.includes(version)) {
584
+ return { compatible: false, version, reason: "claude version is not in the validated restricted-skill manifest" };
585
+ }
586
+ const help = String(helpOutput ?? "");
587
+ const missing = REQUIRED_CLAUDE_SKILL_HELP.filter((option) => !hasOption(help, option));
588
+ if (missing.length > 0) {
589
+ return { compatible: false, version, reason: `claude help is missing required options: ${missing.join(", ")}` };
590
+ }
591
+ if (!/--permission-prompts[\s\S]{0,300}(?:"none"|\bnone\b)/mu.test(help) || !/--output-format[\s\S]{0,300}\bstream-json\b/mu.test(help)) {
592
+ return { compatible: false, version, reason: "claude help does not prove required none/stream-json values" };
593
+ }
594
+ return { compatible: true, version, reason: "validated restricted-skill CLI contract" };
595
+ }
596
+ function runProbe(bin, args, env, timeoutMs = PROBE_TIMEOUT_MS) {
597
+ if (process.platform === "win32") {
598
+ try {
599
+ const launch = buildWindowsClaudeLaunch({ bin, args, env });
600
+ return spawnSync6(launch.bin, launch.args, {
601
+ ...launch.spawnOptions,
602
+ env,
603
+ encoding: "utf8",
604
+ timeout: timeoutMs
605
+ });
606
+ } catch (error) {
607
+ return { status: null, stdout: "", stderr: "", error };
608
+ }
609
+ }
610
+ return spawnSync6(bin, args, { env, encoding: "utf8", timeout: timeoutMs, windowsHide: true });
611
+ }
612
+ function probeText(probe) {
613
+ return `${String(probe?.stdout ?? "")}
614
+ ${String(probe?.stderr ?? "")}`.trim();
615
+ }
616
+ function resolveClaudeBinaryIdentity(bin = "claude", env = process.env) {
617
+ let resolvedBin = String(bin);
618
+ try {
619
+ if (process.platform === "win32") {
620
+ resolvedBin = buildWindowsClaudeLaunch({ bin: resolvedBin, args: [], env }).bin;
621
+ } else if (!path3.isAbsolute(resolvedBin)) {
622
+ const found = String(env?.PATH ?? "").split(path3.delimiter).find((dir) => {
623
+ try {
624
+ accessSync(path3.join(dir, resolvedBin), constants.X_OK);
625
+ return true;
626
+ } catch {
627
+ return false;
628
+ }
629
+ });
630
+ if (found) resolvedBin = path3.join(found, resolvedBin);
631
+ }
632
+ const canonical = realpathSync2(resolvedBin);
633
+ const stat = statSync(canonical);
634
+ return { resolvedBin: canonical, fingerprint: `${canonical}\0${stat.size}\0${stat.mtimeMs}` };
635
+ } catch {
636
+ const pathValue2 = String(env?.PATH ?? env?.Path ?? "");
637
+ return { resolvedBin, fingerprint: `${resolvedBin}\0${pathValue2}` };
638
+ }
639
+ }
640
+ function probeClaudeSkillCapability({
641
+ bin = "claude",
642
+ env = process.env,
643
+ versionOutput,
644
+ spawnProbe = runProbe,
645
+ now = () => Date.now(),
646
+ cacheTtlMs = CACHE_TTL_MS,
647
+ timeoutMs = PROBE_TIMEOUT_MS,
648
+ freshIdentity = false,
649
+ resolveIdentity = resolveClaudeBinaryIdentity
650
+ } = {}) {
651
+ const identity = resolveIdentity(bin, env);
652
+ const key = identity.fingerprint;
653
+ const existing = cache.get(key);
654
+ if (!freshIdentity && versionOutput === void 0 && existing && now() - existing.at < cacheTtlMs) {
655
+ return existing.value;
656
+ }
657
+ const versionProbe = freshIdentity || versionOutput === void 0 ? spawnProbe(identity.resolvedBin, ["--version"], env, timeoutMs) : null;
658
+ if (versionProbe?.error || versionProbe && versionProbe.status !== 0) {
659
+ return {
660
+ compatible: false,
661
+ version: null,
662
+ resolvedBin: identity.resolvedBin,
663
+ reason: "claude version capability probe failed"
664
+ };
665
+ }
666
+ const effectiveVersionOutput = versionProbe ? probeText(versionProbe) : versionOutput;
667
+ const suppliedVersion = parseCliVersion(effectiveVersionOutput);
668
+ if (existing && now() - existing.at < cacheTtlMs && suppliedVersion === existing.value.version) {
669
+ return existing.value;
670
+ }
671
+ const helpProbe = spawnProbe(identity.resolvedBin, ["--help"], env, timeoutMs);
672
+ if (helpProbe?.error || helpProbe?.status !== 0) {
673
+ return {
674
+ compatible: false,
675
+ version: suppliedVersion,
676
+ resolvedBin: identity.resolvedBin,
677
+ reason: "claude help capability probe failed"
678
+ };
679
+ }
680
+ const assessed = assessClaudeSkillCapability({
681
+ versionOutput: effectiveVersionOutput,
682
+ helpOutput: probeText(helpProbe)
683
+ });
684
+ const value = { ...assessed, resolvedBin: identity.resolvedBin };
685
+ cache.set(key, { at: now(), value });
686
+ return value;
687
+ }
688
+
547
689
  // ../../scripts/virtual-office/code-runner/claude-auth-check.mjs
548
690
  var FIRST_VERSION_TIMEOUT_MS = 4500;
549
691
  var RETRY_VERSION_TIMEOUT_MS = 2e3;
692
+ var AUTH_PROBE_BUDGET_MS = 9500;
693
+ var MIN_SKILL_PROBE_MS = 250;
550
694
  function errorCode(error) {
551
695
  return String(error?.code || "").toUpperCase();
552
696
  }
@@ -572,9 +716,12 @@ async function checkClaudeAuth({
572
716
  spawnVersion = spawnClaudeSync,
573
717
  probeLogin = probeClaudeLoginState,
574
718
  getStoredKey = getAnthropicKey,
575
- env = process.env
719
+ probeSkillCapability = probeClaudeSkillCapability,
720
+ env = process.env,
721
+ now = () => Date.now()
576
722
  } = {}) {
577
723
  try {
724
+ const startedAt = now();
578
725
  let probe = spawnVersion(["--version"], {
579
726
  timeout: FIRST_VERSION_TIMEOUT_MS,
580
727
  encoding: "utf8",
@@ -608,23 +755,40 @@ async function checkClaudeAuth({
608
755
  }
609
756
  const floorGate = applyCliVersionFloor({ versionOutput: probe.stdout, env });
610
757
  if (floorGate.refused) {
611
- return { installed: true, authenticated: false, message: floorGate.message };
758
+ return {
759
+ installed: true,
760
+ authenticated: false,
761
+ version: floorGate.check.version ?? void 0,
762
+ skillCapable: false,
763
+ message: floorGate.message
764
+ };
612
765
  }
613
766
  const loggedIn = retriedAfterTimeout ? null : probeLogin();
614
767
  if (loggedIn === false) {
615
768
  return {
616
769
  installed: true,
617
770
  authenticated: false,
771
+ version: floorGate.check.version ?? void 0,
772
+ skillCapable: false,
618
773
  message: "claude CLI is installed but NOT logged in \u2014 its login is SEPARATE from the Claude Desktop app and the Claude Code IDE extension. Run: claude auth login (Claude subscription), then restart the runner."
619
774
  };
620
775
  }
776
+ const authTier = resolveClaudeAuthTier({ env, loggedIn, getStoredKey });
777
+ const remainingMs = AUTH_PROBE_BUDGET_MS - (now() - startedAt);
778
+ const skillCapability = loggedIn === true && remainingMs >= MIN_SKILL_PROBE_MS ? probeSkillCapability({
779
+ versionOutput: probe.stdout,
780
+ env,
781
+ timeoutMs: Math.min(2e3, remainingMs)
782
+ }) : { compatible: false };
621
783
  return {
622
784
  installed: true,
623
785
  authenticated: true,
786
+ version: floorGate.check.version ?? void 0,
787
+ skillCapable: skillCapability.compatible === true,
624
788
  // Dispatch-time billing signal, carried on the same probe that already
625
789
  // paid for the login read. Never sent for a non-authenticated result:
626
790
  // there is no tier without a working credential.
627
- authTier: resolveClaudeAuthTier({ env, loggedIn, getStoredKey }),
791
+ authTier,
628
792
  message: loggedIn === true ? "claude CLI installed and logged in (claude auth status)" : "claude binary found (login state unknown \u2014 auth check is best-effort)"
629
793
  };
630
794
  } catch (error) {
@@ -647,8 +811,8 @@ var ClaudeRunner = class {
647
811
  get binary() {
648
812
  return "claude";
649
813
  }
650
- buildArgs({ permissionMode, maxTurns, model, effort, maxBudgetUsd, researchHarness, toolPolicy } = {}) {
651
- return buildClaudeArgs({ permissionMode, maxTurns, model, effort, maxBudgetUsd, researchHarness, toolPolicy });
814
+ buildArgs({ permissionMode, maxTurns, model, effort, maxBudgetUsd, researchHarness, toolPolicy, structuredOutputSchema } = {}) {
815
+ return buildClaudeArgs({ permissionMode, maxTurns, model, effort, maxBudgetUsd, researchHarness, toolPolicy, structuredOutputSchema });
652
816
  }
653
817
  parseEvent(line) {
654
818
  return parseStreamEvent(line);
@@ -672,11 +836,14 @@ var ClaudeRunner = class {
672
836
  async checkAuth() {
673
837
  return checkClaudeAuth();
674
838
  }
839
+ checkSkillCapability({ bin = this.binary, env = process.env } = {}) {
840
+ return probeClaudeSkillCapability({ bin, env, freshIdentity: true });
841
+ }
675
842
  };
676
843
  var claudeRunner = new ClaudeRunner();
677
844
 
678
845
  // ../../scripts/virtual-office/code-runner/codex-runner.mjs
679
- import { spawnSync as spawnSync6 } from "node:child_process";
846
+ import { spawnSync as spawnSync7 } from "node:child_process";
680
847
  import { existsSync as existsSync3 } from "node:fs";
681
848
  import { win32 } from "node:path";
682
849
 
@@ -928,7 +1095,7 @@ function parseCodexEvent(line) {
928
1095
  return null;
929
1096
  }
930
1097
  var CodexRunner = class {
931
- constructor({ spawn: spawn2 = spawnSync6, resolveBinary = resolveCodexBinary, env = process.env } = {}) {
1098
+ constructor({ spawn: spawn2 = spawnSync7, resolveBinary = resolveCodexBinary, env = process.env } = {}) {
932
1099
  this.spawn = spawn2;
933
1100
  this.resolveBinary = resolveBinary;
934
1101
  this.env = env;
@@ -1039,7 +1206,7 @@ ${login.stderr || ""}`.trim();
1039
1206
  var codexRunner = new CodexRunner();
1040
1207
 
1041
1208
  // ../../scripts/virtual-office/code-runner/cursor-runner.mjs
1042
- import { spawnSync as spawnSync7 } from "node:child_process";
1209
+ import { spawnSync as spawnSync8 } from "node:child_process";
1043
1210
  function buildCursorArgs({ model, prompt } = {}) {
1044
1211
  const args = ["-p", "--output-format", "stream-json", "--force"];
1045
1212
  if (model) {
@@ -1144,7 +1311,7 @@ var CursorRunner = class {
1144
1311
  /** Best-effort: is `cursor-agent` on PATH? Never throws. */
1145
1312
  async checkAuth() {
1146
1313
  try {
1147
- const { status, error } = spawnSync7("cursor-agent", ["--version"], {
1314
+ const { status, error } = spawnSync8("cursor-agent", ["--version"], {
1148
1315
  shell: false,
1149
1316
  windowsHide: true,
1150
1317
  timeout: 3e3,