@monoes/monomindcli 2.7.7 → 2.7.9

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.
Files changed (106) hide show
  1. package/.claude/agents/core/coder.md +9 -0
  2. package/.claude/agents/core/coordinator.md +62 -0
  3. package/.claude/agents/core/planner.md +9 -0
  4. package/.claude/agents/core/reviewer.md +9 -0
  5. package/.claude/agents/core/tester.md +8 -0
  6. package/.claude/helpers/handlers/gates-handler.cjs +180 -47
  7. package/.claude/helpers/handlers/route-handler.cjs +32 -3
  8. package/.claude/helpers/hook-handler.cjs +55 -3
  9. package/.claude/helpers/intelligence.cjs +3 -1
  10. package/.claude/helpers/statusline.cjs +27 -3
  11. package/.claude/helpers/utils/monograph.cjs +104 -18
  12. package/.claude/settings.json +1 -1
  13. package/dist/src/browser/dashboard/server.d.ts.map +1 -1
  14. package/dist/src/browser/dashboard/server.js +6 -1
  15. package/dist/src/browser/dashboard/server.js.map +1 -1
  16. package/dist/src/capabilities/index.d.ts +0 -1
  17. package/dist/src/capabilities/index.d.ts.map +1 -1
  18. package/dist/src/capabilities/index.js +8 -1
  19. package/dist/src/capabilities/index.js.map +1 -1
  20. package/dist/src/commands/agent-lifecycle.d.ts.map +1 -1
  21. package/dist/src/commands/agent-lifecycle.js +5 -1
  22. package/dist/src/commands/agent-lifecycle.js.map +1 -1
  23. package/dist/src/commands/doctor-project-checks.d.ts.map +1 -1
  24. package/dist/src/commands/doctor-project-checks.js +8 -1
  25. package/dist/src/commands/doctor-project-checks.js.map +1 -1
  26. package/dist/src/commands/guidance.d.ts.map +1 -1
  27. package/dist/src/commands/guidance.js +8 -2
  28. package/dist/src/commands/guidance.js.map +1 -1
  29. package/dist/src/commands/init.d.ts.map +1 -1
  30. package/dist/src/commands/init.js +18 -4
  31. package/dist/src/commands/init.js.map +1 -1
  32. package/dist/src/commands/memory-crud.d.ts.map +1 -1
  33. package/dist/src/commands/memory-crud.js +7 -1
  34. package/dist/src/commands/memory-crud.js.map +1 -1
  35. package/dist/src/commands/org-observe.d.ts.map +1 -1
  36. package/dist/src/commands/org-observe.js +17 -7
  37. package/dist/src/commands/org-observe.js.map +1 -1
  38. package/dist/src/commands/org.d.ts +11 -0
  39. package/dist/src/commands/org.d.ts.map +1 -1
  40. package/dist/src/commands/org.js +162 -15
  41. package/dist/src/commands/org.js.map +1 -1
  42. package/dist/src/commands/security-scan.d.ts +30 -1
  43. package/dist/src/commands/security-scan.d.ts.map +1 -1
  44. package/dist/src/commands/security-scan.js +182 -69
  45. package/dist/src/commands/security-scan.js.map +1 -1
  46. package/dist/src/commands/swarm.d.ts.map +1 -1
  47. package/dist/src/commands/swarm.js +41 -14
  48. package/dist/src/commands/swarm.js.map +1 -1
  49. package/dist/src/consensus/audit-writer.d.ts.map +1 -1
  50. package/dist/src/consensus/audit-writer.js +11 -10
  51. package/dist/src/consensus/audit-writer.js.map +1 -1
  52. package/dist/src/init/executor.d.ts.map +1 -1
  53. package/dist/src/init/executor.js +138 -22
  54. package/dist/src/init/executor.js.map +1 -1
  55. package/dist/src/init/settings-generator.d.ts.map +1 -1
  56. package/dist/src/init/settings-generator.js +4 -1
  57. package/dist/src/init/settings-generator.js.map +1 -1
  58. package/dist/src/knowledge/document-pipeline.d.ts +17 -0
  59. package/dist/src/knowledge/document-pipeline.d.ts.map +1 -1
  60. package/dist/src/knowledge/document-pipeline.js +62 -3
  61. package/dist/src/knowledge/document-pipeline.js.map +1 -1
  62. package/dist/src/mcp-tools/embeddings-tools.d.ts.map +1 -1
  63. package/dist/src/mcp-tools/embeddings-tools.js +16 -8
  64. package/dist/src/mcp-tools/embeddings-tools.js.map +1 -1
  65. package/dist/src/mcp-tools/knowledge-tools.d.ts.map +1 -1
  66. package/dist/src/mcp-tools/knowledge-tools.js +27 -13
  67. package/dist/src/mcp-tools/knowledge-tools.js.map +1 -1
  68. package/dist/src/memory/memory-bridge.d.ts +1 -1
  69. package/dist/src/memory/memory-bridge.d.ts.map +1 -1
  70. package/dist/src/memory/memory-bridge.js +5 -1
  71. package/dist/src/memory/memory-bridge.js.map +1 -1
  72. package/dist/src/memory/memory-read.d.ts +7 -2
  73. package/dist/src/memory/memory-read.d.ts.map +1 -1
  74. package/dist/src/memory/memory-read.js +10 -2
  75. package/dist/src/memory/memory-read.js.map +1 -1
  76. package/dist/src/monovector/diff-classifier.d.ts.map +1 -1
  77. package/dist/src/monovector/diff-classifier.js +25 -6
  78. package/dist/src/monovector/diff-classifier.js.map +1 -1
  79. package/dist/src/orgrt/daemon.d.ts +21 -1
  80. package/dist/src/orgrt/daemon.d.ts.map +1 -1
  81. package/dist/src/orgrt/daemon.js +82 -11
  82. package/dist/src/orgrt/daemon.js.map +1 -1
  83. package/dist/src/orgrt/inbox.d.ts.map +1 -1
  84. package/dist/src/orgrt/inbox.js +53 -20
  85. package/dist/src/orgrt/inbox.js.map +1 -1
  86. package/dist/src/parser.d.ts +4 -2
  87. package/dist/src/parser.d.ts.map +1 -1
  88. package/dist/src/parser.js +61 -25
  89. package/dist/src/parser.js.map +1 -1
  90. package/dist/src/services/config-file-manager.d.ts +10 -2
  91. package/dist/src/services/config-file-manager.d.ts.map +1 -1
  92. package/dist/src/services/config-file-manager.js +10 -2
  93. package/dist/src/services/config-file-manager.js.map +1 -1
  94. package/dist/src/ui/collector.mjs +43 -3
  95. package/dist/src/ui/dashboard.html +27 -8
  96. package/dist/src/ui/server.mjs +144 -14
  97. package/dist/tsconfig.tsbuildinfo +1 -1
  98. package/package.json +4 -4
  99. package/dist/src/capabilities/watcher.d.ts +0 -18
  100. package/dist/src/capabilities/watcher.d.ts.map +0 -1
  101. package/dist/src/capabilities/watcher.js +0 -107
  102. package/dist/src/capabilities/watcher.js.map +0 -1
  103. package/dist/src/config-adapter.d.ts +0 -16
  104. package/dist/src/config-adapter.d.ts.map +0 -1
  105. package/dist/src/config-adapter.js +0 -220
  106. package/dist/src/config-adapter.js.map +0 -1
