@monoes/monomindcli 2.7.7 → 2.7.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/.claude/agents/core/coder.md +9 -0
- package/.claude/agents/core/coordinator.md +62 -0
- package/.claude/agents/core/planner.md +9 -0
- package/.claude/agents/core/reviewer.md +9 -0
- package/.claude/agents/core/tester.md +8 -0
- package/.claude/helpers/handlers/gates-handler.cjs +180 -47
- package/.claude/helpers/handlers/route-handler.cjs +32 -3
- package/.claude/helpers/hook-handler.cjs +55 -3
- package/.claude/helpers/intelligence.cjs +3 -1
- package/.claude/helpers/statusline.cjs +27 -3
- package/.claude/helpers/utils/monograph.cjs +104 -18
- package/.claude/settings.json +1 -1
- package/dist/src/browser/dashboard/server.d.ts.map +1 -1
- package/dist/src/browser/dashboard/server.js +6 -1
- package/dist/src/browser/dashboard/server.js.map +1 -1
- package/dist/src/capabilities/index.d.ts +0 -1
- package/dist/src/capabilities/index.d.ts.map +1 -1
- package/dist/src/capabilities/index.js +8 -1
- package/dist/src/capabilities/index.js.map +1 -1
- package/dist/src/commands/agent-lifecycle.d.ts.map +1 -1
- package/dist/src/commands/agent-lifecycle.js +5 -1
- package/dist/src/commands/agent-lifecycle.js.map +1 -1
- package/dist/src/commands/doctor-project-checks.d.ts.map +1 -1
- package/dist/src/commands/doctor-project-checks.js +8 -1
- package/dist/src/commands/doctor-project-checks.js.map +1 -1
- package/dist/src/commands/guidance.d.ts.map +1 -1
- package/dist/src/commands/guidance.js +8 -2
- package/dist/src/commands/guidance.js.map +1 -1
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +18 -4
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/memory-crud.d.ts.map +1 -1
- package/dist/src/commands/memory-crud.js +7 -1
- package/dist/src/commands/memory-crud.js.map +1 -1
- package/dist/src/commands/org-observe.d.ts.map +1 -1
- package/dist/src/commands/org-observe.js +17 -7
- package/dist/src/commands/org-observe.js.map +1 -1
- package/dist/src/commands/org.d.ts +11 -0
- package/dist/src/commands/org.d.ts.map +1 -1
- package/dist/src/commands/org.js +162 -15
- package/dist/src/commands/org.js.map +1 -1
- package/dist/src/commands/security-scan.d.ts +30 -1
- package/dist/src/commands/security-scan.d.ts.map +1 -1
- package/dist/src/commands/security-scan.js +182 -69
- package/dist/src/commands/security-scan.js.map +1 -1
- package/dist/src/commands/swarm.d.ts.map +1 -1
- package/dist/src/commands/swarm.js +41 -14
- package/dist/src/commands/swarm.js.map +1 -1
- package/dist/src/consensus/audit-writer.d.ts.map +1 -1
- package/dist/src/consensus/audit-writer.js +11 -10
- package/dist/src/consensus/audit-writer.js.map +1 -1
- package/dist/src/init/executor.d.ts.map +1 -1
- package/dist/src/init/executor.js +138 -22
- package/dist/src/init/executor.js.map +1 -1
- package/dist/src/init/settings-generator.d.ts.map +1 -1
- package/dist/src/init/settings-generator.js +4 -1
- package/dist/src/init/settings-generator.js.map +1 -1
- package/dist/src/knowledge/document-pipeline.d.ts +17 -0
- package/dist/src/knowledge/document-pipeline.d.ts.map +1 -1
- package/dist/src/knowledge/document-pipeline.js +62 -3
- package/dist/src/knowledge/document-pipeline.js.map +1 -1
- package/dist/src/mcp-tools/embeddings-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/embeddings-tools.js +16 -8
- package/dist/src/mcp-tools/embeddings-tools.js.map +1 -1
- package/dist/src/mcp-tools/knowledge-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/knowledge-tools.js +27 -13
- package/dist/src/mcp-tools/knowledge-tools.js.map +1 -1
- package/dist/src/memory/memory-bridge.d.ts +1 -1
- package/dist/src/memory/memory-bridge.d.ts.map +1 -1
- package/dist/src/memory/memory-bridge.js +5 -1
- package/dist/src/memory/memory-bridge.js.map +1 -1
- package/dist/src/memory/memory-read.d.ts +7 -2
- package/dist/src/memory/memory-read.d.ts.map +1 -1
- package/dist/src/memory/memory-read.js +10 -2
- package/dist/src/memory/memory-read.js.map +1 -1
- package/dist/src/monovector/diff-classifier.d.ts.map +1 -1
- package/dist/src/monovector/diff-classifier.js +25 -6
- package/dist/src/monovector/diff-classifier.js.map +1 -1
- package/dist/src/orgrt/daemon.d.ts +21 -1
- package/dist/src/orgrt/daemon.d.ts.map +1 -1
- package/dist/src/orgrt/daemon.js +82 -11
- package/dist/src/orgrt/daemon.js.map +1 -1
- package/dist/src/orgrt/inbox.d.ts.map +1 -1
- package/dist/src/orgrt/inbox.js +53 -20
- package/dist/src/orgrt/inbox.js.map +1 -1
- package/dist/src/parser.d.ts +4 -2
- package/dist/src/parser.d.ts.map +1 -1
- package/dist/src/parser.js +61 -25
- package/dist/src/parser.js.map +1 -1
- package/dist/src/services/config-file-manager.d.ts +10 -2
- package/dist/src/services/config-file-manager.d.ts.map +1 -1
- package/dist/src/services/config-file-manager.js +10 -2
- package/dist/src/services/config-file-manager.js.map +1 -1
- package/dist/src/ui/collector.mjs +43 -3
- package/dist/src/ui/dashboard.html +27 -8
- package/dist/src/ui/server.mjs +144 -14
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/dist/src/capabilities/watcher.d.ts +0 -18
- package/dist/src/capabilities/watcher.d.ts.map +0 -1
- package/dist/src/capabilities/watcher.js +0 -107
- package/dist/src/capabilities/watcher.js.map +0 -1
- package/dist/src/config-adapter.d.ts +0 -16
- package/dist/src/config-adapter.d.ts.map +0 -1
- package/dist/src/config-adapter.js +0 -220
- package/dist/src/config-adapter.js.map +0 -1
package/dist/src/ui/server.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import fs from 'fs';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import os from 'os';
|
|
5
5
|
import crypto from 'crypto';
|
|
6
|
+
import zlib from 'zlib';
|
|
6
7
|
import { fileURLToPath } from 'url';
|
|
7
8
|
import { createRequire } from 'module';
|
|
8
9
|
import { spawn } from 'child_process';
|
|
@@ -41,6 +42,16 @@ function _sjGetPricing(model) {
|
|
|
41
42
|
for (const k of Object.keys(_SJ_PRICING)) { if (canonical.startsWith(k) || canonical.includes(k)) return _SJ_PRICING[k]; }
|
|
42
43
|
return null;
|
|
43
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* True when this model has a pricing row (directly, by alias, or by prefix match).
|
|
47
|
+
* A model with no row cannot be costed — every caller that sums _sjCalcCost() must
|
|
48
|
+
* use this to mark the total as INCOMPLETE rather than presenting the missing
|
|
49
|
+
* contribution as a genuine $0.00. The table above is a hand-maintained snapshot,
|
|
50
|
+
* so newly released models land here routinely.
|
|
51
|
+
*/
|
|
52
|
+
export function _sjHasPricing(model) {
|
|
53
|
+
return _sjGetPricing(model) !== null;
|
|
54
|
+
}
|
|
44
55
|
function _sjCalcCost(model, usage) {
|
|
45
56
|
const p = _sjGetPricing(model);
|
|
46
57
|
if (!p || !usage) return 0;
|
|
@@ -586,6 +597,70 @@ function _updateRunState(event, rootDir) {
|
|
|
586
597
|
}
|
|
587
598
|
// ── End runstate helpers ─────────────────────────────────────────────────
|
|
588
599
|
|
|
600
|
+
// ── Security: DNS-rebinding defence ───────────────────────────────────────────
|
|
601
|
+
// The dashboard binds 127.0.0.1 and embeds a live auth token in the HTML served
|
|
602
|
+
// by GET / (an open route — the page needs the token before it can attach it to
|
|
603
|
+
// any fetch). Loopback binding is NOT a boundary against a browser: a page on
|
|
604
|
+
// attacker.example can point its own DNS at 127.0.0.1, and the browser will
|
|
605
|
+
// happily connect and treat the response as same-origin with attacker.example,
|
|
606
|
+
// letting the attacker's script read the token straight out of the <meta> tag
|
|
607
|
+
// and then drive every authenticated /api/* route.
|
|
608
|
+
//
|
|
609
|
+
// The distinguishing signal is the Host header: the browser sends the name from
|
|
610
|
+
// the URL bar, so a rebound request carries `Host: attacker.example` while the
|
|
611
|
+
// real dashboard carries a loopback name. Rejecting non-loopback Host values
|
|
612
|
+
// closes the hole without affecting any legitimate client. (CORS does not help
|
|
613
|
+
// here — the rebound page IS same-origin as far as the browser is concerned.)
|
|
614
|
+
const _LOOPBACK_HOSTNAMES = new Set(['localhost', '127.0.0.1', '[::1]', '::1']);
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Extract the hostname from a Host header, dropping the port.
|
|
618
|
+
* Returns '' for a missing/blank header. IPv6 literals keep their brackets
|
|
619
|
+
* (`[::1]:4242` → `[::1]`), which is how they appear in a Host header.
|
|
620
|
+
*/
|
|
621
|
+
export function parseHostHeader(hostHeader) {
|
|
622
|
+
const h = String(hostHeader ?? '').trim().toLowerCase();
|
|
623
|
+
if (!h) return '';
|
|
624
|
+
if (h.startsWith('[')) {
|
|
625
|
+
const end = h.indexOf(']');
|
|
626
|
+
return end === -1 ? h : h.slice(0, end + 1);
|
|
627
|
+
}
|
|
628
|
+
const colon = h.lastIndexOf(':');
|
|
629
|
+
return colon === -1 ? h : h.slice(0, colon);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* True when a request's Host header names this machine's loopback interface or
|
|
634
|
+
* an explicitly configured extra host.
|
|
635
|
+
*
|
|
636
|
+
* A request with NO Host header is allowed: HTTP/1.0 and raw local scripts omit
|
|
637
|
+
* it, and it is not an attack vector — browsers ALWAYS send Host, so a rebinding
|
|
638
|
+
* page cannot suppress it.
|
|
639
|
+
*
|
|
640
|
+
* `extraHosts` comes from startServer({ allowedHosts }) or the
|
|
641
|
+
* MONOMIND_DASHBOARD_ALLOWED_HOSTS env var (comma-separated), for the case where
|
|
642
|
+
* someone deliberately fronts the dashboard with another name.
|
|
643
|
+
*/
|
|
644
|
+
export function isAllowedHost(hostHeader, extraHosts = []) {
|
|
645
|
+
const name = parseHostHeader(hostHeader);
|
|
646
|
+
if (!name) return true; // absent Host — non-browser client, see above
|
|
647
|
+
if (_LOOPBACK_HOSTNAMES.has(name)) return true;
|
|
648
|
+
// 127.0.0.0/8 is entirely loopback, not just 127.0.0.1.
|
|
649
|
+
if (/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(name)) return true;
|
|
650
|
+
for (const extra of extraHosts) {
|
|
651
|
+
if (parseHostHeader(extra) === name) return true;
|
|
652
|
+
}
|
|
653
|
+
return false;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/** Parse the configured extra-host allow-list from an option + env var. */
|
|
657
|
+
export function resolveAllowedHosts(optionValue) {
|
|
658
|
+
const raw = []
|
|
659
|
+
.concat(Array.isArray(optionValue) ? optionValue : (optionValue ? [optionValue] : []))
|
|
660
|
+
.concat(String(process.env.MONOMIND_DASHBOARD_ALLOWED_HOSTS || '').split(','));
|
|
661
|
+
return raw.map((s) => String(s).trim().toLowerCase()).filter(Boolean);
|
|
662
|
+
}
|
|
663
|
+
|
|
589
664
|
// Server state
|
|
590
665
|
let running = false;
|
|
591
666
|
let currentPort = null;
|
|
@@ -707,7 +782,9 @@ function _resolveSlugToPathUncached(slug, projDir) {
|
|
|
707
782
|
return slug.replace(/-/g, '/');
|
|
708
783
|
}
|
|
709
784
|
|
|
710
|
-
export async function startServer({ port = 4242, projectDir, openBrowser = true } = {}) {
|
|
785
|
+
export async function startServer({ port = 4242, projectDir, openBrowser = true, allowedHosts } = {}) {
|
|
786
|
+
// Extra Host names accepted beyond loopback (see isAllowedHost above).
|
|
787
|
+
const _allowedHosts = resolveAllowedHosts(allowedHosts);
|
|
711
788
|
// ── Security: per-process auth credential for mutating (non-GET) requests ─
|
|
712
789
|
// Generated once per server start and written to a well-known location so
|
|
713
790
|
// trusted local callers (CLI, control-start.cjs) can read it and pass it
|
|
@@ -976,7 +1053,10 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
976
1053
|
const _coldPath = _wp.replace('.warm.jsonl', '.cold.jsonl.gz');
|
|
977
1054
|
if (fs.existsSync(_coldPath)) return; // already compacted
|
|
978
1055
|
const _warmData = fs.readFileSync(_wp);
|
|
979
|
-
|
|
1056
|
+
// `zlib` is a static ESM import at the top of this file. It used to be
|
|
1057
|
+
// `require('zlib')`, which is undefined in an ESM module — the
|
|
1058
|
+
// ReferenceError was swallowed by the surrounding catch, so cold-tier
|
|
1059
|
+
// compaction silently never ran. See tests/repo/no-cjs-require-in-esm.
|
|
980
1060
|
zlib.gzip(_warmData, (_err, _gz) => {
|
|
981
1061
|
if (_err) return;
|
|
982
1062
|
try {
|
|
@@ -1246,6 +1326,16 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1246
1326
|
const server = http.createServer(async (req, res) => {
|
|
1247
1327
|
const url = req.url.split('?')[0];
|
|
1248
1328
|
|
|
1329
|
+
// ── Security: DNS-rebinding defence (see isAllowedHost) ─────────────────
|
|
1330
|
+
// Must run before ANY route — including the open ones, since GET / is what
|
|
1331
|
+
// hands out the auth token. Fails closed with a bare 403 and no CORS header.
|
|
1332
|
+
if (!isAllowedHost(req.headers.host, _allowedHosts)) {
|
|
1333
|
+
res.writeHead(403, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
1334
|
+
res.end('Forbidden: Host header is not a loopback address. '
|
|
1335
|
+
+ 'Set MONOMIND_DASHBOARD_ALLOWED_HOSTS to permit another host name.\n');
|
|
1336
|
+
return;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1249
1339
|
// ── Security: strict CORS allow-list ────────────────────────────────────
|
|
1250
1340
|
// Only reflect Origin when it is this dashboard's own loopback origin.
|
|
1251
1341
|
// Otherwise the header is omitted entirely, so cross-origin reads fail
|
|
@@ -1436,6 +1526,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1436
1526
|
const id = f.replace('.jsonl', '');
|
|
1437
1527
|
let lastPrompt = '', summaries = [], totalDurationMs = 0, totalMessages = 0, firstTs = null, lastTs = null, totalCost = 0, toolCalls = 0, userMessages = 0, cacheReadTokens = 0, totalInputTokens = 0, errorCount = 0;
|
|
1438
1528
|
const modelBreakdown = {};
|
|
1529
|
+
const unknownPricingModels = new Set();
|
|
1439
1530
|
const filesTouchedSet = new Set();
|
|
1440
1531
|
try {
|
|
1441
1532
|
const lines = _readTailLines(fp);
|
|
@@ -1478,6 +1569,12 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1478
1569
|
if (!modelBreakdown[mk]) modelBreakdown[mk] = { calls: 0, cost: 0 };
|
|
1479
1570
|
modelBreakdown[mk].calls++;
|
|
1480
1571
|
modelBreakdown[mk].cost += c;
|
|
1572
|
+
// A model absent from the pricing table contributes 0 to the sum. Flag it
|
|
1573
|
+
// instead of letting the UI render an authoritative-looking $0.00.
|
|
1574
|
+
if (!_sjHasPricing(msg.model)) {
|
|
1575
|
+
modelBreakdown[mk].unknownPricing = true;
|
|
1576
|
+
unknownPricingModels.add(mk);
|
|
1577
|
+
}
|
|
1481
1578
|
cacheReadTokens += (msg.usage.cache_read_input_tokens || 0);
|
|
1482
1579
|
totalInputTokens += (msg.usage.input_tokens || 0)
|
|
1483
1580
|
+ (msg.usage.cache_creation_input_tokens || 0)
|
|
@@ -1493,7 +1590,9 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1493
1590
|
const filesTouched = [...filesTouchedSet].slice(0, 20);
|
|
1494
1591
|
const compactCount = summaries.length;
|
|
1495
1592
|
const summary = summaries.length ? summaries[summaries.length - 1].text : null;
|
|
1496
|
-
|
|
1593
|
+
// costIncomplete => totalCost is a LOWER BOUND: at least one model had no
|
|
1594
|
+
// pricing row, so its spend is missing from the sum entirely.
|
|
1595
|
+
sessions.push({ id, mtime, firstTs, lastTs, lastPrompt, summaries, summary, compactCount, errorCount, totalDurationMs, totalMessages, totalCost, costIncomplete: unknownPricingModels.size > 0, unknownPricingModels: [...unknownPricingModels], toolCalls, userMessages, cacheReadTokens, totalInputTokens, modelBreakdown, filesTouched, file: fp });
|
|
1497
1596
|
}
|
|
1498
1597
|
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
1499
1598
|
res.end(JSON.stringify({ sessions }));
|
|
@@ -1740,16 +1839,29 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1740
1839
|
try { sessionFiles = fs.readdirSync(projDir).filter(f => f.endsWith('.jsonl') && !f.startsWith('._')).map(f => path.join(projDir, f)); } catch {}
|
|
1741
1840
|
if (!sessionFiles.length) continue;
|
|
1742
1841
|
let totalCost = 0;
|
|
1842
|
+
const unknownPricingModels = new Set();
|
|
1743
1843
|
for (const fp of sessionFiles) {
|
|
1744
1844
|
try {
|
|
1745
1845
|
const lines = fs.readFileSync(fp, 'utf8').split('\n').filter(Boolean);
|
|
1746
1846
|
for (const line of lines) {
|
|
1747
1847
|
let e; try { e = JSON.parse(line); } catch { continue; }
|
|
1748
|
-
if (e.type === 'assistant' && e.message?.usage) {
|
|
1848
|
+
if (e.type === 'assistant' && e.message?.usage) {
|
|
1849
|
+
const _m = e.message.model || '';
|
|
1850
|
+
totalCost += _sjCalcCost(_m, e.message.usage);
|
|
1851
|
+
if (!_sjHasPricing(_m)) unknownPricingModels.add(_m.replace(/@.*$/, '').replace(/-\d{8}$/, ''));
|
|
1852
|
+
}
|
|
1749
1853
|
}
|
|
1750
1854
|
} catch {}
|
|
1751
1855
|
}
|
|
1752
|
-
|
|
1856
|
+
// Include projects whose entire spend is on unpriced models — previously they
|
|
1857
|
+
// were dropped by `totalCost > 0` and simply vanished from the cost list.
|
|
1858
|
+
if (totalCost > 0 || unknownPricingModels.size > 0) {
|
|
1859
|
+
projectCosts.push({
|
|
1860
|
+
path: projPath, cost: totalCost, sessions: sessionFiles.length,
|
|
1861
|
+
costIncomplete: unknownPricingModels.size > 0,
|
|
1862
|
+
unknownPricingModels: [...unknownPricingModels],
|
|
1863
|
+
});
|
|
1864
|
+
}
|
|
1753
1865
|
}
|
|
1754
1866
|
projectCosts.sort((a, b) => b.cost - a.cost);
|
|
1755
1867
|
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
@@ -3845,7 +3957,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3845
3957
|
|
|
3846
3958
|
const toolCounts = {};
|
|
3847
3959
|
const agentSpawns = {}; // subagent_type → count
|
|
3848
|
-
let turns = 0, totalCost = 0;
|
|
3960
|
+
let turns = 0, totalCost = 0, _costIncomplete = false;
|
|
3849
3961
|
|
|
3850
3962
|
try {
|
|
3851
3963
|
const raw = fs.readFileSync(fp, 'utf8').replace(/\r\n/g, '\n');
|
|
@@ -3868,7 +3980,11 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3868
3980
|
agentSpawns[sub] = (agentSpawns[sub] || 0) + 1;
|
|
3869
3981
|
}
|
|
3870
3982
|
}
|
|
3871
|
-
if (e.message?.usage)
|
|
3983
|
+
if (e.message?.usage) {
|
|
3984
|
+
const _m = e.message.model || '';
|
|
3985
|
+
totalCost += _sjCalcCost(_m, e.message.usage);
|
|
3986
|
+
if (!_sjHasPricing(_m)) _costIncomplete = true;
|
|
3987
|
+
}
|
|
3872
3988
|
}
|
|
3873
3989
|
}
|
|
3874
3990
|
} catch {}
|
|
@@ -3877,7 +3993,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3877
3993
|
nodes.push({
|
|
3878
3994
|
id: sid, type: 'session', label: sid.slice(0,8),
|
|
3879
3995
|
turns, totalTools, toolCounts,
|
|
3880
|
-
cost: totalCost, mtime: stat.mtimeMs, size: stat.size,
|
|
3996
|
+
cost: totalCost, costIncomplete: _costIncomplete, mtime: stat.mtimeMs, size: stat.size,
|
|
3881
3997
|
agentSpawns
|
|
3882
3998
|
});
|
|
3883
3999
|
|
|
@@ -6260,9 +6376,23 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
6260
6376
|
if (wantKg && scope !== 'global') {
|
|
6261
6377
|
try { kgSearch = (await import('../memory/memory-kg.js')).kgSearch; } catch { /* kg unavailable */ }
|
|
6262
6378
|
}
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6379
|
+
// Superseded-version filtering — the SAME rule `searchKnowledge` (and
|
|
6380
|
+
// therefore `monomind doc search` / `knowledge_search`) applies. This
|
|
6381
|
+
// endpoint queries the bridge directly for warmth, so without this it
|
|
6382
|
+
// would inject old document versions the Second Brain itself hides.
|
|
6383
|
+
let liveProj = new Set(), liveGlob = new Set(), isSuperseded = () => false, overfetch = (n) => n;
|
|
6384
|
+
try {
|
|
6385
|
+
const dp = await import('../knowledge/document-pipeline.js');
|
|
6386
|
+
liveProj = dp.liveContentHashes(process.env.CLAUDE_PROJECT_DIR || process.cwd());
|
|
6387
|
+
liveGlob = dp.liveContentHashes(process.env.MONOMIND_GLOBAL_BRAIN_DIR
|
|
6388
|
+
|| path.join(os.homedir(), '.monomind', 'global-brain'));
|
|
6389
|
+
isSuperseded = dp.isSupersededKey;
|
|
6390
|
+
overfetch = dp.supersededOverfetchLimit;
|
|
6391
|
+
} catch { /* pipeline unavailable — no filtering, same as before */ }
|
|
6392
|
+
const keepLive = (rows, live) => (rows || []).filter(r => !isSuperseded(String(r.key || ''), live)).slice(0, limit);
|
|
6393
|
+
const [projRaw, globRaw, rules, graph, mems] = await Promise.all([
|
|
6394
|
+
scope !== 'global' ? bridge.bridgeSearchEntries({ query, namespace, limit: overfetch(limit, liveProj) }).catch(() => null) : null,
|
|
6395
|
+
scope !== 'project' ? bridge.bridgeSearchEntries({ query, namespace: 'knowledge:global', limit: overfetch(limit, liveGlob), dbPath: '@global' }).catch(() => null) : null,
|
|
6266
6396
|
// Distilled rules ("when X do Y") learned from sessions/runs —
|
|
6267
6397
|
// small namespace, high injection value, threshold keeps it quiet.
|
|
6268
6398
|
scope !== 'global' && wantRules ? bridge.bridgeSearchEntries({ query, namespace: 'rules', limit: 2, threshold: 0.45 }).catch(() => null) : null,
|
|
@@ -6274,8 +6404,8 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
6274
6404
|
scope !== 'global' && wantMemory ? bridge.bridgeSearchEntries({ query, namespace: 'patterns', limit: 2, threshold: 0.4 }).catch(() => null) : null,
|
|
6275
6405
|
]);
|
|
6276
6406
|
const lists = [
|
|
6277
|
-
(
|
|
6278
|
-
(
|
|
6407
|
+
keepLive(projRaw?.results, liveProj).map(r => ({ id: r.id, key: r.key, content: String(r.content || '').slice(0, 2000), score: r.score + 0.05, global: false, tags: r.tags, importance: 0.6 })),
|
|
6408
|
+
keepLive(globRaw?.results, liveGlob).map(r => ({ id: r.id, key: r.key, content: String(r.content || '').slice(0, 2000), score: r.score, global: true, tags: r.tags })),
|
|
6279
6409
|
(rules?.results || []).map(r => ({ id: r.id, key: r.key, content: String(r.content || '').slice(0, 2000), score: r.score + 0.05, global: false, rule: true, tags: r.tags, importance: 0.7 })),
|
|
6280
6410
|
(graph?.triplets || []).map((t, i) => ({
|
|
6281
6411
|
id: 'kg:' + i + ':' + t.source + '|' + t.relation + '|' + t.target,
|
|
@@ -6303,7 +6433,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
6303
6433
|
} catch { /* best effort */ }
|
|
6304
6434
|
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
6305
6435
|
res.end(JSON.stringify({
|
|
6306
|
-
method:
|
|
6436
|
+
method: projRaw?.searchMethod || globRaw?.searchMethod || rules?.searchMethod || mems?.searchMethod
|
|
6307
6437
|
|| (merged.length ? 'mixed' : 'none'),
|
|
6308
6438
|
results: merged,
|
|
6309
6439
|
}));
|