monomind 2.7.4 → 2.7.5
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/package.json +1 -1
- package/packages/@monomind/cli/bin/cli.js +17 -1
- package/packages/@monomind/cli/dist/src/commands/doctor-project-checks.d.ts +3 -0
- package/packages/@monomind/cli/dist/src/commands/doctor-project-checks.js +81 -8
- package/packages/@monomind/cli/dist/src/commands/doctor.js +16 -2
- package/packages/@monomind/cli/dist/src/commands/memory-crud.js +15 -5
- package/packages/@monomind/cli/dist/src/commands/security-misc.js +7 -2
- package/packages/@monomind/cli/dist/src/init/helpers-generator.js +5 -0
- package/packages/@monomind/cli/dist/src/mcp-client.js +0 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.js +1 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/embeddings-tools.d.ts +2 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/embeddings-tools.js +11 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/guidance-tools.js +17 -17
- package/packages/@monomind/cli/dist/src/mcp-tools/hive-mind-tools.d.ts +2 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/hive-mind-tools.js +14 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/index.d.ts +0 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/index.js +0 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +1 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/neural-tools.js +1 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/swarm-tools.js +2 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/transfer-tools.d.ts +2 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/transfer-tools.js +11 -1
- package/packages/@monomind/cli/dist/src/ui/dashboard.html +44 -618
- package/packages/@monomind/cli/dist/src/ui/server.mjs +0 -275
- package/packages/@monomind/cli/dist/src/utils/cosine-similarity.d.ts +2 -2
- package/packages/@monomind/cli/dist/src/utils/cosine-similarity.js +2 -2
- package/packages/@monomind/cli/package.json +1 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/coherence/causal-infer.d.ts +0 -15
- package/packages/@monomind/cli/dist/src/mcp-tools/coherence/causal-infer.js +0 -333
- package/packages/@monomind/cli/dist/src/mcp-tools/coherence/coherence-check.d.ts +0 -15
- package/packages/@monomind/cli/dist/src/mcp-tools/coherence/coherence-check.js +0 -188
- package/packages/@monomind/cli/dist/src/mcp-tools/coherence/consensus-verify.d.ts +0 -14
- package/packages/@monomind/cli/dist/src/mcp-tools/coherence/consensus-verify.js +0 -305
- package/packages/@monomind/cli/dist/src/mcp-tools/coherence/memory-gate.d.ts +0 -16
- package/packages/@monomind/cli/dist/src/mcp-tools/coherence/memory-gate.js +0 -279
- package/packages/@monomind/cli/dist/src/mcp-tools/coherence/quantum-topology.d.ts +0 -15
- package/packages/@monomind/cli/dist/src/mcp-tools/coherence/quantum-topology.js +0 -400
- package/packages/@monomind/cli/dist/src/mcp-tools/coherence/spectral-analyze.d.ts +0 -14
- package/packages/@monomind/cli/dist/src/mcp-tools/coherence/spectral-analyze.js +0 -312
- package/packages/@monomind/cli/dist/src/mcp-tools/coherence/types.d.ts +0 -430
- package/packages/@monomind/cli/dist/src/mcp-tools/coherence/types.js +0 -144
- package/packages/@monomind/cli/dist/src/mcp-tools/coherence-tools.d.ts +0 -9
- package/packages/@monomind/cli/dist/src/mcp-tools/coherence-tools.js +0 -25
- package/packages/@monomind/cli/dist/src/mcp-tools/daa-tools.d.ts +0 -13
- package/packages/@monomind/cli/dist/src/mcp-tools/daa-tools.js +0 -553
|
@@ -1874,22 +1874,6 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1874
1874
|
return;
|
|
1875
1875
|
}
|
|
1876
1876
|
|
|
1877
|
-
// GET /api/adr-file?file=<name>&dir=<dir> — read a single ADR markdown file
|
|
1878
|
-
if (req.method === 'GET' && url === '/api/adr-file') {
|
|
1879
|
-
try {
|
|
1880
|
-
const qs = new URL(req.url, 'http://localhost').searchParams;
|
|
1881
|
-
const dir = qs.get('dir') || projectDir || process.cwd();
|
|
1882
|
-
const fname = (qs.get('file') || '').replace(/[/\\]/g, '');
|
|
1883
|
-
if (!fname || !fname.endsWith('.md')) { res.writeHead(400); res.end('{"error":"invalid file"}'); return; }
|
|
1884
|
-
const fpath = path.join(path.resolve(dir), 'docs', 'adrs', fname);
|
|
1885
|
-
if (!fs.existsSync(fpath)) { res.writeHead(404); res.end('{"error":"not found"}'); return; }
|
|
1886
|
-
const content = fs.readFileSync(fpath, 'utf8');
|
|
1887
|
-
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
1888
|
-
res.end(JSON.stringify({ file: fname, content }));
|
|
1889
|
-
} catch (err) { res.writeHead(500); res.end(JSON.stringify({ error: err.message })); }
|
|
1890
|
-
return;
|
|
1891
|
-
}
|
|
1892
|
-
|
|
1893
1877
|
// ------------------------------------------------------- GET /api/memory-files
|
|
1894
1878
|
if (req.method === 'GET' && url === '/api/memory-files') {
|
|
1895
1879
|
try {
|
|
@@ -6331,265 +6315,6 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
6331
6315
|
return;
|
|
6332
6316
|
}
|
|
6333
6317
|
|
|
6334
|
-
// GET /api/second-brain-metrics — aggregated injection analytics from second-brain.jsonl
|
|
6335
|
-
if (req.method === 'GET' && url === '/api/second-brain-metrics') {
|
|
6336
|
-
try {
|
|
6337
|
-
const metricsFile = path.join(projectDir || process.cwd(), '.monomind', 'metrics', 'second-brain.jsonl');
|
|
6338
|
-
if (!fs.existsSync(metricsFile)) {
|
|
6339
|
-
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
6340
|
-
res.end(JSON.stringify({ total: 0, injected: 0, hitRate: 0, methods: {}, avgScore: 0, sparkline: [], scores: [] }));
|
|
6341
|
-
return;
|
|
6342
|
-
}
|
|
6343
|
-
const raw = fs.readFileSync(metricsFile, 'utf8');
|
|
6344
|
-
const entries = raw.split('\n').filter(Boolean).map(l => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean);
|
|
6345
|
-
const total = entries.length;
|
|
6346
|
-
const injectedCount = entries.filter(e => e.injected).length;
|
|
6347
|
-
const hitRate = total > 0 ? injectedCount / total : 0;
|
|
6348
|
-
// Method distribution
|
|
6349
|
-
const methods = {};
|
|
6350
|
-
for (const e of entries) { const m = e.method || 'unknown'; methods[m] = (methods[m] || 0) + 1; }
|
|
6351
|
-
// Average top score (only for entries with hits)
|
|
6352
|
-
const withScores = entries.filter(e => typeof e.topScore === 'number' && e.topScore !== null);
|
|
6353
|
-
const avgScore = withScores.length > 0 ? withScores.reduce((s, e) => s + e.topScore, 0) / withScores.length : 0;
|
|
6354
|
-
// Score distribution buckets
|
|
6355
|
-
const scoreBuckets = [0, 0, 0, 0, 0]; // <0.3, 0.3-0.5, 0.5-0.7, 0.7-0.9, 0.9+
|
|
6356
|
-
for (const e of withScores) {
|
|
6357
|
-
const s = e.topScore;
|
|
6358
|
-
if (s < 0.3) scoreBuckets[0]++;
|
|
6359
|
-
else if (s < 0.5) scoreBuckets[1]++;
|
|
6360
|
-
else if (s < 0.7) scoreBuckets[2]++;
|
|
6361
|
-
else if (s < 0.9) scoreBuckets[3]++;
|
|
6362
|
-
else scoreBuckets[4]++;
|
|
6363
|
-
}
|
|
6364
|
-
// Sparkline: group by hour for last 24h, then by day for older
|
|
6365
|
-
const now = Date.now();
|
|
6366
|
-
const h24 = now - 86400000;
|
|
6367
|
-
const sparkline = [];
|
|
6368
|
-
// Last 24 hours in 2h buckets
|
|
6369
|
-
for (let t = h24; t < now; t += 7200000) {
|
|
6370
|
-
const bucket = entries.filter(e => e.ts >= t && e.ts < t + 7200000);
|
|
6371
|
-
const inj = bucket.filter(e => e.injected).length;
|
|
6372
|
-
sparkline.push({ t, total: bucket.length, injected: inj });
|
|
6373
|
-
}
|
|
6374
|
-
// Top sources from recent entries (last 200)
|
|
6375
|
-
const recentSlice = entries.slice(-200);
|
|
6376
|
-
const misses = recentSlice.filter(e => !e.injected).length;
|
|
6377
|
-
// Route overrides (misroute counts)
|
|
6378
|
-
let routeOverrides = {};
|
|
6379
|
-
try {
|
|
6380
|
-
const overridesFile = path.join(projectDir || process.cwd(), '.monomind', 'metrics', 'route-overrides.json');
|
|
6381
|
-
if (fs.existsSync(overridesFile)) routeOverrides = JSON.parse(fs.readFileSync(overridesFile, 'utf8'));
|
|
6382
|
-
} catch {}
|
|
6383
|
-
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
6384
|
-
res.end(JSON.stringify({ total, injected: injectedCount, hitRate: +hitRate.toFixed(3), methods, avgScore: +avgScore.toFixed(3), sparkline, scoreBuckets, recentMisses: misses, recentTotal: recentSlice.length, routeOverrides }));
|
|
6385
|
-
} catch (err) {
|
|
6386
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
6387
|
-
res.end(JSON.stringify({ error: err.message }));
|
|
6388
|
-
}
|
|
6389
|
-
return;
|
|
6390
|
-
}
|
|
6391
|
-
|
|
6392
|
-
// POST /api/second-brain-route — show query router decision for a given query
|
|
6393
|
-
if (req.method === 'POST' && url === '/api/second-brain-route') {
|
|
6394
|
-
let body = '';
|
|
6395
|
-
req.on('data', c => { body += c; if (body.length > 65536) { req.destroy(); return; } });
|
|
6396
|
-
req.on('end', async () => {
|
|
6397
|
-
try {
|
|
6398
|
-
const { query } = JSON.parse(body || '{}');
|
|
6399
|
-
if (!query) { res.writeHead(400, { 'Content-Type': 'application/json' }); res.end('{"error":"query required"}'); return; }
|
|
6400
|
-
const routerMod = await import('../memory/query-router.js');
|
|
6401
|
-
const route = routerMod.routeQuery(String(query).slice(0, 2000));
|
|
6402
|
-
const overrides = routerMod.getRouteOverrides(true);
|
|
6403
|
-
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
6404
|
-
res.end(JSON.stringify({ route, overrides }));
|
|
6405
|
-
} catch (err) {
|
|
6406
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
6407
|
-
res.end(JSON.stringify({ error: err.message }));
|
|
6408
|
-
}
|
|
6409
|
-
});
|
|
6410
|
-
return;
|
|
6411
|
-
}
|
|
6412
|
-
|
|
6413
|
-
// GET/PUT /api/second-brain-config — read/write Second Brain config (.monomind/second-brain.json)
|
|
6414
|
-
if ((req.method === 'GET' || req.method === 'PUT') && url === '/api/second-brain-config') {
|
|
6415
|
-
const configFile = path.join(projectDir || process.cwd(), '.monomind', 'second-brain.json');
|
|
6416
|
-
if (req.method === 'GET') {
|
|
6417
|
-
try {
|
|
6418
|
-
let config = { relevanceFloor: 0.35, injectionLimit: 5 };
|
|
6419
|
-
try { config = { ...config, ...JSON.parse(fs.readFileSync(configFile, 'utf8')) }; } catch {}
|
|
6420
|
-
// Auto-tune suggestion: analyze telemetry to suggest optimal floor
|
|
6421
|
-
let suggestion = null;
|
|
6422
|
-
try {
|
|
6423
|
-
const metricsFile = path.join(projectDir || process.cwd(), '.monomind', 'metrics', 'second-brain.jsonl');
|
|
6424
|
-
if (fs.existsSync(metricsFile)) {
|
|
6425
|
-
const entries = fs.readFileSync(metricsFile, 'utf8').split('\n').filter(Boolean)
|
|
6426
|
-
.map(l => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean);
|
|
6427
|
-
const withScores = entries.filter(e => typeof e.topScore === 'number' && e.topScore !== null);
|
|
6428
|
-
if (withScores.length >= 20) {
|
|
6429
|
-
const scores = withScores.map(e => e.topScore).sort((a, b) => a - b);
|
|
6430
|
-
const p25 = scores[Math.floor(scores.length * 0.25)];
|
|
6431
|
-
const hitRate = entries.filter(e => e.injected).length / entries.length;
|
|
6432
|
-
// If hit rate is very low (<30%), suggest lowering floor
|
|
6433
|
-
// If hit rate is very high (>80%) with low avg scores, suggest raising
|
|
6434
|
-
if (hitRate < 0.3 && p25 > 0.2) suggestion = { floor: +(p25 - 0.05).toFixed(2), reason: 'Low hit rate (' + (hitRate * 100).toFixed(0) + '%) — lowering floor may surface more relevant results' };
|
|
6435
|
-
else if (hitRate > 0.8 && scores[Math.floor(scores.length * 0.1)] < 0.35) suggestion = { floor: 0.4, reason: 'High hit rate with low-quality matches — raising floor filters noise' };
|
|
6436
|
-
}
|
|
6437
|
-
}
|
|
6438
|
-
} catch {}
|
|
6439
|
-
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
6440
|
-
res.end(JSON.stringify({ config, suggestion }));
|
|
6441
|
-
} catch (err) {
|
|
6442
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
6443
|
-
res.end(JSON.stringify({ error: err.message }));
|
|
6444
|
-
}
|
|
6445
|
-
return;
|
|
6446
|
-
}
|
|
6447
|
-
if (req.method === 'PUT') {
|
|
6448
|
-
let body = '';
|
|
6449
|
-
req.on('data', c => { body += c; if (body.length > 4096) { req.destroy(); return; } });
|
|
6450
|
-
req.on('end', () => {
|
|
6451
|
-
try {
|
|
6452
|
-
const raw = JSON.parse(body || '{}');
|
|
6453
|
-
const updates = {};
|
|
6454
|
-
if (typeof raw.relevanceFloor === 'number' && raw.relevanceFloor >= 0 && raw.relevanceFloor <= 1) updates.relevanceFloor = raw.relevanceFloor;
|
|
6455
|
-
if (typeof raw.injectionLimit === 'number' && raw.injectionLimit >= 1 && raw.injectionLimit <= 50) updates.injectionLimit = Math.floor(raw.injectionLimit);
|
|
6456
|
-
if (Object.keys(updates).length === 0) { res.writeHead(400, { 'Content-Type': 'application/json' }); res.end('{"error":"no valid fields to update"}'); return; }
|
|
6457
|
-
let config = {};
|
|
6458
|
-
try { config = JSON.parse(fs.readFileSync(configFile, 'utf8')); } catch {}
|
|
6459
|
-
config = { ...config, ...updates };
|
|
6460
|
-
fs.mkdirSync(path.dirname(configFile), { recursive: true });
|
|
6461
|
-
fs.writeFileSync(configFile, JSON.stringify(config, null, 2));
|
|
6462
|
-
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
6463
|
-
res.end(JSON.stringify({ ok: true, config }));
|
|
6464
|
-
} catch (err) {
|
|
6465
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
6466
|
-
res.end(JSON.stringify({ error: err.message }));
|
|
6467
|
-
}
|
|
6468
|
-
});
|
|
6469
|
-
return;
|
|
6470
|
-
}
|
|
6471
|
-
}
|
|
6472
|
-
|
|
6473
|
-
// GET /api/second-brain-docs — document-level view from doc-metadata.jsonl
|
|
6474
|
-
if (req.method === 'GET' && url === '/api/second-brain-docs') {
|
|
6475
|
-
try {
|
|
6476
|
-
const d = path.resolve(projectDir || process.cwd());
|
|
6477
|
-
const metaFile = path.join(d, '.monomind', 'knowledge', 'doc-metadata.jsonl');
|
|
6478
|
-
// Also check global brain
|
|
6479
|
-
const globalDir = process.env.MONOMIND_GLOBAL_BRAIN_DIR || path.join(os.homedir(), '.monomind', 'global-brain');
|
|
6480
|
-
const globalMetaFile = path.join(globalDir, '.monomind', 'knowledge', 'doc-metadata.jsonl');
|
|
6481
|
-
const docs = [];
|
|
6482
|
-
let totalSize = 0, totalChunks = 0;
|
|
6483
|
-
for (const [file, scope] of [[metaFile, 'project'], [globalMetaFile, 'global']]) {
|
|
6484
|
-
if (!fs.existsSync(file)) continue;
|
|
6485
|
-
const latest = new Map();
|
|
6486
|
-
for (const l of fs.readFileSync(file, 'utf8').split('\n')) {
|
|
6487
|
-
if (!l.trim()) continue;
|
|
6488
|
-
try {
|
|
6489
|
-
const m = JSON.parse(l);
|
|
6490
|
-
latest.set(m.filePath + ' ' + m.scope, m);
|
|
6491
|
-
} catch {}
|
|
6492
|
-
}
|
|
6493
|
-
for (const m of latest.values()) {
|
|
6494
|
-
if (m.chunkCount < 0) continue;
|
|
6495
|
-
const exists = fs.existsSync(m.filePath);
|
|
6496
|
-
docs.push({ ...m, brainScope: scope, exists, name: path.basename(m.filePath) });
|
|
6497
|
-
totalSize += m.size || 0;
|
|
6498
|
-
totalChunks += m.chunkCount || 0;
|
|
6499
|
-
}
|
|
6500
|
-
}
|
|
6501
|
-
docs.sort((a, b) => (b.indexedAt || '').localeCompare(a.indexedAt || ''));
|
|
6502
|
-
const globalCount = docs.filter(d => d.brainScope === 'global').length;
|
|
6503
|
-
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
6504
|
-
res.end(JSON.stringify({ docs, totalDocs: docs.length, totalSize, totalChunks, globalCount, projectCount: docs.length - globalCount }));
|
|
6505
|
-
} catch (err) {
|
|
6506
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
6507
|
-
res.end(JSON.stringify({ error: err.message }));
|
|
6508
|
-
}
|
|
6509
|
-
return;
|
|
6510
|
-
}
|
|
6511
|
-
|
|
6512
|
-
// GET /api/second-brain-ingest-stream — SSE stream tailing doc-metadata.jsonl for live ingest events
|
|
6513
|
-
if (req.method === 'GET' && url === '/api/second-brain-ingest-stream') {
|
|
6514
|
-
res.writeHead(200, {
|
|
6515
|
-
'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive',
|
|
6516
|
-
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {})
|
|
6517
|
-
});
|
|
6518
|
-
res.write('data: {"type":"connected"}\n\n');
|
|
6519
|
-
const metaFile = path.join(projectDir || process.cwd(), '.monomind', 'knowledge', 'doc-metadata.jsonl');
|
|
6520
|
-
let lastSize = 0;
|
|
6521
|
-
try { lastSize = fs.statSync(metaFile).size; } catch {}
|
|
6522
|
-
function _sseTailFile(filePath, lastSizeRef, eventType, transform) {
|
|
6523
|
-
try {
|
|
6524
|
-
const stat = fs.statSync(filePath);
|
|
6525
|
-
if (stat.size < lastSizeRef.v) { lastSizeRef.v = 0; }
|
|
6526
|
-
if (stat.size > lastSizeRef.v) {
|
|
6527
|
-
let fd;
|
|
6528
|
-
try {
|
|
6529
|
-
fd = fs.openSync(filePath, 'r');
|
|
6530
|
-
const buf = Buffer.alloc(stat.size - lastSizeRef.v);
|
|
6531
|
-
fs.readSync(fd, buf, 0, buf.length, lastSizeRef.v);
|
|
6532
|
-
lastSizeRef.v = stat.size;
|
|
6533
|
-
const lines = buf.toString('utf8').split('\n').filter(Boolean);
|
|
6534
|
-
for (const l of lines) {
|
|
6535
|
-
try { const parsed = JSON.parse(l); res.write('data: ' + JSON.stringify(transform(parsed)) + '\n\n'); } catch {}
|
|
6536
|
-
}
|
|
6537
|
-
} finally { if (fd !== undefined) fs.closeSync(fd); }
|
|
6538
|
-
}
|
|
6539
|
-
} catch {}
|
|
6540
|
-
}
|
|
6541
|
-
const metaSize = { v: lastSize };
|
|
6542
|
-
const interval = setInterval(() => _sseTailFile(metaFile, metaSize, 'ingest', m => ({ type: 'ingest', doc: { ...m, name: path.basename(m.filePath || '') } })), 2000);
|
|
6543
|
-
const metricsFile = path.join(projectDir || process.cwd(), '.monomind', 'metrics', 'second-brain.jsonl');
|
|
6544
|
-
const metricsSize = { v: 0 };
|
|
6545
|
-
try { metricsSize.v = fs.statSync(metricsFile).size; } catch {}
|
|
6546
|
-
const metricsInterval = setInterval(() => _sseTailFile(metricsFile, metricsSize, 'injection', e => ({ type: 'injection', event: e })), 3000);
|
|
6547
|
-
req.on('close', () => { clearInterval(interval); clearInterval(metricsInterval); });
|
|
6548
|
-
return;
|
|
6549
|
-
}
|
|
6550
|
-
|
|
6551
|
-
// GET /api/second-brain-kg — knowledge graph triplets + stats for the KG explorer
|
|
6552
|
-
if (req.method === 'GET' && url === '/api/second-brain-kg') {
|
|
6553
|
-
try {
|
|
6554
|
-
const qs = new URL(req.url, 'http://localhost').searchParams;
|
|
6555
|
-
const query = (qs.get('q') || '').slice(0, 200);
|
|
6556
|
-
const limit = Math.min(parseInt(qs.get('limit') || '50', 10), 200);
|
|
6557
|
-
const monoDir = path.join(projectDir || process.cwd(), '.monomind');
|
|
6558
|
-
const dbPath = path.join(monoDir, 'memory');
|
|
6559
|
-
let stats = { nodes: 0, edges: 0 }, triplets = [], glossary = [];
|
|
6560
|
-
try {
|
|
6561
|
-
const kg = await import('../memory/memory-kg.js');
|
|
6562
|
-
stats = await kg.kgStats({ dbPath });
|
|
6563
|
-
glossary = await kg.kgGlossary({ dbPath, limit: 30 });
|
|
6564
|
-
if (query) {
|
|
6565
|
-
const searchResult = await kg.kgSearch({ query, limit, dbPath });
|
|
6566
|
-
triplets = searchResult.triplets || searchResult || [];
|
|
6567
|
-
} else {
|
|
6568
|
-
const searchResult = await kg.kgSearch({ query: '*', limit, dbPath }).catch(() => null);
|
|
6569
|
-
triplets = searchResult ? (searchResult.triplets || searchResult) : [];
|
|
6570
|
-
}
|
|
6571
|
-
} catch {}
|
|
6572
|
-
// Also check palace kg.json as fallback
|
|
6573
|
-
if (triplets.length === 0) {
|
|
6574
|
-
try {
|
|
6575
|
-
const palaceDir = path.join(monoDir, 'palace');
|
|
6576
|
-
const kgData = JSON.parse(fs.readFileSync(path.join(palaceDir, 'kg.json'), 'utf8'));
|
|
6577
|
-
if (Array.isArray(kgData)) {
|
|
6578
|
-
triplets = kgData.slice(0, limit);
|
|
6579
|
-
stats.nodes = new Set(kgData.flatMap(t => [t.subject || t.s, t.object || t.o])).size;
|
|
6580
|
-
stats.edges = kgData.length;
|
|
6581
|
-
}
|
|
6582
|
-
} catch {}
|
|
6583
|
-
}
|
|
6584
|
-
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
6585
|
-
res.end(JSON.stringify({ stats, triplets, glossary }));
|
|
6586
|
-
} catch (err) {
|
|
6587
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
6588
|
-
res.end(JSON.stringify({ error: err.message }));
|
|
6589
|
-
}
|
|
6590
|
-
return;
|
|
6591
|
-
}
|
|
6592
|
-
|
|
6593
6318
|
// GET /api/mastermind/metrics — aggregate system metrics from token-summary and swarm-activity
|
|
6594
6319
|
if (req.method === 'GET' && url === '/api/mastermind/metrics') {
|
|
6595
6320
|
try {
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* - memory/intelligence.ts (ReasoningBank.cosineSim)
|
|
6
6
|
* - mcp-tools/neural-tools.ts (cosineSimilarity)
|
|
7
7
|
* - mcp-tools/embeddings-tools.ts (cosineSimilarity)
|
|
8
|
-
* - mcp-tools/coherence/types.ts (
|
|
9
|
-
*
|
|
8
|
+
* - mcp-tools/coherence/types.ts + coherence-check.ts (both since deleted
|
|
9
|
+
* along with the rest of the uncalled coherence_* family)
|
|
10
10
|
*
|
|
11
11
|
* This is the single canonical implementation. Accepts number[] or
|
|
12
12
|
* Float32Array. Handles dimension mismatches gracefully (uses the shorter
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* - memory/intelligence.ts (ReasoningBank.cosineSim)
|
|
6
6
|
* - mcp-tools/neural-tools.ts (cosineSimilarity)
|
|
7
7
|
* - mcp-tools/embeddings-tools.ts (cosineSimilarity)
|
|
8
|
-
* - mcp-tools/coherence/types.ts (
|
|
9
|
-
*
|
|
8
|
+
* - mcp-tools/coherence/types.ts + coherence-check.ts (both since deleted
|
|
9
|
+
* along with the rest of the uncalled coherence_* family)
|
|
10
10
|
*
|
|
11
11
|
* This is the single canonical implementation. Accepts number[] or
|
|
12
12
|
* Float32Array. Handles dimension mismatches gracefully (uses the shorter
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monoes/monomindcli",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CLI engine for Monomind — an open-source MCP server that extends Claude Code with a codebase knowledge graph (tree-sitter + SQLite), persistent memory, multi-agent task coordination, and session hooks. MIT licensed, fully local.",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Causal Graph Analysis Tool - pr_causal_infer
|
|
3
|
-
*
|
|
4
|
-
* Analyzes causal graph structure to assess identifiability.
|
|
5
|
-
* Identifies confounders, finds backdoor paths, and determines
|
|
6
|
-
* whether a causal effect can be identified from the graph.
|
|
7
|
-
* Does NOT estimate causal effect magnitude (that requires data).
|
|
8
|
-
*/
|
|
9
|
-
import type { MCPTool } from './types.js';
|
|
10
|
-
/**
|
|
11
|
-
* pr_causal_infer MCP Tool Definition
|
|
12
|
-
*/
|
|
13
|
-
export declare const causalInferTool: MCPTool;
|
|
14
|
-
export default causalInferTool;
|
|
15
|
-
//# sourceMappingURL=causal-infer.d.ts.map
|
|
@@ -1,333 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Causal Graph Analysis Tool - pr_causal_infer
|
|
3
|
-
*
|
|
4
|
-
* Analyzes causal graph structure to assess identifiability.
|
|
5
|
-
* Identifies confounders, finds backdoor paths, and determines
|
|
6
|
-
* whether a causal effect can be identified from the graph.
|
|
7
|
-
* Does NOT estimate causal effect magnitude (that requires data).
|
|
8
|
-
*/
|
|
9
|
-
import { CausalInputSchema, successResult, errorResult, } from './types.js';
|
|
10
|
-
// Default logger
|
|
11
|
-
const defaultLogger = {
|
|
12
|
-
debug: (msg, meta) => console.debug(`[pr_causal_infer] ${msg}`, meta),
|
|
13
|
-
info: (msg, meta) => console.info(`[pr_causal_infer] ${msg}`, meta),
|
|
14
|
-
warn: (msg, meta) => console.warn(`[pr_causal_infer] ${msg}`, meta),
|
|
15
|
-
error: (msg, meta) => console.error(`[pr_causal_infer] ${msg}`, meta),
|
|
16
|
-
};
|
|
17
|
-
function buildGraphStructure(graph) {
|
|
18
|
-
const nodes = new Map();
|
|
19
|
-
// Initialize nodes
|
|
20
|
-
for (const nodeId of graph.nodes) {
|
|
21
|
-
nodes.set(nodeId, {
|
|
22
|
-
id: nodeId,
|
|
23
|
-
parents: new Set(),
|
|
24
|
-
children: new Set(),
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
// Add edges
|
|
28
|
-
for (const [from, to] of graph.edges) {
|
|
29
|
-
const fromNode = nodes.get(from);
|
|
30
|
-
const toNode = nodes.get(to);
|
|
31
|
-
if (fromNode && toNode) {
|
|
32
|
-
fromNode.children.add(to);
|
|
33
|
-
toNode.parents.add(from);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return nodes;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Find all ancestors of a node (including the node itself)
|
|
40
|
-
*/
|
|
41
|
-
function findAncestors(nodeId, graph) {
|
|
42
|
-
const ancestors = new Set();
|
|
43
|
-
const queue = [nodeId];
|
|
44
|
-
while (queue.length > 0) {
|
|
45
|
-
const current = queue.shift();
|
|
46
|
-
if (ancestors.has(current))
|
|
47
|
-
continue;
|
|
48
|
-
ancestors.add(current);
|
|
49
|
-
const node = graph.get(current);
|
|
50
|
-
if (node) {
|
|
51
|
-
for (const parent of node.parents) {
|
|
52
|
-
queue.push(parent);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return ancestors;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Find all descendants of a node (including the node itself)
|
|
60
|
-
*/
|
|
61
|
-
function findDescendants(nodeId, graph) {
|
|
62
|
-
const descendants = new Set();
|
|
63
|
-
const queue = [nodeId];
|
|
64
|
-
while (queue.length > 0) {
|
|
65
|
-
const current = queue.shift();
|
|
66
|
-
if (descendants.has(current))
|
|
67
|
-
continue;
|
|
68
|
-
descendants.add(current);
|
|
69
|
-
const node = graph.get(current);
|
|
70
|
-
if (node) {
|
|
71
|
-
for (const child of node.children) {
|
|
72
|
-
queue.push(child);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return descendants;
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Identify confounders (common causes of treatment and outcome)
|
|
80
|
-
*/
|
|
81
|
-
function identifyConfounders(treatment, outcome, graph) {
|
|
82
|
-
const treatmentAncestors = findAncestors(treatment, graph);
|
|
83
|
-
const outcomeAncestors = findAncestors(outcome, graph);
|
|
84
|
-
// Confounders are ancestors of both treatment and outcome
|
|
85
|
-
// that are not the treatment itself
|
|
86
|
-
const confounders = [];
|
|
87
|
-
for (const ancestor of treatmentAncestors) {
|
|
88
|
-
if (ancestor !== treatment && ancestor !== outcome && outcomeAncestors.has(ancestor)) {
|
|
89
|
-
confounders.push(ancestor);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return confounders;
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Find backdoor paths from treatment to outcome
|
|
96
|
-
* A backdoor path is a path that goes into the treatment node
|
|
97
|
-
*/
|
|
98
|
-
function findBackdoorPaths(treatment, outcome, graph, maxPaths = 10) {
|
|
99
|
-
const backdoorPaths = [];
|
|
100
|
-
const treatmentNode = graph.get(treatment);
|
|
101
|
-
if (!treatmentNode)
|
|
102
|
-
return [];
|
|
103
|
-
// Start from each parent of treatment (backdoor)
|
|
104
|
-
for (const parent of treatmentNode.parents) {
|
|
105
|
-
const paths = findAllPaths(parent, outcome, graph, new Set([treatment]), maxPaths - backdoorPaths.length);
|
|
106
|
-
for (const path of paths) {
|
|
107
|
-
backdoorPaths.push([treatment, ...path]);
|
|
108
|
-
if (backdoorPaths.length >= maxPaths)
|
|
109
|
-
break;
|
|
110
|
-
}
|
|
111
|
-
if (backdoorPaths.length >= maxPaths)
|
|
112
|
-
break;
|
|
113
|
-
}
|
|
114
|
-
return backdoorPaths;
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Find all paths from source to target (BFS with path tracking)
|
|
118
|
-
*/
|
|
119
|
-
function findAllPaths(source, target, graph, exclude, maxPaths) {
|
|
120
|
-
const paths = [];
|
|
121
|
-
const queue = [{ node: source, path: [source] }];
|
|
122
|
-
while (queue.length > 0 && paths.length < maxPaths) {
|
|
123
|
-
const { node, path } = queue.shift();
|
|
124
|
-
if (node === target) {
|
|
125
|
-
paths.push(path);
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
const currentNode = graph.get(node);
|
|
129
|
-
if (!currentNode)
|
|
130
|
-
continue;
|
|
131
|
-
// Consider both parents and children for undirected path finding
|
|
132
|
-
const neighbors = new Set([...currentNode.parents, ...currentNode.children]);
|
|
133
|
-
for (const neighbor of neighbors) {
|
|
134
|
-
if (!path.includes(neighbor) && !exclude.has(neighbor)) {
|
|
135
|
-
queue.push({
|
|
136
|
-
node: neighbor,
|
|
137
|
-
path: [...path, neighbor],
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return paths;
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* Check if intervention is valid (treatment must exist and have outgoing edges)
|
|
146
|
-
*/
|
|
147
|
-
function validateIntervention(treatment, outcome, graph) {
|
|
148
|
-
const treatmentNode = graph.get(treatment);
|
|
149
|
-
const outcomeNode = graph.get(outcome);
|
|
150
|
-
if (!treatmentNode || !outcomeNode) {
|
|
151
|
-
return false;
|
|
152
|
-
}
|
|
153
|
-
// Check if there's any path from treatment to outcome
|
|
154
|
-
const descendants = findDescendants(treatment, graph);
|
|
155
|
-
return descendants.has(outcome);
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Assess identifiability of the causal effect based on graph structure.
|
|
159
|
-
* Does NOT estimate the causal effect magnitude -- that requires
|
|
160
|
-
* observational or experimental data which this tool does not have.
|
|
161
|
-
* Returns an identifiability score reflecting how cleanly the effect
|
|
162
|
-
* can be estimated if data were available (fewer confounders and
|
|
163
|
-
* unblocked backdoor paths = higher identifiability).
|
|
164
|
-
*/
|
|
165
|
-
function assessIdentifiability(confounders, backdoorPaths) {
|
|
166
|
-
// Identifiability decreases with confounders and unblocked backdoor paths
|
|
167
|
-
const confounderPenalty = Math.min(confounders.length * 0.1, 0.3);
|
|
168
|
-
const pathPenalty = Math.min(backdoorPaths.length * 0.05, 0.2);
|
|
169
|
-
const identifiability = Math.max(0.1, 1 - confounderPenalty - pathPenalty);
|
|
170
|
-
return { identifiability };
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* Get interpretation of causal graph analysis results
|
|
174
|
-
*/
|
|
175
|
-
function getInterpretation(interventionValid, confounders, backdoorPaths, identifiability) {
|
|
176
|
-
if (!interventionValid) {
|
|
177
|
-
return 'Intervention is not valid - no causal path exists from treatment to outcome';
|
|
178
|
-
}
|
|
179
|
-
if (confounders.length === 0 && backdoorPaths.length === 0) {
|
|
180
|
-
return 'Clean causal identification - no confounding detected. Effect is identifiable with data.';
|
|
181
|
-
}
|
|
182
|
-
if (identifiability > 0.8) {
|
|
183
|
-
return 'Strong identifiability - confounders can be adjusted for with observational data';
|
|
184
|
-
}
|
|
185
|
-
if (identifiability > 0.5) {
|
|
186
|
-
return `Moderate identifiability - ${confounders.length} confounder(s) require adjustment`;
|
|
187
|
-
}
|
|
188
|
-
return `Weak identifiability - multiple confounders (${confounders.length}) and backdoor paths (${backdoorPaths.length}) detected`;
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* Handler for pr_causal_infer tool
|
|
192
|
-
*/
|
|
193
|
-
async function handler(input, context) {
|
|
194
|
-
const logger = context?.logger ?? defaultLogger;
|
|
195
|
-
const startTime = performance.now();
|
|
196
|
-
try {
|
|
197
|
-
// Validate input
|
|
198
|
-
const validationResult = CausalInputSchema.safeParse(input);
|
|
199
|
-
if (!validationResult.success) {
|
|
200
|
-
logger.error('Input validation failed', { error: validationResult.error.message });
|
|
201
|
-
return errorResult(`Invalid input: ${validationResult.error.message}`);
|
|
202
|
-
}
|
|
203
|
-
const { graph, intervention, outcome } = validationResult.data;
|
|
204
|
-
logger.debug('Processing causal inference', {
|
|
205
|
-
nodes: graph.nodes.length,
|
|
206
|
-
edges: graph.edges.length,
|
|
207
|
-
intervention,
|
|
208
|
-
outcome,
|
|
209
|
-
});
|
|
210
|
-
// Validate nodes exist
|
|
211
|
-
if (!graph.nodes.includes(intervention)) {
|
|
212
|
-
return errorResult(`Intervention variable "${intervention}" not found in graph nodes`);
|
|
213
|
-
}
|
|
214
|
-
if (!graph.nodes.includes(outcome)) {
|
|
215
|
-
return errorResult(`Outcome variable "${outcome}" not found in graph nodes`);
|
|
216
|
-
}
|
|
217
|
-
const maxBackdoorPaths = context?.config?.causal?.maxBackdoorPaths ?? 10;
|
|
218
|
-
let identifiability;
|
|
219
|
-
let confounders;
|
|
220
|
-
let backdoorPaths;
|
|
221
|
-
let interventionValid;
|
|
222
|
-
// Try to use WASM bridge if available
|
|
223
|
-
if (context?.bridge?.initialized) {
|
|
224
|
-
try {
|
|
225
|
-
logger.debug('Using WASM bridge for causal inference');
|
|
226
|
-
const result = await context.bridge.inferCausal(graph, intervention, outcome);
|
|
227
|
-
confounders = result.confounders;
|
|
228
|
-
backdoorPaths = result.backdoorPaths;
|
|
229
|
-
interventionValid = result.interventionValid;
|
|
230
|
-
identifiability = assessIdentifiability(confounders, backdoorPaths).identifiability;
|
|
231
|
-
}
|
|
232
|
-
catch (wasmError) {
|
|
233
|
-
logger.warn('WASM bridge failed, falling back to JS implementation', {
|
|
234
|
-
error: wasmError instanceof Error ? wasmError.message : String(wasmError),
|
|
235
|
-
});
|
|
236
|
-
// Use JavaScript implementation
|
|
237
|
-
const graphStructure = buildGraphStructure(graph);
|
|
238
|
-
interventionValid = validateIntervention(intervention, outcome, graphStructure);
|
|
239
|
-
confounders = identifyConfounders(intervention, outcome, graphStructure);
|
|
240
|
-
backdoorPaths = findBackdoorPaths(intervention, outcome, graphStructure, maxBackdoorPaths);
|
|
241
|
-
identifiability = assessIdentifiability(confounders, backdoorPaths).identifiability;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
else {
|
|
245
|
-
// Pure JavaScript fallback
|
|
246
|
-
logger.debug('Using JavaScript fallback for causal inference');
|
|
247
|
-
const graphStructure = buildGraphStructure(graph);
|
|
248
|
-
interventionValid = validateIntervention(intervention, outcome, graphStructure);
|
|
249
|
-
confounders = identifyConfounders(intervention, outcome, graphStructure);
|
|
250
|
-
backdoorPaths = findBackdoorPaths(intervention, outcome, graphStructure, maxBackdoorPaths);
|
|
251
|
-
identifiability = assessIdentifiability(confounders, backdoorPaths).identifiability;
|
|
252
|
-
}
|
|
253
|
-
const output = {
|
|
254
|
-
identifiability,
|
|
255
|
-
backdoorPaths: backdoorPaths.map(p => p.join(' -> ')),
|
|
256
|
-
details: {
|
|
257
|
-
confounders,
|
|
258
|
-
interventionValid,
|
|
259
|
-
interpretation: getInterpretation(interventionValid, confounders, backdoorPaths, identifiability),
|
|
260
|
-
nodeCount: graph.nodes.length,
|
|
261
|
-
edgeCount: graph.edges.length,
|
|
262
|
-
},
|
|
263
|
-
};
|
|
264
|
-
const duration = performance.now() - startTime;
|
|
265
|
-
logger.info('Causal graph analysis completed', {
|
|
266
|
-
identifiability: identifiability.toFixed(4),
|
|
267
|
-
confounders: confounders.length,
|
|
268
|
-
backdoorPaths: backdoorPaths.length,
|
|
269
|
-
interventionValid,
|
|
270
|
-
durationMs: duration.toFixed(2),
|
|
271
|
-
});
|
|
272
|
-
return successResult(output);
|
|
273
|
-
}
|
|
274
|
-
catch (error) {
|
|
275
|
-
const duration = performance.now() - startTime;
|
|
276
|
-
logger.error('Causal inference failed', {
|
|
277
|
-
error: error instanceof Error ? error.message : String(error),
|
|
278
|
-
durationMs: duration.toFixed(2),
|
|
279
|
-
});
|
|
280
|
-
return errorResult(error instanceof Error ? error : new Error(String(error)));
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
/**
|
|
284
|
-
* pr_causal_infer MCP Tool Definition
|
|
285
|
-
*/
|
|
286
|
-
export const causalInferTool = {
|
|
287
|
-
name: 'pr_causal_infer',
|
|
288
|
-
description: 'Analyze causal graph structure for identifiability. Identifies confounders, finds backdoor paths, and assesses whether a causal effect can be identified. Does not estimate effect magnitude (requires observational data).',
|
|
289
|
-
category: 'causal',
|
|
290
|
-
version: '0.1.3',
|
|
291
|
-
tags: ['causal', 'do-calculus', 'confounders', 'backdoor-paths', 'ai-interpretability'],
|
|
292
|
-
cacheable: true,
|
|
293
|
-
cacheTTL: 60000,
|
|
294
|
-
inputSchema: {
|
|
295
|
-
type: 'object',
|
|
296
|
-
properties: {
|
|
297
|
-
graph: {
|
|
298
|
-
type: 'object',
|
|
299
|
-
properties: {
|
|
300
|
-
nodes: {
|
|
301
|
-
type: 'array',
|
|
302
|
-
items: { type: 'string' },
|
|
303
|
-
description: 'List of variable names',
|
|
304
|
-
},
|
|
305
|
-
edges: {
|
|
306
|
-
type: 'array',
|
|
307
|
-
items: {
|
|
308
|
-
type: 'array',
|
|
309
|
-
items: { type: 'string' },
|
|
310
|
-
minItems: 2,
|
|
311
|
-
maxItems: 2,
|
|
312
|
-
},
|
|
313
|
-
description: 'Directed edges as [from, to] pairs',
|
|
314
|
-
},
|
|
315
|
-
},
|
|
316
|
-
required: ['nodes', 'edges'],
|
|
317
|
-
description: 'Causal graph with nodes and directed edges',
|
|
318
|
-
},
|
|
319
|
-
intervention: {
|
|
320
|
-
type: 'string',
|
|
321
|
-
description: 'Treatment/intervention variable',
|
|
322
|
-
},
|
|
323
|
-
outcome: {
|
|
324
|
-
type: 'string',
|
|
325
|
-
description: 'Outcome variable to measure effect on',
|
|
326
|
-
},
|
|
327
|
-
},
|
|
328
|
-
required: ['graph', 'intervention', 'outcome'],
|
|
329
|
-
},
|
|
330
|
-
handler,
|
|
331
|
-
};
|
|
332
|
-
export default causalInferTool;
|
|
333
|
-
//# sourceMappingURL=causal-infer.js.map
|