@algosuite/vo-mcp 0.2.0-beta.20 → 0.2.0-beta.22
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/dist/cli.js +180 -15
- package/dist/cli.js.map +4 -4
- package/dist/index.js +186 -34
- package/dist/index.js.map +4 -4
- package/dist/runner-cli.js +675 -50
- package/dist/runner-cli.js.map +4 -4
- package/dist/runner-supervisor.js +32 -32
- package/dist/runner-supervisor.js.map +3 -3
- package/dist/set-key-cli.js +8 -2
- package/dist/set-key-cli.js.map +2 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1399,6 +1399,98 @@ var init_safe_memory_file = __esm({
|
|
|
1399
1399
|
}
|
|
1400
1400
|
});
|
|
1401
1401
|
|
|
1402
|
+
// src/tools/memory/memory-knowledge-bridge.ts
|
|
1403
|
+
var memory_knowledge_bridge_exports = {};
|
|
1404
|
+
__export(memory_knowledge_bridge_exports, {
|
|
1405
|
+
extractMemoryTitle: () => extractMemoryTitle,
|
|
1406
|
+
upsertMemoryFilesAsKnowledge: () => upsertMemoryFilesAsKnowledge
|
|
1407
|
+
});
|
|
1408
|
+
import { existsSync as existsSync5, readdirSync as readdirSync4, readFileSync as readFileSync7 } from "node:fs";
|
|
1409
|
+
function extractMemoryTitle(fileName, content) {
|
|
1410
|
+
const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
1411
|
+
if (frontmatter) {
|
|
1412
|
+
const description23 = frontmatter[1].match(/^description:\s*(.+)$/m);
|
|
1413
|
+
if (description23 && description23[1].trim()) return description23[1].trim().slice(0, 200);
|
|
1414
|
+
}
|
|
1415
|
+
const heading = content.match(/^#\s+(.+)$/m);
|
|
1416
|
+
if (heading && heading[1].trim()) return heading[1].trim().slice(0, 200);
|
|
1417
|
+
return fileName;
|
|
1418
|
+
}
|
|
1419
|
+
async function upsertMemoryFilesAsKnowledge(options) {
|
|
1420
|
+
const { controlPlaneUrl, token, memoryDir, fetchFn } = options;
|
|
1421
|
+
let files;
|
|
1422
|
+
try {
|
|
1423
|
+
if (!existsSync5(memoryDir)) {
|
|
1424
|
+
return { attempted: 0, upserted: 0, failed: 0, failures: [] };
|
|
1425
|
+
}
|
|
1426
|
+
files = readdirSync4(memoryDir).filter(
|
|
1427
|
+
(f) => f.endsWith(".md") && f.toUpperCase() !== "MEMORY.MD"
|
|
1428
|
+
);
|
|
1429
|
+
} catch (err) {
|
|
1430
|
+
return {
|
|
1431
|
+
attempted: 0,
|
|
1432
|
+
upserted: 0,
|
|
1433
|
+
failed: 1,
|
|
1434
|
+
failures: [`memory dir scan: ${err instanceof Error ? err.message : String(err)}`]
|
|
1435
|
+
};
|
|
1436
|
+
}
|
|
1437
|
+
let upserted = 0;
|
|
1438
|
+
const failures = [];
|
|
1439
|
+
for (const fileName of files) {
|
|
1440
|
+
try {
|
|
1441
|
+
const content = readFileSync7(resolveMemoryFilePath(memoryDir, fileName), "utf8");
|
|
1442
|
+
if (content.length > CONTENT_HARD_LIMIT) {
|
|
1443
|
+
failures.push(`${fileName}: ${content.length} chars exceeds the ${CONTENT_HARD_LIMIT} server limit \u2014 split the memory file`);
|
|
1444
|
+
continue;
|
|
1445
|
+
}
|
|
1446
|
+
const title = extractMemoryTitle(fileName, content);
|
|
1447
|
+
const base = {
|
|
1448
|
+
knowledge_class: "memory",
|
|
1449
|
+
source_path: `memory/${fileName}`,
|
|
1450
|
+
title,
|
|
1451
|
+
content
|
|
1452
|
+
};
|
|
1453
|
+
const post = (body) => fetchFn(`${controlPlaneUrl}/api/v1/knowledge/private`, {
|
|
1454
|
+
method: "POST",
|
|
1455
|
+
headers: {
|
|
1456
|
+
authorization: `Bearer ${token}`,
|
|
1457
|
+
"content-type": "application/json"
|
|
1458
|
+
},
|
|
1459
|
+
body: JSON.stringify(body)
|
|
1460
|
+
});
|
|
1461
|
+
let response = await post({
|
|
1462
|
+
...base,
|
|
1463
|
+
provenance: { written_by: "memory-bridge", source_kind: "operator_memory" }
|
|
1464
|
+
});
|
|
1465
|
+
if (response.status === 400) {
|
|
1466
|
+
response = await post(base);
|
|
1467
|
+
}
|
|
1468
|
+
if (response.status >= 200 && response.status < 300) {
|
|
1469
|
+
upserted += 1;
|
|
1470
|
+
} else {
|
|
1471
|
+
const text = await response.text();
|
|
1472
|
+
failures.push(`${fileName}: HTTP ${response.status} ${text.slice(0, 80)}`);
|
|
1473
|
+
}
|
|
1474
|
+
} catch (err) {
|
|
1475
|
+
failures.push(`${fileName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
return {
|
|
1479
|
+
attempted: files.length,
|
|
1480
|
+
upserted,
|
|
1481
|
+
failed: failures.length,
|
|
1482
|
+
failures: failures.slice(0, 5)
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
var CONTENT_HARD_LIMIT;
|
|
1486
|
+
var init_memory_knowledge_bridge = __esm({
|
|
1487
|
+
"src/tools/memory/memory-knowledge-bridge.ts"() {
|
|
1488
|
+
"use strict";
|
|
1489
|
+
init_safe_memory_file();
|
|
1490
|
+
CONTENT_HARD_LIMIT = 5e5;
|
|
1491
|
+
}
|
|
1492
|
+
});
|
|
1493
|
+
|
|
1402
1494
|
// src/tools/memory/sync-config.ts
|
|
1403
1495
|
var sync_config_exports = {};
|
|
1404
1496
|
__export(sync_config_exports, {
|
|
@@ -1413,7 +1505,7 @@ __export(sync_config_exports, {
|
|
|
1413
1505
|
});
|
|
1414
1506
|
import { homedir as homedir5 } from "node:os";
|
|
1415
1507
|
import { join as join7 } from "node:path";
|
|
1416
|
-
import { existsSync as
|
|
1508
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync3, readdirSync as readdirSync5 } from "node:fs";
|
|
1417
1509
|
function isToolInput22(v) {
|
|
1418
1510
|
if (typeof v !== "object" || v === null) return false;
|
|
1419
1511
|
const o = v;
|
|
@@ -1422,7 +1514,7 @@ function isToolInput22(v) {
|
|
|
1422
1514
|
return true;
|
|
1423
1515
|
}
|
|
1424
1516
|
function deriveProjectSlug(cwd) {
|
|
1425
|
-
return cwd.replace(
|
|
1517
|
+
return cwd.replace(/([^:\\/])[\\/]+$/, "$1").replace(/\\/g, "/").replace(/^([a-zA-Z]):/, (_m, drive) => `${drive.toUpperCase()}:`).replace(/[^a-zA-Z0-9]/g, "-");
|
|
1426
1518
|
}
|
|
1427
1519
|
function getMemoryDir(cwd) {
|
|
1428
1520
|
const slug = deriveProjectSlug(cwd);
|
|
@@ -1457,12 +1549,12 @@ async function pullMemory(controlPlaneUrl, token, memoryDir, fetchFn) {
|
|
|
1457
1549
|
return { pulled: data.entries.length, files };
|
|
1458
1550
|
}
|
|
1459
1551
|
async function pushMemory(controlPlaneUrl, token, memoryDir, sessionId, fetchFn) {
|
|
1460
|
-
if (!
|
|
1552
|
+
if (!existsSync6(memoryDir)) {
|
|
1461
1553
|
return { pushed: 0, created: 0, updated: 0 };
|
|
1462
1554
|
}
|
|
1463
|
-
const localFiles =
|
|
1555
|
+
const localFiles = readdirSync5(memoryDir).filter((f) => f.endsWith(".md")).map((f) => ({
|
|
1464
1556
|
file_name: f,
|
|
1465
|
-
content:
|
|
1557
|
+
content: readFileSync8(resolveMemoryFilePath(memoryDir, f), "utf8"),
|
|
1466
1558
|
entry_type: f === "MEMORY.md" ? "index" : "topic"
|
|
1467
1559
|
}));
|
|
1468
1560
|
if (localFiles.length === 0) {
|
|
@@ -1571,13 +1663,32 @@ async function runMemorySync(action, cwd, sessionId, fetchFn = globalThis.fetch)
|
|
|
1571
1663
|
return { synced: true, action: "pull", pulled: result2.pulled, files: result2.files, memory_dir: memoryDir };
|
|
1572
1664
|
}
|
|
1573
1665
|
const result = await pushMemory(baseUrl, token, memoryDir, sessionId, fetchFn);
|
|
1666
|
+
let bridge = { upserted: 0, failed: 0, failures: [] };
|
|
1667
|
+
try {
|
|
1668
|
+
const { upsertMemoryFilesAsKnowledge: upsertMemoryFilesAsKnowledge2 } = await Promise.resolve().then(() => (init_memory_knowledge_bridge(), memory_knowledge_bridge_exports));
|
|
1669
|
+
bridge = await upsertMemoryFilesAsKnowledge2({
|
|
1670
|
+
controlPlaneUrl: baseUrl,
|
|
1671
|
+
token,
|
|
1672
|
+
memoryDir,
|
|
1673
|
+
fetchFn
|
|
1674
|
+
});
|
|
1675
|
+
} catch (err) {
|
|
1676
|
+
bridge = {
|
|
1677
|
+
upserted: 0,
|
|
1678
|
+
failed: 1,
|
|
1679
|
+
failures: [`bridge unavailable: ${err instanceof Error ? err.message : String(err)}`]
|
|
1680
|
+
};
|
|
1681
|
+
}
|
|
1574
1682
|
return {
|
|
1575
1683
|
synced: true,
|
|
1576
1684
|
action: "push",
|
|
1577
1685
|
pushed: result.pushed,
|
|
1578
1686
|
created: result.created,
|
|
1579
1687
|
updated: result.updated,
|
|
1580
|
-
memory_dir: memoryDir
|
|
1688
|
+
memory_dir: memoryDir,
|
|
1689
|
+
knowledge_upserted: bridge.upserted,
|
|
1690
|
+
knowledge_failed: bridge.failed,
|
|
1691
|
+
...bridge.failed > 0 ? { knowledge_failures: bridge.failures } : {}
|
|
1581
1692
|
};
|
|
1582
1693
|
} catch (err) {
|
|
1583
1694
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -5507,6 +5618,7 @@ init_sync_config();
|
|
|
5507
5618
|
init_common();
|
|
5508
5619
|
var UPSERT_TOOL_NAME = "vo_private_knowledge_upsert";
|
|
5509
5620
|
var CONTEXT_TOOL_NAME = "vo_private_knowledge_context";
|
|
5621
|
+
var INVALIDATE_TOOL_NAME = "vo_private_knowledge_invalidate";
|
|
5510
5622
|
var KNOWLEDGE_CLASSES = ["memory", "skill", "doctrine", "hook", "command"];
|
|
5511
5623
|
var PRECISION_CHAR_BUDGET = 12e3;
|
|
5512
5624
|
var upsertInputSchema = {
|
|
@@ -5530,8 +5642,18 @@ var contextInputSchema = {
|
|
|
5530
5642
|
required: ["query"],
|
|
5531
5643
|
additionalProperties: false
|
|
5532
5644
|
};
|
|
5645
|
+
var invalidateInputSchema = {
|
|
5646
|
+
type: "object",
|
|
5647
|
+
properties: {
|
|
5648
|
+
knowledge_class: { type: "string", enum: KNOWLEDGE_CLASSES },
|
|
5649
|
+
source_path: { type: "string", minLength: 1, maxLength: 400, description: "Stable private source identifier of the entry to invalidate \u2014 must match the source_path used at upsert." }
|
|
5650
|
+
},
|
|
5651
|
+
required: ["knowledge_class", "source_path"],
|
|
5652
|
+
additionalProperties: false
|
|
5653
|
+
};
|
|
5533
5654
|
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.";
|
|
5534
5655
|
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.";
|
|
5656
|
+
var invalidateDescription = `Soft-deletes one private-knowledge entry for the authenticated operator: closes the live entry\u2019s validity window (bi-temporal) so it stops surfacing in retrieval. Never destroys data \u2014 invalidated versions remain queryable server-side via include_invalidated. Identify the entry by the same { knowledge_class, source_path } used at upsert; a not_found response means no live entry matches. After invalidating, self-test via ${CONTEXT_TOOL_NAME} to confirm the entry no longer surfaces.`;
|
|
5535
5657
|
function isKnowledgeClass(value) {
|
|
5536
5658
|
return typeof value === "string" && KNOWLEDGE_CLASSES.includes(value);
|
|
5537
5659
|
}
|
|
@@ -5540,6 +5662,11 @@ function isUpsertInput(value) {
|
|
|
5540
5662
|
const input = value;
|
|
5541
5663
|
return isKnowledgeClass(input["knowledge_class"]) && typeof input["source_path"] === "string" && typeof input["title"] === "string" && typeof input["content"] === "string";
|
|
5542
5664
|
}
|
|
5665
|
+
function isInvalidateInput(value) {
|
|
5666
|
+
if (typeof value !== "object" || value === null) return false;
|
|
5667
|
+
const input = value;
|
|
5668
|
+
return isKnowledgeClass(input["knowledge_class"]) && typeof input["source_path"] === "string";
|
|
5669
|
+
}
|
|
5543
5670
|
function isContextInput(value) {
|
|
5544
5671
|
if (typeof value !== "object" || value === null) return false;
|
|
5545
5672
|
const input = value;
|
|
@@ -5573,7 +5700,12 @@ async function callPrivateKnowledge(path3, body, fetchFn) {
|
|
|
5573
5700
|
body: JSON.stringify(body)
|
|
5574
5701
|
});
|
|
5575
5702
|
const text = await response.text();
|
|
5576
|
-
|
|
5703
|
+
let parsed;
|
|
5704
|
+
try {
|
|
5705
|
+
parsed = text ? JSON.parse(text) : null;
|
|
5706
|
+
} catch {
|
|
5707
|
+
parsed = null;
|
|
5708
|
+
}
|
|
5577
5709
|
if (response.status < 200 || response.status >= 300) {
|
|
5578
5710
|
return { ok: false, status: response.status, response: parsed ?? text };
|
|
5579
5711
|
}
|
|
@@ -5594,6 +5726,13 @@ async function handlePrivateKnowledgeUpsert(_deps, rawInput, _signal, fetchFn =
|
|
|
5594
5726
|
}
|
|
5595
5727
|
return jsonContent(envelope);
|
|
5596
5728
|
}
|
|
5729
|
+
async function handlePrivateKnowledgeInvalidate(_deps, rawInput, _signal, fetchFn = globalThis.fetch) {
|
|
5730
|
+
if (!isInvalidateInput(rawInput)) {
|
|
5731
|
+
throw invalidParams(INVALIDATE_TOOL_NAME, "expected { knowledge_class, source_path }.");
|
|
5732
|
+
}
|
|
5733
|
+
const payload = await callPrivateKnowledge("/api/v1/knowledge/private/invalidate", rawInput, fetchFn);
|
|
5734
|
+
return jsonContent({ tool: INVALIDATE_TOOL_NAME, schema_version: 1, payload });
|
|
5735
|
+
}
|
|
5597
5736
|
async function handlePrivateKnowledgeContext(_deps, rawInput, _signal, fetchFn = globalThis.fetch) {
|
|
5598
5737
|
if (!isContextInput(rawInput)) {
|
|
5599
5738
|
throw invalidParams(CONTEXT_TOOL_NAME, "expected { query, optional limit, optional knowledge_class }.");
|
|
@@ -5746,11 +5885,11 @@ async function handleHqWhiteboardRead(_deps, rawInput, signal) {
|
|
|
5746
5885
|
}
|
|
5747
5886
|
|
|
5748
5887
|
// src/tools/skills/skill-corpus.ts
|
|
5749
|
-
import { existsSync as
|
|
5888
|
+
import { existsSync as existsSync7, statSync as statSync5 } from "node:fs";
|
|
5750
5889
|
import { dirname as dirname5, isAbsolute, join as join9, resolve as resolve2 } from "node:path";
|
|
5751
5890
|
|
|
5752
5891
|
// ../skill-registry/src/loader.ts
|
|
5753
|
-
import { readdirSync as
|
|
5892
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync9, statSync as statSync4 } from "node:fs";
|
|
5754
5893
|
import { join as join8 } from "node:path";
|
|
5755
5894
|
var InvalidSkillFrontmatterError = class extends Error {
|
|
5756
5895
|
constructor(skillFile, reason) {
|
|
@@ -5801,7 +5940,7 @@ ${FRONTMATTER_DELIMITER}
|
|
|
5801
5940
|
return { name, description: description23, body };
|
|
5802
5941
|
}
|
|
5803
5942
|
function loadSkillsFromDir(skillsDir) {
|
|
5804
|
-
const entries =
|
|
5943
|
+
const entries = readdirSync6(skillsDir);
|
|
5805
5944
|
const skills = [];
|
|
5806
5945
|
for (const entry of entries) {
|
|
5807
5946
|
const entryPath = join8(skillsDir, entry);
|
|
@@ -5815,7 +5954,7 @@ function loadSkillsFromDir(skillsDir) {
|
|
|
5815
5954
|
const skillFile = join8(entryPath, "SKILL.md");
|
|
5816
5955
|
let raw;
|
|
5817
5956
|
try {
|
|
5818
|
-
raw =
|
|
5957
|
+
raw = readFileSync9(skillFile, "utf8");
|
|
5819
5958
|
} catch {
|
|
5820
5959
|
continue;
|
|
5821
5960
|
}
|
|
@@ -5857,12 +5996,12 @@ function resolveSkillsDir(env = process.env, startDir = process.cwd()) {
|
|
|
5857
5996
|
const override = env.VO_SKILLS_DIR;
|
|
5858
5997
|
if (typeof override === "string" && override.length > 0) {
|
|
5859
5998
|
const abs = isAbsolute(override) ? override : resolve2(startDir, override);
|
|
5860
|
-
return
|
|
5999
|
+
return existsSync7(abs) && statSync5(abs).isDirectory() ? abs : null;
|
|
5861
6000
|
}
|
|
5862
6001
|
let dir = resolve2(startDir);
|
|
5863
6002
|
for (let i = 0; i < MAX_WALK_UP_LEVELS; i += 1) {
|
|
5864
6003
|
const candidate = join9(dir, ".claude", "skills");
|
|
5865
|
-
if (
|
|
6004
|
+
if (existsSync7(candidate) && statSync5(candidate).isDirectory()) return candidate;
|
|
5866
6005
|
const parent = dirname5(dir);
|
|
5867
6006
|
if (parent === dir) break;
|
|
5868
6007
|
dir = parent;
|
|
@@ -6130,6 +6269,14 @@ function buildToolRegistry() {
|
|
|
6130
6269
|
},
|
|
6131
6270
|
handler: handlePrivateKnowledgeContext
|
|
6132
6271
|
},
|
|
6272
|
+
[INVALIDATE_TOOL_NAME]: {
|
|
6273
|
+
definition: {
|
|
6274
|
+
name: INVALIDATE_TOOL_NAME,
|
|
6275
|
+
description: invalidateDescription,
|
|
6276
|
+
inputSchema: invalidateInputSchema
|
|
6277
|
+
},
|
|
6278
|
+
handler: handlePrivateKnowledgeInvalidate
|
|
6279
|
+
},
|
|
6133
6280
|
[POST_TOOL_NAME]: {
|
|
6134
6281
|
definition: {
|
|
6135
6282
|
name: POST_TOOL_NAME,
|
|
@@ -7401,18 +7548,36 @@ if (process.argv[2] === "login") {
|
|
|
7401
7548
|
const cwdFlag = process.argv.indexOf("--cwd");
|
|
7402
7549
|
const cwd = cwdFlag >= 0 && typeof process.argv[cwdFlag + 1] === "string" ? process.argv[cwdFlag + 1] : process.cwd();
|
|
7403
7550
|
const sessionId = randomUUID5();
|
|
7551
|
+
const appendSyncLog = async (line) => {
|
|
7552
|
+
try {
|
|
7553
|
+
const { appendFileSync: appendFileSync2, mkdirSync: mkdirSync6 } = await import("node:fs");
|
|
7554
|
+
const { join: join11 } = await import("node:path");
|
|
7555
|
+
const { homedir: homedir7 } = await import("node:os");
|
|
7556
|
+
const dir = join11(homedir7(), ".claude");
|
|
7557
|
+
mkdirSync6(dir, { recursive: true });
|
|
7558
|
+
appendFileSync2(join11(dir, "vo-mcp-sync.log"), `${line}
|
|
7559
|
+
`, "utf8");
|
|
7560
|
+
} catch {
|
|
7561
|
+
}
|
|
7562
|
+
};
|
|
7404
7563
|
Promise.resolve().then(() => (init_sync_config(), sync_config_exports)).then(async ({ runMemorySync: runMemorySync2, isNoopSyncReason: isNoopSyncReason2 }) => {
|
|
7405
7564
|
const r = await runMemorySync2(action, cwd, sessionId);
|
|
7565
|
+
const stamp = `${sessionId} ${action}`;
|
|
7406
7566
|
if (r.synced) {
|
|
7407
7567
|
console.error(`[vo-mcp] sync ${action} ok: ${JSON.stringify(r)}`);
|
|
7568
|
+
await appendSyncLog(`ok ${stamp} ${JSON.stringify(r)}`);
|
|
7408
7569
|
} else if (isNoopSyncReason2(r.reason)) {
|
|
7409
7570
|
console.error(`[vo-mcp] sync ${action} skipped: ${r.reason}`);
|
|
7571
|
+
await appendSyncLog(`skip ${stamp} ${r.reason ?? ""}`);
|
|
7410
7572
|
} else {
|
|
7411
7573
|
console.error(`[vo-mcp] sync ${action} failed: ${r.reason}`);
|
|
7574
|
+
await appendSyncLog(`FAIL ${stamp} ${r.reason ?? ""}`);
|
|
7412
7575
|
process.exitCode = 1;
|
|
7413
7576
|
}
|
|
7414
|
-
}).catch((err) => {
|
|
7415
|
-
|
|
7577
|
+
}).catch(async (err) => {
|
|
7578
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7579
|
+
console.error("[vo-mcp] sync fatal:", message);
|
|
7580
|
+
await appendSyncLog(`FATAL ${sessionId} ${action} ${message}`);
|
|
7416
7581
|
process.exitCode = 1;
|
|
7417
7582
|
});
|
|
7418
7583
|
} else {
|