@algosuite/vo-mcp 0.2.0-beta.4 → 0.2.0-beta.8
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/bin/vo-mcp +8 -3
- package/dist/cli.js +347 -26
- package/dist/cli.js.map +4 -4
- package/dist/index.js +259 -21
- package/dist/index.js.map +4 -4
- package/dist/install-cli.js +88 -263
- package/dist/install-cli.js.map +4 -4
- package/dist/runner-cli.js +6300 -985
- package/dist/runner-cli.js.map +4 -4
- package/dist/runner-supervisor.js +777 -0
- package/dist/runner-supervisor.js.map +7 -0
- package/dist/set-key-cli.js +13 -3
- package/dist/set-key-cli.js.map +2 -2
- package/dist/update-cli.js +62 -0
- package/dist/update-cli.js.map +7 -0
- package/package.json +3 -2
package/bin/vo-mcp
CHANGED
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
* Usage:
|
|
6
6
|
* vo-mcp # MCP stdio server (default)
|
|
7
7
|
* vo-mcp install # one-command installer
|
|
8
|
+
* vo-mcp update # update MCP + runner package
|
|
8
9
|
* vo-mcp login # credential login (browser loopback)
|
|
9
10
|
* vo-mcp pair # device-code pairing (enter a code in the web)
|
|
10
|
-
* vo-mcp runner # agent runner
|
|
11
|
+
* vo-mcp runner # supervised agent runner
|
|
11
12
|
* vo-mcp runner --install-autostart # register runner to start at login
|
|
12
13
|
* vo-mcp runner --uninstall-autostart # remove auto-start registration
|
|
13
14
|
*/
|
|
@@ -17,6 +18,8 @@ const subcommand = process.argv[3];
|
|
|
17
18
|
|
|
18
19
|
if (command === 'install') {
|
|
19
20
|
import('../dist/install-cli.js');
|
|
21
|
+
} else if (command === 'update') {
|
|
22
|
+
import('../dist/update-cli.js');
|
|
20
23
|
} else if (command === 'login') {
|
|
21
24
|
import('../dist/login-cli.js');
|
|
22
25
|
} else if (command === 'pair') {
|
|
@@ -28,9 +31,11 @@ if (command === 'install') {
|
|
|
28
31
|
import('../dist/autostart-cli.js').then((m) => m.installAutostartCli());
|
|
29
32
|
} else if (subcommand === '--uninstall-autostart') {
|
|
30
33
|
import('../dist/autostart-cli.js').then((m) => m.uninstallAutostartCli());
|
|
31
|
-
} else {
|
|
32
|
-
//
|
|
34
|
+
} else if (process.argv.includes('--once') || process.argv.includes('--status') || process.argv.includes('--version') || process.argv.includes('-v')) {
|
|
35
|
+
// Direct diagnostic/one-shot mode; remote maintenance uses the supervisor.
|
|
33
36
|
import('../dist/runner-cli.js');
|
|
37
|
+
} else {
|
|
38
|
+
import('../dist/runner-supervisor.js');
|
|
34
39
|
}
|
|
35
40
|
} else {
|
|
36
41
|
// Default: MCP stdio server
|
package/dist/cli.js
CHANGED
|
@@ -1368,6 +1368,31 @@ var init_credential_store = __esm({
|
|
|
1368
1368
|
}
|
|
1369
1369
|
});
|
|
1370
1370
|
|
|
1371
|
+
// src/tools/memory/safe-memory-file.ts
|
|
1372
|
+
import { resolve, sep } from "node:path";
|
|
1373
|
+
function isSafeMemoryFileName(fileName) {
|
|
1374
|
+
return fileName.length <= 200 && fileName.trim() === fileName && !fileName.includes("/") && !fileName.includes("\\") && !fileName.includes(":") && SAFE_MEMORY_FILE_RE.test(fileName);
|
|
1375
|
+
}
|
|
1376
|
+
function resolveMemoryFilePath(memoryDir, fileName) {
|
|
1377
|
+
if (!isSafeMemoryFileName(fileName)) {
|
|
1378
|
+
throw new Error(`unsafe memory file_name: ${fileName.slice(0, 80)}`);
|
|
1379
|
+
}
|
|
1380
|
+
const root = resolve(memoryDir);
|
|
1381
|
+
const filePath = resolve(root, fileName);
|
|
1382
|
+
const rootPrefix = root.endsWith(sep) ? root : `${root}${sep}`;
|
|
1383
|
+
if (filePath !== root && !filePath.startsWith(rootPrefix)) {
|
|
1384
|
+
throw new Error(`memory file path escapes memory directory: ${fileName.slice(0, 80)}`);
|
|
1385
|
+
}
|
|
1386
|
+
return filePath;
|
|
1387
|
+
}
|
|
1388
|
+
var SAFE_MEMORY_FILE_RE;
|
|
1389
|
+
var init_safe_memory_file = __esm({
|
|
1390
|
+
"src/tools/memory/safe-memory-file.ts"() {
|
|
1391
|
+
"use strict";
|
|
1392
|
+
SAFE_MEMORY_FILE_RE = /^[A-Za-z0-9][A-Za-z0-9._ -]*\.md$/i;
|
|
1393
|
+
}
|
|
1394
|
+
});
|
|
1395
|
+
|
|
1371
1396
|
// src/tools/memory/sync-config.ts
|
|
1372
1397
|
var sync_config_exports = {};
|
|
1373
1398
|
__export(sync_config_exports, {
|
|
@@ -1413,10 +1438,13 @@ async function pullMemory(controlPlaneUrl, token, memoryDir, fetchFn) {
|
|
|
1413
1438
|
if (!data.ok || !Array.isArray(data.entries)) {
|
|
1414
1439
|
throw new Error("GET /api/v1/agent-config/memory/me response missing ok=true or entries array");
|
|
1415
1440
|
}
|
|
1441
|
+
const writes = data.entries.map((entry) => ({
|
|
1442
|
+
entry,
|
|
1443
|
+
filePath: resolveMemoryFilePath(memoryDir, entry.file_name)
|
|
1444
|
+
}));
|
|
1416
1445
|
mkdirSync4(memoryDir, { recursive: true });
|
|
1417
1446
|
const files = [];
|
|
1418
|
-
for (const entry of
|
|
1419
|
-
const filePath = join7(memoryDir, entry.file_name);
|
|
1447
|
+
for (const { entry, filePath } of writes) {
|
|
1420
1448
|
writeFileSync3(filePath, entry.content, "utf8");
|
|
1421
1449
|
files.push(entry.file_name);
|
|
1422
1450
|
}
|
|
@@ -1428,7 +1456,7 @@ async function pushMemory(controlPlaneUrl, token, memoryDir, sessionId, fetchFn)
|
|
|
1428
1456
|
}
|
|
1429
1457
|
const localFiles = readdirSync4(memoryDir).filter((f) => f.endsWith(".md")).map((f) => ({
|
|
1430
1458
|
file_name: f,
|
|
1431
|
-
content: readFileSync7(
|
|
1459
|
+
content: readFileSync7(resolveMemoryFilePath(memoryDir, f), "utf8"),
|
|
1432
1460
|
entry_type: f === "MEMORY.md" ? "index" : "topic"
|
|
1433
1461
|
}));
|
|
1434
1462
|
if (localFiles.length === 0) {
|
|
@@ -1566,6 +1594,7 @@ var init_sync_config = __esm({
|
|
|
1566
1594
|
"src/tools/memory/sync-config.ts"() {
|
|
1567
1595
|
"use strict";
|
|
1568
1596
|
init_common();
|
|
1597
|
+
init_safe_memory_file();
|
|
1569
1598
|
TOOL_NAME22 = "vo_sync_config";
|
|
1570
1599
|
inputSchema22 = {
|
|
1571
1600
|
type: "object",
|
|
@@ -5023,6 +5052,8 @@ function suggestedHandoffPath(session_id, isoTimestamp) {
|
|
|
5023
5052
|
}
|
|
5024
5053
|
|
|
5025
5054
|
// src/tools/session/report-session-state.ts
|
|
5055
|
+
init_auth_token_source();
|
|
5056
|
+
init_credential_store();
|
|
5026
5057
|
var TOOL_NAME19 = "vo_report_session_state";
|
|
5027
5058
|
var VALID_AGENT_TYPES = ["claude-code", "codex", "cursor", "continue"];
|
|
5028
5059
|
var MAX_GOAL_CHARS = 500;
|
|
@@ -5073,7 +5104,7 @@ var inputSchema19 = {
|
|
|
5073
5104
|
required: ["operator_id", "session_id", "agent_type", "context_used_pct"],
|
|
5074
5105
|
additionalProperties: false
|
|
5075
5106
|
};
|
|
5076
|
-
var description19 = "Reports per-session context-window utilization to VO and returns a directive: 'continue' (under 70%), 'prepare_handoff' (70-84%), or 'execute_handoff_now' (\u226585%). Implements V1 launch gate #9 (fleet context lifecycle management) per the official VO roadmap. Cloud-control-plane mode when VO_CONTROL_PLANE_URL
|
|
5107
|
+
var description19 = "Reports per-session context-window utilization to VO and returns a directive: 'continue' (under 70%), 'prepare_handoff' (70-84%), or 'execute_handoff_now' (\u226585%). Implements V1 launch gate #9 (fleet context lifecycle management) per the official VO roadmap. Cloud-control-plane mode when VO_CONTROL_PLANE_URL plus a user/scoped VO credential (or legacy admin token) is available; auto-allocates the session on first report so interactive agents (Claude Code, Cursor, Codex, Continue) appear on the live fleet whiteboard. Stub-local fallback when cloud config is absent or fails. The response shape stays stable across modes (`backend_mode` field in the payload tells the caller which mode produced the verdict).";
|
|
5077
5108
|
function isStringArray2(v, maxItems) {
|
|
5078
5109
|
if (!Array.isArray(v)) return false;
|
|
5079
5110
|
if (v.length > maxItems) return false;
|
|
@@ -5098,14 +5129,39 @@ function isToolInput19(v) {
|
|
|
5098
5129
|
}
|
|
5099
5130
|
return true;
|
|
5100
5131
|
}
|
|
5101
|
-
function
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5132
|
+
async function fetchCloudIdentity(url, token, fetchFn) {
|
|
5133
|
+
try {
|
|
5134
|
+
const response = await fetchFn(`${url}/api/v1/auth/me`, {
|
|
5135
|
+
method: "GET",
|
|
5136
|
+
headers: {
|
|
5137
|
+
"Authorization": `Bearer ${token}`
|
|
5138
|
+
}
|
|
5139
|
+
});
|
|
5140
|
+
if (!response.ok) return null;
|
|
5141
|
+
const data = await response.json();
|
|
5142
|
+
if (!data.ok || !data.provisioned || !data.operator_id || !data.tenant_id) return null;
|
|
5143
|
+
return { operator_id: data.operator_id, tenant_id: data.tenant_id };
|
|
5144
|
+
} catch {
|
|
5145
|
+
return null;
|
|
5146
|
+
}
|
|
5147
|
+
}
|
|
5148
|
+
async function getCloudConfig(fetchFn = fetch) {
|
|
5149
|
+
const url = process.env["VO_CONTROL_PLANE_URL"]?.trim();
|
|
5150
|
+
if (!url) return null;
|
|
5151
|
+
const tokenSource = createAuthTokenSourceFromEnv(
|
|
5152
|
+
process.env,
|
|
5153
|
+
fetchFn,
|
|
5154
|
+
() => readStoredCredential(process.env)
|
|
5155
|
+
);
|
|
5156
|
+
const token = await tokenSource?.getToken();
|
|
5157
|
+
if (!token) return null;
|
|
5158
|
+
const tenant_id = process.env["VO_TENANT_ID"]?.trim();
|
|
5159
|
+
if (tenant_id) return { url, token, tenant_id };
|
|
5160
|
+
const identity = await fetchCloudIdentity(url, token, fetchFn);
|
|
5161
|
+
if (!identity) return null;
|
|
5162
|
+
return { url, token, tenant_id: identity.tenant_id, operator_id: identity.operator_id };
|
|
5107
5163
|
}
|
|
5108
|
-
async function tryCloudReportState(cloud, input) {
|
|
5164
|
+
async function tryCloudReportState(cloud, input, fetchFn = fetch) {
|
|
5109
5165
|
try {
|
|
5110
5166
|
const reportBody = {
|
|
5111
5167
|
context_used_pct: input.context_used_pct
|
|
@@ -5118,7 +5174,7 @@ async function tryCloudReportState(cloud, input) {
|
|
|
5118
5174
|
reportBody["recent_tool_uses"] = input.recent_tool_uses;
|
|
5119
5175
|
}
|
|
5120
5176
|
const reportUrl = `${cloud.url}/api/v1/session/${input.session_id}/report-state`;
|
|
5121
|
-
let response = await
|
|
5177
|
+
let response = await fetchFn(reportUrl, {
|
|
5122
5178
|
method: "POST",
|
|
5123
5179
|
headers: {
|
|
5124
5180
|
"Content-Type": "application/json",
|
|
@@ -5128,7 +5184,7 @@ async function tryCloudReportState(cloud, input) {
|
|
|
5128
5184
|
});
|
|
5129
5185
|
if (response.status === 404) {
|
|
5130
5186
|
const allocateBody = {
|
|
5131
|
-
operator_id: input.operator_id,
|
|
5187
|
+
operator_id: cloud.operator_id ?? input.operator_id,
|
|
5132
5188
|
tenant_id: cloud.tenant_id,
|
|
5133
5189
|
agent_type: input.agent_type,
|
|
5134
5190
|
current_goal: input.current_goal ?? "Interactive session"
|
|
@@ -5137,7 +5193,7 @@ async function tryCloudReportState(cloud, input) {
|
|
|
5137
5193
|
allocateBody["initial_context_used_pct"] = input.context_used_pct;
|
|
5138
5194
|
}
|
|
5139
5195
|
const allocateUrl = `${cloud.url}/api/v1/session`;
|
|
5140
|
-
const allocateResponse = await
|
|
5196
|
+
const allocateResponse = await fetchFn(allocateUrl, {
|
|
5141
5197
|
method: "POST",
|
|
5142
5198
|
headers: {
|
|
5143
5199
|
"Content-Type": "application/json",
|
|
@@ -5148,7 +5204,10 @@ async function tryCloudReportState(cloud, input) {
|
|
|
5148
5204
|
if (!allocateResponse.ok) {
|
|
5149
5205
|
return null;
|
|
5150
5206
|
}
|
|
5151
|
-
|
|
5207
|
+
const allocateData = await allocateResponse.json();
|
|
5208
|
+
const retrySessionId = typeof allocateData.session?.session_id === "string" && allocateData.session.session_id.length > 0 ? allocateData.session.session_id : input.session_id;
|
|
5209
|
+
const retryReportUrl = `${cloud.url}/api/v1/session/${retrySessionId}/report-state`;
|
|
5210
|
+
response = await fetchFn(retryReportUrl, {
|
|
5152
5211
|
method: "POST",
|
|
5153
5212
|
headers: {
|
|
5154
5213
|
"Content-Type": "application/json",
|
|
@@ -5187,7 +5246,7 @@ async function handleReportSessionState(deps, rawInput, _signal) {
|
|
|
5187
5246
|
`invalid input. Required fields: operator_id (non-empty string), session_id (non-empty string), agent_type (one of: ${VALID_AGENT_TYPES.join(" | ")}), context_used_pct (number 0-100). Optional: current_goal (string \u2264${MAX_GOAL_CHARS} chars), recent_files_touched (string[] \u2264${MAX_RECENT_FILES}), recent_tool_uses (string[] \u2264${MAX_RECENT_TOOLS}).`
|
|
5188
5247
|
);
|
|
5189
5248
|
}
|
|
5190
|
-
const cloud = getCloudConfig();
|
|
5249
|
+
const cloud = await getCloudConfig();
|
|
5191
5250
|
if (cloud !== null) {
|
|
5192
5251
|
const cloudPayload = await tryCloudReportState(cloud, rawInput);
|
|
5193
5252
|
if (cloudPayload !== null) {
|
|
@@ -5433,6 +5492,107 @@ async function handleConciergeDispatch(deps, rawInput, _signal) {
|
|
|
5433
5492
|
|
|
5434
5493
|
// src/server.ts
|
|
5435
5494
|
init_sync_config();
|
|
5495
|
+
|
|
5496
|
+
// src/tools/memory/private-knowledge.ts
|
|
5497
|
+
init_common();
|
|
5498
|
+
var UPSERT_TOOL_NAME = "vo_private_knowledge_upsert";
|
|
5499
|
+
var CONTEXT_TOOL_NAME = "vo_private_knowledge_context";
|
|
5500
|
+
var KNOWLEDGE_CLASSES = ["memory", "skill", "doctrine", "hook", "command"];
|
|
5501
|
+
var PRECISION_CHAR_BUDGET = 12e3;
|
|
5502
|
+
var upsertInputSchema = {
|
|
5503
|
+
type: "object",
|
|
5504
|
+
properties: {
|
|
5505
|
+
knowledge_class: { type: "string", enum: KNOWLEDGE_CLASSES },
|
|
5506
|
+
source_path: { type: "string", description: "Stable private source identifier; not exposed to other users." },
|
|
5507
|
+
title: { type: "string", description: 'Descriptive, retrieval-friendly title (e.g. "AlgoTax OCR redaction architecture", not "notes") \u2014 retrieval matches on it.' },
|
|
5508
|
+
content: { type: "string", description: "Private knowledge text to store server-side. Keep each entry tight and focused (~1-3 pages, under ~12k chars); split larger corpora into separate entries." }
|
|
5509
|
+
},
|
|
5510
|
+
required: ["knowledge_class", "source_path", "title", "content"],
|
|
5511
|
+
additionalProperties: false
|
|
5512
|
+
};
|
|
5513
|
+
var contextInputSchema = {
|
|
5514
|
+
type: "object",
|
|
5515
|
+
properties: {
|
|
5516
|
+
query: { type: "string" },
|
|
5517
|
+
limit: { type: "number", minimum: 1, maximum: 50 },
|
|
5518
|
+
knowledge_class: { type: "string", enum: KNOWLEDGE_CLASSES }
|
|
5519
|
+
},
|
|
5520
|
+
required: ["query"],
|
|
5521
|
+
additionalProperties: false
|
|
5522
|
+
};
|
|
5523
|
+
var upsertDescription = "Uploads or refreshes the authenticated operator\u2019s private cloud knowledge. Works for Claude, Codex, Cursor, and cowork clients via the same vo-mcp login credential. Returns metadata only, not raw stored content. PRECISION DISCIPLINE: keep each entry tight and focused (~1-3 pages) with a descriptive retrieval-friendly title \u2014 retrieval surfaces whole entries, so small dense entries beat bulk dumps. Split large corpora into focused entries, then run a retrieval self-test via vo_private_knowledge_context before relying on the knowledge.";
|
|
5524
|
+
var contextDescription = "Retrieves prompt-ready private knowledge context for the authenticated operator. Returns snippets/context only; no raw corpus download. Also the retrieval self-test surface: after upserting critical knowledge, query for it here and confirm the entry surfaces before trusting it in downstream work.";
|
|
5525
|
+
function isKnowledgeClass(value) {
|
|
5526
|
+
return typeof value === "string" && KNOWLEDGE_CLASSES.includes(value);
|
|
5527
|
+
}
|
|
5528
|
+
function isUpsertInput(value) {
|
|
5529
|
+
if (typeof value !== "object" || value === null) return false;
|
|
5530
|
+
const input = value;
|
|
5531
|
+
return isKnowledgeClass(input["knowledge_class"]) && typeof input["source_path"] === "string" && typeof input["title"] === "string" && typeof input["content"] === "string";
|
|
5532
|
+
}
|
|
5533
|
+
function isContextInput(value) {
|
|
5534
|
+
if (typeof value !== "object" || value === null) return false;
|
|
5535
|
+
const input = value;
|
|
5536
|
+
if (typeof input["query"] !== "string") return false;
|
|
5537
|
+
if (input["limit"] !== void 0 && typeof input["limit"] !== "number") return false;
|
|
5538
|
+
if (input["knowledge_class"] !== void 0 && !isKnowledgeClass(input["knowledge_class"])) return false;
|
|
5539
|
+
return true;
|
|
5540
|
+
}
|
|
5541
|
+
async function getCloudAuth(fetchFn) {
|
|
5542
|
+
const controlPlaneUrl = process.env["VO_CONTROL_PLANE_URL"]?.replace(/\/+$/, "");
|
|
5543
|
+
if (!controlPlaneUrl) {
|
|
5544
|
+
return { ok: false, reason: "VO_CONTROL_PLANE_URL not set \u2014 cloud mode disabled" };
|
|
5545
|
+
}
|
|
5546
|
+
const { createAuthTokenSourceFromEnv: createAuthTokenSourceFromEnv2 } = await Promise.resolve().then(() => (init_auth_token_source(), auth_token_source_exports));
|
|
5547
|
+
const { readStoredCredential: readStoredCredential2 } = await Promise.resolve().then(() => (init_credential_store(), credential_store_exports));
|
|
5548
|
+
const tokenSource = createAuthTokenSourceFromEnv2(process.env, fetchFn, () => readStoredCredential2(process.env));
|
|
5549
|
+
if (!tokenSource) return { ok: false, reason: "No auth configured. Run `vo-mcp login`." };
|
|
5550
|
+
const token = await tokenSource.getToken();
|
|
5551
|
+
if (!token) return { ok: false, reason: "Failed to obtain auth token. Run `vo-mcp login` again." };
|
|
5552
|
+
return { ok: true, controlPlaneUrl, token };
|
|
5553
|
+
}
|
|
5554
|
+
async function callPrivateKnowledge(path3, body, fetchFn) {
|
|
5555
|
+
const auth = await getCloudAuth(fetchFn);
|
|
5556
|
+
if (!auth.ok) return { ok: false, reason: auth.reason };
|
|
5557
|
+
const response = await fetchFn(`${auth.controlPlaneUrl}${path3}`, {
|
|
5558
|
+
method: "POST",
|
|
5559
|
+
headers: {
|
|
5560
|
+
authorization: `Bearer ${auth.token}`,
|
|
5561
|
+
"content-type": "application/json"
|
|
5562
|
+
},
|
|
5563
|
+
body: JSON.stringify(body)
|
|
5564
|
+
});
|
|
5565
|
+
const text = await response.text();
|
|
5566
|
+
const parsed = text ? JSON.parse(text) : null;
|
|
5567
|
+
if (response.status < 200 || response.status >= 300) {
|
|
5568
|
+
return { ok: false, status: response.status, response: parsed ?? text };
|
|
5569
|
+
}
|
|
5570
|
+
return parsed;
|
|
5571
|
+
}
|
|
5572
|
+
async function handlePrivateKnowledgeUpsert(_deps, rawInput, _signal, fetchFn = globalThis.fetch) {
|
|
5573
|
+
if (!isUpsertInput(rawInput)) {
|
|
5574
|
+
throw invalidParams(UPSERT_TOOL_NAME, "expected { knowledge_class, source_path, title, content }.");
|
|
5575
|
+
}
|
|
5576
|
+
const payload = await callPrivateKnowledge("/api/v1/knowledge/private", rawInput, fetchFn);
|
|
5577
|
+
const envelope = {
|
|
5578
|
+
tool: UPSERT_TOOL_NAME,
|
|
5579
|
+
schema_version: 1,
|
|
5580
|
+
payload
|
|
5581
|
+
};
|
|
5582
|
+
if (rawInput.content.length > PRECISION_CHAR_BUDGET) {
|
|
5583
|
+
envelope.precision_note = `content is ${rawInput.content.length} chars (> ${PRECISION_CHAR_BUDGET}). Tight 1-3 page entries retrieve better \u2014 consider splitting into focused entries, then re-test retrieval via ${CONTEXT_TOOL_NAME}.`;
|
|
5584
|
+
}
|
|
5585
|
+
return jsonContent(envelope);
|
|
5586
|
+
}
|
|
5587
|
+
async function handlePrivateKnowledgeContext(_deps, rawInput, _signal, fetchFn = globalThis.fetch) {
|
|
5588
|
+
if (!isContextInput(rawInput)) {
|
|
5589
|
+
throw invalidParams(CONTEXT_TOOL_NAME, "expected { query, optional limit, optional knowledge_class }.");
|
|
5590
|
+
}
|
|
5591
|
+
const payload = await callPrivateKnowledge("/api/v1/knowledge/private/context", rawInput, fetchFn);
|
|
5592
|
+
return jsonContent({ tool: CONTEXT_TOOL_NAME, schema_version: 1, payload });
|
|
5593
|
+
}
|
|
5594
|
+
|
|
5595
|
+
// src/server.ts
|
|
5436
5596
|
function buildToolRegistry() {
|
|
5437
5597
|
return {
|
|
5438
5598
|
[TOOL_NAME]: {
|
|
@@ -5610,6 +5770,22 @@ function buildToolRegistry() {
|
|
|
5610
5770
|
inputSchema: inputSchema22
|
|
5611
5771
|
},
|
|
5612
5772
|
handler: handleSyncConfig
|
|
5773
|
+
},
|
|
5774
|
+
[UPSERT_TOOL_NAME]: {
|
|
5775
|
+
definition: {
|
|
5776
|
+
name: UPSERT_TOOL_NAME,
|
|
5777
|
+
description: upsertDescription,
|
|
5778
|
+
inputSchema: upsertInputSchema
|
|
5779
|
+
},
|
|
5780
|
+
handler: handlePrivateKnowledgeUpsert
|
|
5781
|
+
},
|
|
5782
|
+
[CONTEXT_TOOL_NAME]: {
|
|
5783
|
+
definition: {
|
|
5784
|
+
name: CONTEXT_TOOL_NAME,
|
|
5785
|
+
description: contextDescription,
|
|
5786
|
+
inputSchema: contextInputSchema
|
|
5787
|
+
},
|
|
5788
|
+
handler: handlePrivateKnowledgeContext
|
|
5613
5789
|
}
|
|
5614
5790
|
};
|
|
5615
5791
|
}
|
|
@@ -5901,6 +6077,64 @@ function createNullConsensusEngineClient(reason = NULL_CLIENT_DEFAULT_REASON) {
|
|
|
5901
6077
|
};
|
|
5902
6078
|
}
|
|
5903
6079
|
|
|
6080
|
+
// src/consensus/meta-model-caller.ts
|
|
6081
|
+
var META_MODEL_API_BASE_URL = "https://api.meta.ai/v1";
|
|
6082
|
+
var META_CONSENSUS_MODEL = "muse-spark-1.1";
|
|
6083
|
+
var META_MODEL_API_KEY_ENV = "MODEL_API_KEY";
|
|
6084
|
+
var META_MODEL_API_KEY_ALIAS = "META_API";
|
|
6085
|
+
function resolveMetaKey(env) {
|
|
6086
|
+
return String(env[META_MODEL_API_KEY_ENV] || env[META_MODEL_API_KEY_ALIAS] || "").trim();
|
|
6087
|
+
}
|
|
6088
|
+
function positiveMaxTokens(value) {
|
|
6089
|
+
const parsed = Math.floor(Number(value));
|
|
6090
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 2048;
|
|
6091
|
+
}
|
|
6092
|
+
function createMetaModelCaller(options = {}) {
|
|
6093
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
6094
|
+
const envSource = options.envSource ?? process.env;
|
|
6095
|
+
const reasoningEffort = options.reasoningEffort ?? "high";
|
|
6096
|
+
return async function callMetaWithMetrics2(prompt, systemPrompt, model, maxTokens, _privacyOptions, signal) {
|
|
6097
|
+
const key = resolveMetaKey(envSource);
|
|
6098
|
+
if (!key) throw new Error(`Missing ${META_MODEL_API_KEY_ENV} for Meta Model API`);
|
|
6099
|
+
const messages = [
|
|
6100
|
+
...systemPrompt ? [{ role: "system", content: systemPrompt }] : [],
|
|
6101
|
+
{ role: "user", content: prompt }
|
|
6102
|
+
];
|
|
6103
|
+
const response = await fetchImpl(`${META_MODEL_API_BASE_URL}/chat/completions`, {
|
|
6104
|
+
method: "POST",
|
|
6105
|
+
headers: {
|
|
6106
|
+
Authorization: `Bearer ${key}`,
|
|
6107
|
+
"Content-Type": "application/json"
|
|
6108
|
+
},
|
|
6109
|
+
body: JSON.stringify({
|
|
6110
|
+
model: model || META_CONSENSUS_MODEL,
|
|
6111
|
+
messages,
|
|
6112
|
+
max_tokens: positiveMaxTokens(maxTokens),
|
|
6113
|
+
reasoning_effort: reasoningEffort
|
|
6114
|
+
}),
|
|
6115
|
+
signal
|
|
6116
|
+
});
|
|
6117
|
+
const payload = await response.json();
|
|
6118
|
+
if (!response.ok) {
|
|
6119
|
+
const message = String(payload.error?.message || response.statusText || "request failed").slice(0, 500);
|
|
6120
|
+
throw Object.assign(new Error(`Meta Model API ${response.status}: ${message}`), { status: response.status });
|
|
6121
|
+
}
|
|
6122
|
+
const content = payload.choices?.[0]?.message?.content;
|
|
6123
|
+
if (typeof content !== "string" || !content.trim()) {
|
|
6124
|
+
throw new Error(`Meta Model API returned no text (finish=${payload.choices?.[0]?.finish_reason || "unknown"})`);
|
|
6125
|
+
}
|
|
6126
|
+
const inputTokens = Number(payload.usage?.prompt_tokens || 0);
|
|
6127
|
+
const outputTokens = Number(payload.usage?.completion_tokens || 0);
|
|
6128
|
+
return {
|
|
6129
|
+
content,
|
|
6130
|
+
inputTokens,
|
|
6131
|
+
outputTokens,
|
|
6132
|
+
totalTokens: Number(payload.usage?.total_tokens || inputTokens + outputTokens)
|
|
6133
|
+
};
|
|
6134
|
+
};
|
|
6135
|
+
}
|
|
6136
|
+
var callMetaWithMetrics = createMetaModelCaller();
|
|
6137
|
+
|
|
5904
6138
|
// src/consensus/engine-options.ts
|
|
5905
6139
|
var AGREEMENT_GATE_ENV_VAR = "VO_CONSENSUS_AGREEMENT_GATE";
|
|
5906
6140
|
function isTruthyFlag(raw) {
|
|
@@ -6046,6 +6280,9 @@ function createClaimClassifier(model, options = {}) {
|
|
|
6046
6280
|
};
|
|
6047
6281
|
}
|
|
6048
6282
|
|
|
6283
|
+
// src/constants/consensus-models.ts
|
|
6284
|
+
var XAI_CONSENSUS_MODEL = "grok-4.5";
|
|
6285
|
+
|
|
6049
6286
|
// src/consensus/engine-client.ts
|
|
6050
6287
|
function raceAbort(signal) {
|
|
6051
6288
|
return new Promise((_, reject) => {
|
|
@@ -6195,7 +6432,9 @@ var DEFAULT_MODELS = {
|
|
|
6195
6432
|
// callGeminiWithMetrics; 2.5-pro REJECTS budget 0 ("only works in thinking mode").
|
|
6196
6433
|
// Flash is also ~10x cheaper. 2026-06-02.
|
|
6197
6434
|
google: "gemini-2.5-flash",
|
|
6198
|
-
deepseek: "deepseek-chat"
|
|
6435
|
+
deepseek: "deepseek-chat",
|
|
6436
|
+
xai: XAI_CONSENSUS_MODEL,
|
|
6437
|
+
meta: META_CONSENSUS_MODEL
|
|
6199
6438
|
};
|
|
6200
6439
|
function probeProviders(env = process.env) {
|
|
6201
6440
|
const out = [];
|
|
@@ -6203,6 +6442,8 @@ function probeProviders(env = process.env) {
|
|
|
6203
6442
|
if ((env["OPENAI_API_KEY"] ?? "").trim().length > 0) out.push("openai");
|
|
6204
6443
|
if ((env["GOOGLE_API_KEY"] ?? "").trim().length > 0) out.push("google");
|
|
6205
6444
|
if ((env["DEEPSEEK_API_KEY"] ?? "").trim().length > 0) out.push("deepseek");
|
|
6445
|
+
if ((env["XAI_API_KEY"] ?? "").trim().length > 0) out.push("xai");
|
|
6446
|
+
if ((env[META_MODEL_API_KEY_ENV] ?? "").trim().length > 0 || (env[META_MODEL_API_KEY_ALIAS] ?? "").trim().length > 0) out.push("meta");
|
|
6206
6447
|
return out;
|
|
6207
6448
|
}
|
|
6208
6449
|
async function loadFactoryAndCallers(injectedEngine, injectedShared) {
|
|
@@ -6225,7 +6466,7 @@ async function loadFactoryAndCallers(injectedEngine, injectedShared) {
|
|
|
6225
6466
|
return { ok: false, reason: ENGINE_UNAVAILABLE_REASONS.FUNCTIONS_SHARED_NOT_INSTALLED };
|
|
6226
6467
|
}
|
|
6227
6468
|
}
|
|
6228
|
-
if (sharedMod === null || typeof sharedMod !== "object" || typeof sharedMod.callAnthropicWithMetrics !== "function" || typeof sharedMod.callOpenAIWithMetrics !== "function" || typeof sharedMod.callGeminiWithMetrics !== "function") {
|
|
6469
|
+
if (sharedMod === null || typeof sharedMod !== "object" || typeof sharedMod.callAnthropicWithMetrics !== "function" || typeof sharedMod.callOpenAIWithMetrics !== "function" || typeof sharedMod.callGeminiWithMetrics !== "function" || typeof sharedMod.callXAIWithMetrics !== "function") {
|
|
6229
6470
|
return { ok: false, reason: ENGINE_UNAVAILABLE_REASONS.FUNCTIONS_SHARED_NOT_INSTALLED };
|
|
6230
6471
|
}
|
|
6231
6472
|
return {
|
|
@@ -6248,21 +6489,26 @@ async function tryCreateEngineConsensusClientFromEnvAsync(options = {}) {
|
|
|
6248
6489
|
anthropic: loaded.shared.callAnthropicWithMetrics,
|
|
6249
6490
|
openai: loaded.shared.callOpenAIWithMetrics,
|
|
6250
6491
|
google: loaded.shared.callGeminiWithMetrics,
|
|
6251
|
-
deepseek: loaded.shared.callDeepSeekWithMetrics
|
|
6492
|
+
deepseek: loaded.shared.callDeepSeekWithMetrics,
|
|
6493
|
+
xai: loaded.shared.callXAIWithMetrics,
|
|
6494
|
+
meta: options.metaCaller ?? callMetaWithMetrics
|
|
6252
6495
|
};
|
|
6253
6496
|
const modelByProvider = {
|
|
6254
6497
|
anthropic: options.models?.anthropic ?? DEFAULT_MODELS.anthropic,
|
|
6255
6498
|
openai: options.models?.openai ?? DEFAULT_MODELS.openai,
|
|
6256
6499
|
google: options.models?.google ?? DEFAULT_MODELS.google,
|
|
6257
|
-
deepseek: options.models?.deepseek ?? DEFAULT_MODELS.deepseek
|
|
6500
|
+
deepseek: options.models?.deepseek ?? DEFAULT_MODELS.deepseek,
|
|
6501
|
+
xai: options.models?.xai ?? DEFAULT_MODELS.xai,
|
|
6502
|
+
meta: options.models?.meta ?? DEFAULT_MODELS.meta
|
|
6258
6503
|
};
|
|
6504
|
+
const adapterEnv = !(env[META_MODEL_API_KEY_ENV] ?? "").trim() && (env[META_MODEL_API_KEY_ALIAS] ?? "").trim() ? { ...env, [META_MODEL_API_KEY_ENV]: env[META_MODEL_API_KEY_ALIAS] } : env;
|
|
6259
6505
|
const panel = [];
|
|
6260
6506
|
for (const p of providers) {
|
|
6261
6507
|
try {
|
|
6262
6508
|
const adapter = loaded.engine.createAdapter(p, {
|
|
6263
6509
|
model: modelByProvider[p],
|
|
6264
6510
|
caller: callerByProvider[p],
|
|
6265
|
-
envSource:
|
|
6511
|
+
envSource: adapterEnv
|
|
6266
6512
|
});
|
|
6267
6513
|
panel.push(adapter);
|
|
6268
6514
|
} catch {
|
|
@@ -6419,6 +6665,69 @@ function tryCreateMoatConsensusClientFromEnv(env = process.env, fetchFn) {
|
|
|
6419
6665
|
});
|
|
6420
6666
|
}
|
|
6421
6667
|
|
|
6668
|
+
// src/consensus/fallback-client.ts
|
|
6669
|
+
var MIN_VALID_LOCAL_VERDICTS = 2;
|
|
6670
|
+
var INSUFFICIENT_LOCAL_VERDICTS_REASON = "local-panel-insufficient-valid-verdicts";
|
|
6671
|
+
function createConsensusFallbackClient(primary, fallback, options = {}) {
|
|
6672
|
+
return {
|
|
6673
|
+
async run(request) {
|
|
6674
|
+
const primaryResult = await primary.run(request);
|
|
6675
|
+
if (request.signal?.aborted || !primaryResult.ok && primaryResult.reason === CANCELLED_REASON) {
|
|
6676
|
+
return primaryResult;
|
|
6677
|
+
}
|
|
6678
|
+
if (primaryResult.ok) {
|
|
6679
|
+
const validVerdicts = primaryResult.per_model_verdicts.filter(
|
|
6680
|
+
(verdict) => verdict.verdict !== "error"
|
|
6681
|
+
);
|
|
6682
|
+
if (validVerdicts.length >= MIN_VALID_LOCAL_VERDICTS) return primaryResult;
|
|
6683
|
+
options.onFallback?.(INSUFFICIENT_LOCAL_VERDICTS_REASON);
|
|
6684
|
+
return fallback.run(request);
|
|
6685
|
+
}
|
|
6686
|
+
options.onFallback?.(primaryResult.reason);
|
|
6687
|
+
return fallback.run(request);
|
|
6688
|
+
}
|
|
6689
|
+
};
|
|
6690
|
+
}
|
|
6691
|
+
|
|
6692
|
+
// src/consensus/local-credential-env.ts
|
|
6693
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
6694
|
+
var require2 = createRequire2(import.meta.url);
|
|
6695
|
+
var KEY_SERVICE = "algosuite-vo";
|
|
6696
|
+
var KEYCHAIN_TARGETS = [
|
|
6697
|
+
{ account: "anthropic-api-key", envVar: "ANTHROPIC_API_KEY" },
|
|
6698
|
+
{ account: "openai-api-key", envVar: "OPENAI_API_KEY" },
|
|
6699
|
+
{ account: "xai-api-key", envVar: "XAI_API_KEY" },
|
|
6700
|
+
{ account: "meta-api-key", envVar: "MODEL_API_KEY" }
|
|
6701
|
+
];
|
|
6702
|
+
function loadEntryCtor() {
|
|
6703
|
+
try {
|
|
6704
|
+
return require2("@napi-rs/keyring").Entry ?? null;
|
|
6705
|
+
} catch {
|
|
6706
|
+
return null;
|
|
6707
|
+
}
|
|
6708
|
+
}
|
|
6709
|
+
function readKey(EntryCtor, account) {
|
|
6710
|
+
try {
|
|
6711
|
+
return new EntryCtor(KEY_SERVICE, account).getPassword()?.trim() || null;
|
|
6712
|
+
} catch {
|
|
6713
|
+
return null;
|
|
6714
|
+
}
|
|
6715
|
+
}
|
|
6716
|
+
function withLocalConsensusCredentials(baseEnv = process.env, options = {}) {
|
|
6717
|
+
const env = { ...baseEnv };
|
|
6718
|
+
if (!env.OPENAI_API_KEY?.trim() && env.CODEX_API_KEY?.trim()) {
|
|
6719
|
+
env.OPENAI_API_KEY = env.CODEX_API_KEY;
|
|
6720
|
+
}
|
|
6721
|
+
const EntryCtor = options.EntryCtor === void 0 ? loadEntryCtor() : options.EntryCtor;
|
|
6722
|
+
if (!EntryCtor) return env;
|
|
6723
|
+
for (const target of KEYCHAIN_TARGETS) {
|
|
6724
|
+
if (env[target.envVar]?.trim()) continue;
|
|
6725
|
+
const key = readKey(EntryCtor, target.account);
|
|
6726
|
+
if (key) env[target.envVar] = key;
|
|
6727
|
+
}
|
|
6728
|
+
return env;
|
|
6729
|
+
}
|
|
6730
|
+
|
|
6422
6731
|
// src/cloud/login.ts
|
|
6423
6732
|
init_credential_store();
|
|
6424
6733
|
import { createServer as createServer2 } from "node:http";
|
|
@@ -6484,7 +6793,7 @@ async function runLogin(opts = {}) {
|
|
|
6484
6793
|
const nowIso = opts.nowIso ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
6485
6794
|
const openBrowser = opts.openBrowser ?? defaultOpenBrowser;
|
|
6486
6795
|
const state = randomBytes(32).toString("base64url");
|
|
6487
|
-
return new Promise((
|
|
6796
|
+
return new Promise((resolve2, reject) => {
|
|
6488
6797
|
let settled = false;
|
|
6489
6798
|
const finish = (err, result) => {
|
|
6490
6799
|
if (settled) return;
|
|
@@ -6492,7 +6801,7 @@ async function runLogin(opts = {}) {
|
|
|
6492
6801
|
clearTimeout(timer);
|
|
6493
6802
|
server.close();
|
|
6494
6803
|
if (err) reject(err);
|
|
6495
|
-
else
|
|
6804
|
+
else resolve2(result);
|
|
6496
6805
|
};
|
|
6497
6806
|
const server = createServer2((req, res) => {
|
|
6498
6807
|
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
@@ -6682,11 +6991,23 @@ async function main() {
|
|
|
6682
6991
|
const ratchets = createStubRatchetClient();
|
|
6683
6992
|
const testModule = process.env["VO_MCP_TEST_ENGINE_MODULE"];
|
|
6684
6993
|
const testClient = testModule !== void 0 && testModule.length > 0 ? await loadTestEngineClient(testModule) : null;
|
|
6994
|
+
const localEnv = withLocalConsensusCredentials();
|
|
6995
|
+
const localProviders = probeProviders(localEnv);
|
|
6996
|
+
for (const key of ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GOOGLE_API_KEY", "DEEPSEEK_API_KEY", "XAI_API_KEY", "MODEL_API_KEY"]) {
|
|
6997
|
+
if (!process.env[key] && localEnv[key]) process.env[key] = localEnv[key];
|
|
6998
|
+
}
|
|
6999
|
+
const localConsensus = tryCreateEngineConsensusClientFromEnv({ envSource: localEnv });
|
|
6685
7000
|
const cloudConsensus = testClient ? null : tryCreateMoatConsensusClientFromEnv();
|
|
6686
|
-
|
|
6687
|
-
|
|
7001
|
+
let consensus = testClient ?? localConsensus;
|
|
7002
|
+
if (!testClient && cloudConsensus && localProviders.length >= 2) {
|
|
7003
|
+
console.error(`[vo-mcp] local-first consensus active (${localProviders.join(", ")}); cloud moat is fallback-only`);
|
|
7004
|
+
consensus = createConsensusFallbackClient(localConsensus, cloudConsensus, {
|
|
7005
|
+
onFallback: (reason) => console.error(`[vo-mcp] local consensus unavailable (${reason}); using cloud moat fallback`)
|
|
7006
|
+
});
|
|
7007
|
+
} else if (!testClient && cloudConsensus) {
|
|
7008
|
+
console.error("[vo-mcp] fewer than 2 linked local providers; cloud moat consensus active");
|
|
7009
|
+
consensus = cloudConsensus;
|
|
6688
7010
|
}
|
|
6689
|
-
const consensus = testClient ?? cloudConsensus ?? tryCreateEngineConsensusClientFromEnv();
|
|
6690
7011
|
let adminCallables = null;
|
|
6691
7012
|
try {
|
|
6692
7013
|
adminCallables = buildAdminCallableClientFromEnv();
|