@algosuite/vo-mcp 0.2.0-beta.30 → 0.2.0-beta.34

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.
@@ -1118,8 +1118,180 @@ var CursorRunner = class {
1118
1118
  };
1119
1119
  var cursorRunner = new CursorRunner();
1120
1120
 
1121
+ // ../../scripts/virtual-office/code-runner/ollama-agent-tools.mjs
1122
+ var MAX_READ_BYTES = 64 * 1024;
1123
+ var MAX_WRITE_BYTES = 512 * 1024;
1124
+ var TOOL_DEFS = [
1125
+ {
1126
+ type: "function",
1127
+ function: {
1128
+ name: "read_file",
1129
+ description: "Read a UTF-8 text file inside the working directory. Returns up to 64 KiB.",
1130
+ parameters: {
1131
+ type: "object",
1132
+ properties: {
1133
+ path: { type: "string", description: "File path relative to the working directory." }
1134
+ },
1135
+ required: ["path"]
1136
+ }
1137
+ }
1138
+ },
1139
+ {
1140
+ type: "function",
1141
+ function: {
1142
+ name: "list_files",
1143
+ description: "List entries in a directory inside the working directory (files and subdirs).",
1144
+ parameters: {
1145
+ type: "object",
1146
+ properties: {
1147
+ path: { type: "string", description: 'Directory path relative to the working directory. Default ".".' }
1148
+ }
1149
+ }
1150
+ }
1151
+ },
1152
+ {
1153
+ type: "function",
1154
+ function: {
1155
+ name: "write_file",
1156
+ description: "Create or overwrite a UTF-8 text file inside the working directory. Parent dirs are created.",
1157
+ parameters: {
1158
+ type: "object",
1159
+ properties: {
1160
+ path: { type: "string", description: "File path relative to the working directory." },
1161
+ content: { type: "string", description: "Full new file contents." }
1162
+ },
1163
+ required: ["path", "content"]
1164
+ }
1165
+ }
1166
+ }
1167
+ ];
1168
+ var TOOL_NAMES = TOOL_DEFS.map((t) => t.function.name);
1169
+ var READ_ONLY_TOOL_NAMES = Object.freeze(["read_file", "list_files"]);
1170
+ var READ_ONLY_TOOL_DEFS = Object.freeze(
1171
+ TOOL_DEFS.filter((tool) => READ_ONLY_TOOL_NAMES.includes(tool.function.name))
1172
+ );
1173
+
1174
+ // ../../scripts/virtual-office/code-runner/ollama-agent-core.mjs
1175
+ var MAX_TURNS_DEFAULT = 20;
1176
+ var NUM_CTX_DEFAULT = 16384;
1177
+ function parseOllamaAgentEvent(line) {
1178
+ const trimmed = String(line || "").trim();
1179
+ if (!trimmed) return null;
1180
+ let evt;
1181
+ try {
1182
+ evt = JSON.parse(trimmed);
1183
+ } catch {
1184
+ return null;
1185
+ }
1186
+ if (!evt || typeof evt !== "object") return null;
1187
+ if (evt.type === "progress") {
1188
+ const text = String(evt.text || "").trim();
1189
+ return text ? { kind: "progress", text } : null;
1190
+ }
1191
+ if (evt.type === "tool") {
1192
+ const via = evt.recovered ? " (recovered from text)" : "";
1193
+ const label = `${evt.ok === false ? "tool failed" : "tool"}: ${evt.name}${evt.path ? ` ${evt.path}` : ""}${via}`;
1194
+ return { kind: "progress", text: label };
1195
+ }
1196
+ if (evt.type === "result") {
1197
+ const usage = evt.usage || null;
1198
+ const tokenUsage = usage ? { inputTokens: usage.inputTokens ?? null, outputTokens: usage.outputTokens ?? null, totalTokens: usage.totalTokens ?? null } : void 0;
1199
+ return {
1200
+ kind: "result",
1201
+ isError: Boolean(evt.isError),
1202
+ costUsd: 0,
1203
+ // sovereign local inference is free — no meter.
1204
+ summary: String(evt.summary || (evt.isError ? "local run failed" : "completed")),
1205
+ numTurns: Number.isInteger(evt.numTurns) ? evt.numTurns : null,
1206
+ ...tokenUsage ? { tokenUsage } : {},
1207
+ // Pass the sovereign receipt through to the daemon. Omitted entirely when
1208
+ // absent so the event shape is unchanged for every other transport.
1209
+ ...evt.receipt ? { receipt: evt.receipt } : {}
1210
+ };
1211
+ }
1212
+ return null;
1213
+ }
1214
+
1215
+ // ../../scripts/virtual-office/code-runner/ollama-native-transport.mjs
1216
+ import { fileURLToPath } from "node:url";
1217
+ import { dirname, join } from "node:path";
1218
+ var DEFAULT_LOCAL_TRANSPORT = "codex";
1219
+ var LOCAL_NATIVE_PROFILES = Object.freeze(["coding", "verification"]);
1220
+ var DEFAULT_LOCAL_NATIVE_PROFILE = "coding";
1221
+ function resolveLocalNativeProfile(env = process.env) {
1222
+ const profile = String(env.VO_CODE_RUNNER_LOCAL_PROFILE || "").trim().toLowerCase() || DEFAULT_LOCAL_NATIVE_PROFILE;
1223
+ if (!LOCAL_NATIVE_PROFILES.includes(profile)) {
1224
+ throw new Error(`local-model runner (native): unknown profile "${profile}" (coding|verification).`);
1225
+ }
1226
+ return profile;
1227
+ }
1228
+ function resolveLocalTransport(env = process.env) {
1229
+ return String(env.VO_CODE_RUNNER_LOCAL_TRANSPORT || "").trim().toLowerCase() === "native" ? "native" : DEFAULT_LOCAL_TRANSPORT;
1230
+ }
1231
+ function ollamaAgentScriptPath() {
1232
+ return join(dirname(fileURLToPath(import.meta.url)), "ollama-agent.mjs");
1233
+ }
1234
+ function posIntOr(raw, fallback) {
1235
+ const n = Number(String(raw ?? "").trim());
1236
+ return Number.isInteger(n) && n > 0 ? n : fallback;
1237
+ }
1238
+ function buildOllamaAgentArgs({ model, numCtx, maxTurns, profile = DEFAULT_LOCAL_NATIVE_PROFILE } = {}) {
1239
+ return [
1240
+ ollamaAgentScriptPath(),
1241
+ "--model",
1242
+ String(model),
1243
+ "--profile",
1244
+ String(profile),
1245
+ "--num-ctx",
1246
+ String(posIntOr(numCtx, NUM_CTX_DEFAULT)),
1247
+ "--max-turns",
1248
+ String(posIntOr(maxTurns, MAX_TURNS_DEFAULT))
1249
+ ];
1250
+ }
1251
+ function buildLocalNativeArgs(opts = {}, env = process.env) {
1252
+ const provider = resolveLocalProvider(env);
1253
+ if (provider !== "ollama") {
1254
+ throw new Error(
1255
+ `local-model runner (native): the native tool-loop executor speaks Ollama's /api/chat; provider "${provider}" is not supported on native transport. Set VO_CODE_RUNNER_LOCAL_PROVIDER=ollama, or use VO_CODE_RUNNER_LOCAL_TRANSPORT=codex for LM Studio.`
1256
+ );
1257
+ }
1258
+ const model = resolveLocalModel(env);
1259
+ if (!model) {
1260
+ throw new Error(
1261
+ "local-model runner (native): set VO_CODE_RUNNER_LOCAL_MODEL to a coding model your Ollama server already has (e.g. qwen2.5-coder:7b). Refusing to run with no explicit model (fail-closed)."
1262
+ );
1263
+ }
1264
+ if (!isValidLocalModel(model)) {
1265
+ throw new Error(`local-model runner (native): "${model}" is not a valid local model id.`);
1266
+ }
1267
+ const baseUrl = resolveLocalBaseUrl(env);
1268
+ if (baseUrl && !isLoopbackBaseUrl(baseUrl)) {
1269
+ throw new Error(
1270
+ "local-model runner (native): VO_CODE_RUNNER_LOCAL_BASE_URL must be a loopback http(s) URL (localhost / 127.0.0.1 / [::1]). Remote endpoints are refused \u2014 use the Model Firewall lanes."
1271
+ );
1272
+ }
1273
+ return buildOllamaAgentArgs({
1274
+ model,
1275
+ profile: resolveLocalNativeProfile(env),
1276
+ numCtx: env.VO_CODE_RUNNER_LOCAL_NUM_CTX,
1277
+ maxTurns: env.VO_CODE_RUNNER_LOCAL_MAX_TURNS
1278
+ });
1279
+ }
1280
+
1121
1281
  // ../../scripts/virtual-office/code-runner/local-model-runner.mjs
