@algosuite/vo-mcp 0.2.0-beta.4 → 0.2.0-beta.7
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 +3 -0
- package/dist/cli.js +261 -22
- package/dist/cli.js.map +4 -4
- package/dist/index.js +251 -20
- 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 +5417 -1017
- package/dist/runner-cli.js.map +4 -4
- 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 +1 -1
package/bin/vo-mcp
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
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
11
|
* vo-mcp runner # agent runner daemon
|
|
@@ -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') {
|
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
|
+
}
|
|
5107
5147
|
}
|
|
5108
|
-
async function
|
|
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 };
|
|
5163
|
+
}
|
|
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) {
|
|
@@ -6195,7 +6429,8 @@ var DEFAULT_MODELS = {
|
|
|
6195
6429
|
// callGeminiWithMetrics; 2.5-pro REJECTS budget 0 ("only works in thinking mode").
|
|
6196
6430
|
// Flash is also ~10x cheaper. 2026-06-02.
|
|
6197
6431
|
google: "gemini-2.5-flash",
|
|
6198
|
-
deepseek: "deepseek-chat"
|
|
6432
|
+
deepseek: "deepseek-chat",
|
|
6433
|
+
meta: META_CONSENSUS_MODEL
|
|
6199
6434
|
};
|
|
6200
6435
|
function probeProviders(env = process.env) {
|
|
6201
6436
|
const out = [];
|
|
@@ -6203,6 +6438,7 @@ function probeProviders(env = process.env) {
|
|
|
6203
6438
|
if ((env["OPENAI_API_KEY"] ?? "").trim().length > 0) out.push("openai");
|
|
6204
6439
|
if ((env["GOOGLE_API_KEY"] ?? "").trim().length > 0) out.push("google");
|
|
6205
6440
|
if ((env["DEEPSEEK_API_KEY"] ?? "").trim().length > 0) out.push("deepseek");
|
|
6441
|
+
if ((env[META_MODEL_API_KEY_ENV] ?? "").trim().length > 0 || (env[META_MODEL_API_KEY_ALIAS] ?? "").trim().length > 0) out.push("meta");
|
|
6206
6442
|
return out;
|
|
6207
6443
|
}
|
|
6208
6444
|
async function loadFactoryAndCallers(injectedEngine, injectedShared) {
|
|
@@ -6248,21 +6484,24 @@ async function tryCreateEngineConsensusClientFromEnvAsync(options = {}) {
|
|
|
6248
6484
|
anthropic: loaded.shared.callAnthropicWithMetrics,
|
|
6249
6485
|
openai: loaded.shared.callOpenAIWithMetrics,
|
|
6250
6486
|
google: loaded.shared.callGeminiWithMetrics,
|
|
6251
|
-
deepseek: loaded.shared.callDeepSeekWithMetrics
|
|
6487
|
+
deepseek: loaded.shared.callDeepSeekWithMetrics,
|
|
6488
|
+
meta: options.metaCaller ?? callMetaWithMetrics
|
|
6252
6489
|
};
|
|
6253
6490
|
const modelByProvider = {
|
|
6254
6491
|
anthropic: options.models?.anthropic ?? DEFAULT_MODELS.anthropic,
|
|
6255
6492
|
openai: options.models?.openai ?? DEFAULT_MODELS.openai,
|
|
6256
6493
|
google: options.models?.google ?? DEFAULT_MODELS.google,
|
|
6257
|
-
deepseek: options.models?.deepseek ?? DEFAULT_MODELS.deepseek
|
|
6494
|
+
deepseek: options.models?.deepseek ?? DEFAULT_MODELS.deepseek,
|
|
6495
|
+
meta: options.models?.meta ?? DEFAULT_MODELS.meta
|
|
6258
6496
|
};
|
|
6497
|
+
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
6498
|
const panel = [];
|
|
6260
6499
|
for (const p of providers) {
|
|
6261
6500
|
try {
|
|
6262
6501
|
const adapter = loaded.engine.createAdapter(p, {
|
|
6263
6502
|
model: modelByProvider[p],
|
|
6264
6503
|
caller: callerByProvider[p],
|
|
6265
|
-
envSource:
|
|
6504
|
+
envSource: adapterEnv
|
|
6266
6505
|
});
|
|
6267
6506
|
panel.push(adapter);
|
|
6268
6507
|
} catch {
|
|
@@ -6484,7 +6723,7 @@ async function runLogin(opts = {}) {
|
|
|
6484
6723
|
const nowIso = opts.nowIso ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
6485
6724
|
const openBrowser = opts.openBrowser ?? defaultOpenBrowser;
|
|
6486
6725
|
const state = randomBytes(32).toString("base64url");
|
|
6487
|
-
return new Promise((
|
|
6726
|
+
return new Promise((resolve2, reject) => {
|
|
6488
6727
|
let settled = false;
|
|
6489
6728
|
const finish = (err, result) => {
|
|
6490
6729
|
if (settled) return;
|
|
@@ -6492,7 +6731,7 @@ async function runLogin(opts = {}) {
|
|
|
6492
6731
|
clearTimeout(timer);
|
|
6493
6732
|
server.close();
|
|
6494
6733
|
if (err) reject(err);
|
|
6495
|
-
else
|
|
6734
|
+
else resolve2(result);
|
|
6496
6735
|
};
|
|
6497
6736
|
const server = createServer2((req, res) => {
|
|
6498
6737
|
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|