@@ -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
- const zlib = require('zlib');
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
- sessions.push({ id, mtime, firstTs, lastTs, lastPrompt, summaries, summary, compactCount, errorCount, totalDurationMs, totalMessages, totalCost, toolCalls, userMessages, cacheReadTokens, totalInputTokens, modelBreakdown, filesTouched, file: fp });
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) { totalCost += _sjCalcCost(e.message.model || '', 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
- if (totalCost > 0) projectCosts.push({ path: projPath, cost: totalCost, sessions: sessionFiles.length });
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) totalCost += _sjCalcCost(e.message.model || '', 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
- const [proj, glob, rules, graph, mems] = await Promise.all([
6264
- scope !== 'global' ? bridge.bridgeSearchEntries({ query, namespace, limit }).catch(() => null) : null,
6265
- scope !== 'project' ? bridge.bridgeSearchEntries({ query, namespace: 'knowledge:global', limit, dbPath: '@global' }).catch(() => null) : null,
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
- (proj?.results || []).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 })),
6278
- (glob?.results || []).map(r => ({ id: r.id, key: r.key, content: String(r.content || '').slice(0, 2000), score: r.score, global: true, tags: r.tags })),
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: proj?.searchMethod || glob?.searchMethod || rules?.searchMethod || mems?.searchMethod
6436
+ method: projRaw?.searchMethod || globRaw?.searchMethod || rules?.searchMethod || mems?.searchMethod
6307
6437
  || (merged.length ? 'mixed' : 'none'),
6308
6438
  results: merged,
6309
6439
  }));