1122
1282
  var LOCAL_API_KEY_ENV = "VO_CODE_RUNNER_LOCAL_API_KEY";
1283
+ var FORBIDDEN_LOCAL_CHILD_CREDENTIALS = /* @__PURE__ */ new Set([
1284
+ "ANTHROPIC_API_KEY",
1285
+ "AWS_ACCESS_KEY_ID",
1286
+ "AWS_SECRET_ACCESS_KEY",
1287
+ "AWS_SESSION_TOKEN",
1288
+ "FIREBASE_TOKEN",
1289
+ "GH_TOKEN",
1290
+ "GITHUB_TOKEN",
1291
+ "GOOGLE_API_KEY",
1292
+ "GOOGLE_APPLICATION_CREDENTIALS",
1293
+ "OPENAI_API_KEY"
1294
+ ]);
1123
1295
  var LOCAL_PROVIDERS = ["ollama", "lmstudio"];
1124
1296
  var DEFAULT_LOCAL_PROVIDER = "ollama";
1125
1297
  var LOCAL_PROBE_URLS = {
@@ -1196,6 +1368,9 @@ function buildLocalArgs(opts = {}, env = process.env) {
1196
1368
  }
1197
1369
  function applyLocalAuthEnv(baseEnv = process.env, configEnv = process.env) {
1198
1370
  const out = withAgentKey("local", baseEnv);
1371
+ for (const key of Object.keys(out)) {
1372
+ if (FORBIDDEN_LOCAL_CHILD_CREDENTIALS.has(key.toUpperCase())) delete out[key];
1373
+ }
1199
1374
  const baseUrl = resolveLocalBaseUrl(configEnv);
1200
1375
  if (baseUrl && isLoopbackBaseUrl(baseUrl) && resolveLocalProvider(configEnv) === "ollama" && !String(out.OLLAMA_HOST || "").trim()) {
1201
1376
  out.OLLAMA_HOST = baseUrl.replace(/\/+$/, "");
@@ -1214,19 +1389,25 @@ var LocalModelRunner = class {
1214
1389
  this.env = env;
1215
1390
  this.fetchImpl = fetchImpl;
1216
1391
  }
1217
- /** Codex is the transport binary; the model/endpoint are the user's. */
1392
+ /**
1393
+ * Transport binary. codex transport → the codex CLI. native transport →
1394
+ * this daemon's own node (process.execPath), which runs ollama-agent.mjs; no
1395
+ * external CLI is involved on the native path.
1396
+ */
1218
1397
  get binary() {
1219
- return this.resolveBinary();
1398
+ return resolveLocalTransport(this.env) === "native" ? process.execPath : this.resolveBinary();
1220
1399
  }
1221
1400
  buildArgs(opts = {}) {
1222
- return buildLocalArgs(opts, this.env);
1401
+ return resolveLocalTransport(this.env) === "native" ? buildLocalNativeArgs(opts, this.env) : buildLocalArgs(opts, this.env);
1223
1402
  }
1224
1403
  /**
1225
- * Codex JSONL events map identically, except a sovereign local model has no
1226
- * vendor bill. Codex omits total_cost_usd for OSS runs; turn that known fact
1227
- * into a measured zero at the producer so readers never have to guess.
1404
+ * native transport parse the executor's own JSONL contract. codex transport
1405
+ * codex JSONL maps identically, except a sovereign local model has no vendor
1406
+ * bill: codex omits total_cost_usd for OSS runs, so turn that known fact into a
1407
+ * measured zero at the producer. (The native parser already stamps costUsd:0.)
1228
1408
  */
1229
1409
  parseEvent(line) {
1410
+ if (resolveLocalTransport(this.env) === "native") return parseOllamaAgentEvent(line);
1230
1411
  const event = parseCodexEvent(line);
1231
1412
  return event?.kind === "result" ? { ...event, costUsd: 0 } : event;
1232
1413
  }
package/dist/cli.js CHANGED
@@ -1079,6 +1079,42 @@ function toEventPerModelVerdicts(src) {
1079
1079
  };
1080
1080
  });
1081
1081
  }
1082
+ function aggregateEventTokenUsage(src, engineUsage) {
1083
+ if (engineUsage !== void 0) {
1084
+ const hasIn = Object.keys(engineUsage.per_model_tokens_in).length > 0;
1085
+ const hasOut = Object.keys(engineUsage.per_model_tokens_out).length > 0;
1086
+ return {
1087
+ per_model_tokens_in: hasIn ? engineUsage.per_model_tokens_in : null,
1088
+ per_model_tokens_out: hasOut ? engineUsage.per_model_tokens_out : null,
1089
+ total_cost_usd: engineUsage.cost_micro_usd === null ? null : engineUsage.cost_micro_usd / 1e6
1090
+ };
1091
+ }
1092
+ const tokensIn = {};
1093
+ const tokensOut = {};
1094
+ let anyTokensIn = false;
1095
+ let anyTokensOut = false;
1096
+ let costMicroUsd = 0;
1097
+ let anyCost = false;
1098
+ for (const v of src) {
1099
+ if (typeof v.input_tokens === "number") {
1100
+ tokensIn[v.model] = (tokensIn[v.model] ?? 0) + v.input_tokens;
1101
+ anyTokensIn = true;
1102
+ }
1103
+ if (typeof v.output_tokens === "number") {
1104
+ tokensOut[v.model] = (tokensOut[v.model] ?? 0) + v.output_tokens;
1105
+ anyTokensOut = true;
1106
+ }
1107
+ if (typeof v.cost_micro_usd === "number") {
1108
+ costMicroUsd += v.cost_micro_usd;
1109
+ anyCost = true;
1110
+ }
1111
+ }
1112
+ return {
1113
+ per_model_tokens_in: anyTokensIn ? tokensIn : null,
1114
+ per_model_tokens_out: anyTokensOut ? tokensOut : null,
1115
+ total_cost_usd: anyCost ? costMicroUsd / 1e6 : null
1116
+ };
1117
+ }
1082
1118
  function toEventSynthesizedVerdict(src) {
1083
1119
  return {
1084
1120
  verdict: src.verdict,
@@ -2778,7 +2814,8 @@ async function handleCheckHollowTest(deps, rawInput, signal) {
2778
2814
  synthesized_verdict: synthForEvent,
2779
2815
  consensus_confidence: engineResult.synthesized_verdict.confidence,
2780
2816
  duration_ms: engineResult.duration_ms,
2781
- consensus_engine_version: engineResult.engine_version
2817
+ consensus_engine_version: engineResult.engine_version,
2818
+ ...aggregateEventTokenUsage(engineResult.per_model_verdicts, engineResult.token_usage)
2782
2819
  };
2783
2820
  const payload = {
2784
2821
  verdict: engineResult.synthesized_verdict.verdict,
@@ -2961,7 +2998,8 @@ async function handleVerifyAnswer(deps, rawInput, signal) {
2961
2998
  synthesized_verdict: synthForEvent,
2962
2999
  consensus_confidence: engineResult.synthesized_verdict.confidence,
2963
3000
  duration_ms: engineResult.duration_ms,
2964
- consensus_engine_version: engineResult.engine_version
3001
+ consensus_engine_version: engineResult.engine_version,
3002
+ ...aggregateEventTokenUsage(engineResult.per_model_verdicts, engineResult.token_usage)
2965
3003
  };
2966
3004
  const payload = {
2967
3005
  verdict: engineResult.synthesized_verdict.verdict,
@@ -2970,6 +3008,7 @@ async function handleVerifyAnswer(deps, rawInput, signal) {
2970
3008
  synthesized_verdict: synthForEvent,
2971
3009
  engine_version: engineResult.engine_version,
2972
3010
  degraded: engineResult.degraded,
3011
+ ...engineResult.quorum_failed === true ? { quorum_failed: true } : {},
2973
3012
  gate_type: gateType,
2974
3013
  ...kbResult.error !== null ? { kb_unavailable: true } : {},
2975
3014
  ...kbTruncated > 0 ? { kb_rules_truncated: kbTruncated } : {}
@@ -3226,7 +3265,8 @@ async function handleConsensusJudgment(deps, rawInput, signal) {
3226
3265
  duration_ms: engineResult.duration_ms,
3227
3266
  consensus_engine_version: engineResult.engine_version,
3228
3267
  per_model_verdicts: perModelForEvent,
3229
- synthesized_verdict: synthForEvent
3268
+ synthesized_verdict: synthForEvent,
3269
+ ...aggregateEventTokenUsage(engineResult.per_model_verdicts, engineResult.token_usage)
3230
3270
  };
3231
3271
  const payload = {
3232
3272
  verdict: engineResult.synthesized_verdict.verdict,
@@ -3235,6 +3275,7 @@ async function handleConsensusJudgment(deps, rawInput, signal) {
3235
3275
  synthesized_verdict: synthForEvent,
3236
3276
  engine_version: engineResult.engine_version,
3237
3277
  degraded: engineResult.degraded,
3278
+ ...engineResult.quorum_failed === true ? { quorum_failed: true } : {},
3238
3279
  gate_type: gateType,
3239
3280
  // ─── Consensus-engine feature outputs (additive; 2026-06-13) ─────────────
3240
3281
  // Feature 2 (calibrated-confidence) — ON by default; the engine attaches
@@ -3435,7 +3476,8 @@ async function handleArchitectureReview(deps, rawInput, signal) {
3435
3476
  synthesized_verdict: synthForEvent,
3436
3477
  consensus_confidence: engineResult.synthesized_verdict.confidence,
3437
3478
  duration_ms: engineResult.duration_ms,
3438
- consensus_engine_version: engineResult.engine_version
3479
+ consensus_engine_version: engineResult.engine_version,
3480
+ ...aggregateEventTokenUsage(engineResult.per_model_verdicts, engineResult.token_usage)
3439
3481
  };
3440
3482
  const escalationRequired = engineResult.escalation_required === true || engineResult.escalation_required === void 0 && engineResult.synthesized_verdict.dissent_summary !== null;
3441
3483
  const escalationReason = engineResult.escalation_reason ?? engineResult.synthesized_verdict.dissent_summary ?? "";
@@ -4922,7 +4964,8 @@ Produce the JSON dispatch plan now.`;
4922
4964
  duration_ms: engineResult.duration_ms,
4923
4965
  consensus_engine_version: engineResult.engine_version,
4924
4966
  per_model_verdicts: toEventPerModelVerdicts(engineResult.per_model_verdicts),
4925
- synthesized_verdict: toEventSynthesizedVerdict(engineResult.synthesized_verdict)
4967
+ synthesized_verdict: toEventSynthesizedVerdict(engineResult.synthesized_verdict),
4968
+ ...aggregateEventTokenUsage(engineResult.per_model_verdicts, engineResult.token_usage)
4926
4969
  };
4927
4970
  deps.events.append(enrichedEvent);
4928
4971
  return jsonContent(envelope);
@@ -5796,7 +5839,8 @@ async function handleReviewMerge(deps, rawInput, signal) {
5796
5839
  duration_ms: result.duration_ms,
5797
5840
  consensus_engine_version: result.engine_version,
5798
5841
  per_model_verdicts: perModel,
5799
- synthesized_verdict: synth
5842
+ synthesized_verdict: synth,
5843
+ ...aggregateEventTokenUsage(result.per_model_verdicts, result.token_usage)
5800
5844
  });
5801
5845
  }
5802
5846
 
@@ -7801,6 +7845,14 @@ function shadowEnabled(env) {
7801
7845
  const norm = raw.trim().toLowerCase();
7802
7846
  return !(norm === "0" || norm === "false" || norm === "no" || norm === "off" || norm === "");
7803
7847
  }
7848
+ var MIN_RESPONDERS_ENV_VAR = "VO_CONSENSUS_MIN_RESPONDERS";
7849
+ function resolveMinResponders(env) {
7850
+ const raw = (env ?? {})[MIN_RESPONDERS_ENV_VAR];
7851
+ if (raw === void 0 || raw.trim() === "") return 2;
7852
+ const parsed = Number.parseInt(raw.trim(), 10);
7853
+ if (!Number.isFinite(parsed) || parsed <= 0) return void 0;
7854
+ return parsed;
7855
+ }
7804
7856
  function mapShadowSynthesis(s) {
7805
7857
  if (s === void 0) return void 0;
7806
7858
  return {
@@ -7950,9 +8002,11 @@ function createEngineConsensusClient(options) {
7950
8002
  ...options.agreement_gate_enabled !== void 0 ? { configEnabled: options.agreement_gate_enabled } : {},
7951
8003
  ...options.env !== void 0 ? { env: options.env } : {}
7952
8004
  });
8005
+ const minResponders = resolveMinResponders(options.env);
7953
8006
  const engineOptions = {
7954
8007
  panel,
7955
8008
  ...options.per_model_timeout_ms !== void 0 ? { per_model_timeout_ms: options.per_model_timeout_ms } : {},
8009
+ ...minResponders !== void 0 ? { min_responders: minResponders } : {},
7956
8010
  ...agreementGate !== void 0 ? { agreement_gate: agreementGate } : {},
7957
8011
  // Stage A7-shadow: run the adaptive verdict alongside the live one for grading.
7958
8012
  // Cheap (pure log-odds over already-fetched verdicts; no extra model calls),
@@ -8005,8 +8059,13 @@ function createEngineConsensusClient(options) {
8005
8059
  synthesized_verdict: response.synthesized_verdict,
8006
8060
  per_model_verdicts: response.per_model_verdicts,
8007
8061
  degraded: response.degraded,
8062
+ ...response.quorum_failed === true ? { quorum_failed: true } : {},
8008
8063
  duration_ms: response.duration_ms,
8009
8064
  engine_version: response.engine_version,
8065
+ // Cumulative cross-round inference usage (B44-3). Absent when no panel
8066
+ // member reported usage; forwarded verbatim — the aggregator prefers it
8067
+ // over summing final-round verdicts (which under-reports deliberation).
8068
+ ...response.token_usage !== void 0 ? { token_usage: response.token_usage } : {},
8010
8069
  // Phase 2 Lane D-1 — forward escalation signal when present. The
8011
8070
  // source-grounded layer's own escalation (from the citation grade)
8012
8071
  // takes precedence when set, else the synthesizer's.