monomind 2.5.4 → 2.5.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.
Files changed (71) hide show
  1. package/README.md +10 -3
  2. package/package.json +1 -1
  3. package/packages/@monomind/cli/.claude/commands/mastermind/topology.md +121 -0
  4. package/packages/@monomind/cli/.claude/commands/mastermind.md +121 -102
  5. package/packages/@monomind/cli/.claude/helpers/control-start.cjs +22 -2
  6. package/packages/@monomind/cli/.claude/helpers/control-stop.cjs +95 -0
  7. package/packages/@monomind/cli/.claude/helpers/graphify-freshen.cjs +6 -0
  8. package/packages/@monomind/cli/.claude/helpers/hook-handler.cjs +13 -5
  9. package/packages/@monomind/cli/.claude/helpers/utils/monograph.cjs +33 -10
  10. package/packages/@monomind/cli/.claude/helpers/utils/system-pressure.cjs +46 -0
  11. package/packages/@monomind/cli/.claude/settings.json +448 -0
  12. package/packages/@monomind/cli/README.md +10 -3
  13. package/packages/@monomind/cli/dist/src/commands/agent-lifecycle.js +1 -1
  14. package/packages/@monomind/cli/dist/src/commands/agent-ops.js +1 -1
  15. package/packages/@monomind/cli/dist/src/commands/cleanup.js +53 -1
  16. package/packages/@monomind/cli/dist/src/commands/config.js +1 -1
  17. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.js +1 -1
  18. package/packages/@monomind/cli/dist/src/commands/hooks.js +1 -1
  19. package/packages/@monomind/cli/dist/src/commands/index.js +4 -0
  20. package/packages/@monomind/cli/dist/src/commands/init-wizard.js +3 -3
  21. package/packages/@monomind/cli/dist/src/commands/init.js +2 -2
  22. package/packages/@monomind/cli/dist/src/commands/mcp.js +2 -2
  23. package/packages/@monomind/cli/dist/src/commands/memory-admin.js +8 -7
  24. package/packages/@monomind/cli/dist/src/commands/memory-list.js +2 -2
  25. package/packages/@monomind/cli/dist/src/commands/memory.d.ts +1 -1
  26. package/packages/@monomind/cli/dist/src/commands/memory.js +4 -4
  27. package/packages/@monomind/cli/dist/src/commands/neural-core.js +1 -1
  28. package/packages/@monomind/cli/dist/src/commands/performance.js +8 -9
  29. package/packages/@monomind/cli/dist/src/commands/status.js +2 -2
  30. package/packages/@monomind/cli/dist/src/commands/swarm.js +1 -1
  31. package/packages/@monomind/cli/dist/src/index.js +1 -1
  32. package/packages/@monomind/cli/dist/src/init/claudemd-generator.js +2 -2
  33. package/packages/@monomind/cli/dist/src/init/executor.js +13 -6
  34. package/packages/@monomind/cli/dist/src/init/helpers-generator.js +66 -3
  35. package/packages/@monomind/cli/dist/src/init/shared-instructions-generator.d.ts +1 -1
  36. package/packages/@monomind/cli/dist/src/init/shared-instructions-generator.js +1 -1
  37. package/packages/@monomind/cli/dist/src/init/statusline-generator.d.ts +1 -1
  38. package/packages/@monomind/cli/dist/src/init/statusline-generator.js +2 -2
  39. package/packages/@monomind/cli/dist/src/init/types.d.ts +1 -1
  40. package/packages/@monomind/cli/dist/src/knowledge/document-pipeline.d.ts +1 -1
  41. package/packages/@monomind/cli/dist/src/knowledge/document-pipeline.js +3 -3
  42. package/packages/@monomind/cli/dist/src/mcp-client.d.ts +13 -51
  43. package/packages/@monomind/cli/dist/src/mcp-client.js +79 -134
  44. package/packages/@monomind/cli/dist/src/mcp-server.js +4 -3
  45. package/packages/@monomind/cli/dist/src/mcp-tools/browser-tools.js +45 -19
  46. package/packages/@monomind/cli/dist/src/mcp-tools/guidance-tools.js +1 -1
  47. package/packages/@monomind/cli/dist/src/mcp-tools/hive-mind-tools.js +2 -2
  48. package/packages/@monomind/cli/dist/src/mcp-tools/hooks-intelligence.js +2 -2
  49. package/packages/@monomind/cli/dist/src/mcp-tools/knowledge-tools.js +1 -1
  50. package/packages/@monomind/cli/dist/src/mcp-tools/monograph-compat.js +2 -2
  51. package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +22 -9
  52. package/packages/@monomind/cli/dist/src/mcp-tools/neural-tools.d.ts +1 -1
  53. package/packages/@monomind/cli/dist/src/mcp-tools/neural-tools.js +3 -3
  54. package/packages/@monomind/cli/dist/src/mcp-tools/system-tools.js +1 -1
  55. package/packages/@monomind/cli/dist/src/memory/embedding-operations.js +2 -2
  56. package/packages/@monomind/cli/dist/src/memory/hnsw-operations.js +53 -26
  57. package/packages/@monomind/cli/dist/src/memory/intelligence.js +2 -2
  58. package/packages/@monomind/cli/dist/src/memory/memory-bridge.d.ts +7 -3
  59. package/packages/@monomind/cli/dist/src/memory/memory-bridge.js +24 -10
  60. package/packages/@monomind/cli/dist/src/memory/memory-crud.js +2 -2
  61. package/packages/@monomind/cli/dist/src/memory/memory-initializer.d.ts +2 -2
  62. package/packages/@monomind/cli/dist/src/memory/memory-initializer.js +6 -6
  63. package/packages/@monomind/cli/dist/src/memory/memory-read.js +4 -4
  64. package/packages/@monomind/cli/dist/src/orgrt/daemon.js +12 -6
  65. package/packages/@monomind/cli/dist/src/orgrt/mailbox.d.ts +1 -0
  66. package/packages/@monomind/cli/dist/src/orgrt/mailbox.js +4 -0
  67. package/packages/@monomind/cli/dist/src/orgrt/policy.js +1 -1
  68. package/packages/@monomind/cli/dist/src/orgrt/server.js +25 -0
  69. package/packages/@monomind/cli/dist/src/ui/dashboard.html +327 -611
  70. package/packages/@monomind/cli/dist/src/ui/server.mjs +165 -22
  71. package/packages/@monomind/cli/package.json +1 -1
@@ -168,10 +168,22 @@ const monographQueryTool = {
168
168
  const label = input.label;
169
169
  const rerank = input.rerank ?? true;
170
170
  const damping = input.damping ?? 0.5;
171
+ const zeroResultHint = /\s/.test(query) && !/[A-Z]/.test(query.replace(/\s+/g, '').slice(1))
172
+ ? ' Hint: monograph indexes identifiers and filenames — try camelCase/PascalCase (e.g. "AgentSpawn") or a filename instead of a phrase.'
173
+ : '';
174
+ // Lightweight staleness check — fire-and-forget background rebuild;
175
+ // append warning to results so the agent knows data may be outdated.
176
+ let stalenessNote = '';
177
+ const repoPath = getProjectCwd();
178
+ const staleness = await computeCommitsBehind(repoPath);
179
+ if (staleness && staleness.commitsBehind > 0) {
180
+ const triggered = triggerBackgroundBuildIfNeeded(repoPath, staleness.commitsBehind);
181
+ stalenessNote = `\n⚠ Index is ${staleness.commitsBehind} commit(s) behind HEAD${triggered ? ' — rebuild triggered' : ''}.`;
182
+ }
171
183
  if (process.env['MONOGRAPH_EMBEDDINGS'] === 'true') {
172
184
  const results = await hybridQuery(db, query, { limit: rerank ? limit * 2 : limit, label });
173
185
  if (results.length === 0)
174
- return text('No results found.');
186
+ return text('No results found.' + zeroResultHint + stalenessNote);
175
187
  if (rerank) {
176
188
  const seeds = results.map(r => ({
177
189
  id: r.id, name: r.name ?? r.id, label: r.label ?? '?',
@@ -184,17 +196,17 @@ const monographQueryTool = {
184
196
  const tag = r.boostedByNeighbors ? ' [PPR-boosted]' : '';
185
197
  return `[${r.label}] ${r.name} ${loc} (score: ${r.score.toFixed(4)})${tag}`;
186
198
  });
187
- return text(lines.join('\n'));
199
+ return text(lines.join('\n') + stalenessNote);
188
200
  }
189
201
  const lines = results.map(r => {
190
202
  const loc = r.filePath ? (r.startLine != null ? `${r.filePath}:${r.startLine}` : r.filePath) : '';
191
203
  return `[${r.label ?? '?'}] ${r.name ?? r.id} ${loc} (score: ${r.score.toFixed(4)})`;
192
204
  });
193
- return text(lines.join('\n'));
205
+ return text(lines.join('\n') + stalenessNote);
194
206
  }
195
207
  const results = ftsSearch(db, query, rerank ? limit * 2 : limit, label);
196
208
  if (results.length === 0)
197
- return text('No results found.');
209
+ return text('No results found.' + zeroResultHint + stalenessNote);
198
210
  if (rerank) {
199
211
  const seeds = results.map(r => ({
200
212
  id: r.id, name: r.name, label: r.label,
@@ -207,13 +219,13 @@ const monographQueryTool = {
207
219
  const tag = r.boostedByNeighbors ? ' [PPR-boosted]' : '';
208
220
  return `[${r.label}] ${r.name} ${loc} (score: ${r.score.toFixed(3)})${tag}`;
209
221
  });
210
- return text(lines.join('\n'));
222
+ return text(lines.join('\n') + stalenessNote);
211
223
  }
212
224
  const lines = results.map(r => {
213
225
  const loc = r.filePath ? (r.startLine != null ? `${r.filePath}:${r.startLine}` : r.filePath) : '';
214
226
  return `[${r.label}] ${r.name} ${loc} (score: ${r.rank.toFixed(3)})`;
215
227
  });
216
- return text(lines.join('\n'));
228
+ return text(lines.join('\n') + stalenessNote);
217
229
  }
218
230
  finally {
219
231
  closeDb(db);
@@ -762,10 +774,11 @@ async function computeCommitsBehind(repoPath) {
762
774
  * Shared staleness threshold: both monograph_staleness and monograph_suggest (checkStaleness)
763
775
  * trigger a background rebuild only when the index is more than this many commits behind HEAD.
764
776
  * Using a shared constant prevents conflicting rebuild pressure during active dev sessions.
765
- * Was 10 (11-commit trigger) loose enough that a "2 commits behind" graph (the common
766
- * case session banners actually report) never rebuilt and silently served stale results.
777
+ * Was 10 3 1. Even 3 let routine small commits accumulate stale results
778
+ * silently. At 1 (rebuild triggers at 2+ behind), staleness rarely persists
779
+ * across sessions.
767
780
  */
768
- const STALENESS_THRESHOLD = 3;
781
+ const STALENESS_THRESHOLD = 1;
769
782
  /**
770
783
  * Fire-and-forget background rebuild. Uses a module-level guard so concurrent
771
784
  * MCP tool calls (e.g. repeated monograph_suggest checkStaleness) don't pile up builds.
@@ -5,7 +5,7 @@
5
5
  * No ML training, gradient descent, or neural network inference occurs.
6
6
  * The "train" tool embeds and stores; the "predict" tool finds similar stored
7
7
  * patterns. Embeddings come from the shared memory/embedding-operations.ts
8
- * pipeline (LanceDB bridge -> ONNX -> deterministic hash fallback).
8
+ * pipeline (SQLite-backed memory bridge -> ONNX -> deterministic hash fallback).
9
9
  *
10
10
  * All pattern storage is delegated to intelligence.ts's LocalReasoningBank
11
11
  * (single source of truth for patterns.json). No separate models.json store.
@@ -5,7 +5,7 @@
5
5
  * No ML training, gradient descent, or neural network inference occurs.
6
6
  * The "train" tool embeds and stores; the "predict" tool finds similar stored
7
7
  * patterns. Embeddings come from the shared memory/embedding-operations.ts
8
- * pipeline (LanceDB bridge -> ONNX -> deterministic hash fallback).
8
+ * pipeline (SQLite-backed memory bridge -> ONNX -> deterministic hash fallback).
9
9
  *
10
10
  * All pattern storage is delegated to intelligence.ts's LocalReasoningBank
11
11
  * (single source of truth for patterns.json). No separate models.json store.
@@ -16,11 +16,11 @@ const MAX_PATTERNS = 10000;
16
16
  const MAX_TRAINING_ENTRIES = 1000;
17
17
  const MAX_TEXT_LENGTH = 64 * 1024;
18
18
  const MAX_SEARCH_QUERY_LENGTH = 16 * 1024;
19
- // Embeddings: delegate to the shared embedding pipeline (LanceDB bridge -> ONNX ->
19
+ // Embeddings: delegate to the shared embedding pipeline (SQLite-backed memory bridge -> ONNX ->
20
20
  // deterministic hash fallback) in memory/embedding-operations.ts.
21
21
  let lastEmbeddingModel = 'unknown';
22
22
  /**
23
- * Generate embedding via the shared pipeline (LanceDB bridge -> ONNX -> deterministic
23
+ * Generate embedding via the shared pipeline (SQLite-backed memory bridge -> ONNX -> deterministic
24
24
  * hash), same one used by CLI `neural` commands and memory search. Falls back to a
25
25
  * local deterministic hash only if the shared module fails to load entirely.
26
26
  */
@@ -143,7 +143,7 @@ export const systemTools = [
143
143
  let agentCounts = { active: 0, total: 0 };
144
144
  let taskCounts = { pending: 0, completed: 0, failed: 0 };
145
145
  let _metricsSource = 'none';
146
- // Primary: LanceDB bridge
146
+ // Primary: SQLite-backed memory bridge
147
147
  try {
148
148
  const bridge = await import('../memory/memory-bridge.js');
149
149
  const agentResults = await bridge.bridgeListEntries({ namespace: 'agents', limit: 10000 });
@@ -38,7 +38,7 @@ export async function loadEmbeddingModel(options) {
38
38
  loadTime: 0
39
39
  };
40
40
  }
41
- // ADR-053: Try LanceDB bridge first
41
+ // ADR-053: Try SQLite-backed memory bridge first
42
42
  const bridge = await getBridge();
43
43
  if (bridge) {
44
44
  const bridgeResult = await bridge.bridgeLoadEmbeddingModel();
@@ -148,7 +148,7 @@ export async function generateEmbedding(text) {
148
148
  text = String(text ?? '');
149
149
  if (text.length > 16 * 1024)
150
150
  text = text.slice(0, 16 * 1024);
151
- // ADR-053: Try LanceDB bridge first
151
+ // ADR-053: Try SQLite-backed memory bridge first
152
152
  const bridge = await getBridge();
153
153
  if (bridge) {
154
154
  const bridgeResult = await bridge.bridgeGenerateEmbedding(text);
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import * as fs from 'fs';
8
8
  import * as path from 'path';
9
- // ADR-053: Lazy import of LanceDB memory bridge
9
+ // ADR-053: Lazy import of SQLite-backed memory bridge
10
10
  let _bridge;
11
11
  async function getBridge() {
12
12
  if (_bridge === null)
@@ -34,38 +34,65 @@ async function loadHNSWIndexClass() {
34
34
  return null;
35
35
  }
36
36
  }
37
- /** Read entries with parseable embeddings of the expected dimension from the sql.js DB. */
37
+ /** Read entries with parseable embeddings of the expected dimension from SQLite. */
38
38
  async function loadEntriesFromDb(dbPath, dimensions) {
39
39
  if (!fs.existsSync(dbPath))
40
40
  return [];
41
- const initSqlJs = (await import('sql.js')).default;
42
- const SQL = await initSqlJs();
43
- const fileBuffer = fs.readFileSync(dbPath);
44
- const db = new SQL.Database(fileBuffer);
41
+ const { safeParseEmbedding } = await import('./memory-bridge.js');
42
+ const results = [];
43
+ // Prefer better-sqlite3 (mmap-backed, doesn't load full DB into memory)
45
44
  try {
46
- const { safeParseEmbedding } = await import('./memory-bridge.js');
47
- const stmt = db.prepare(`SELECT id, key, namespace, content, embedding FROM memory_entries WHERE status = 'active' AND embedding IS NOT NULL LIMIT 50000`);
48
- const rows = [];
49
- while (stmt.step())
50
- rows.push(stmt.get());
51
- stmt.free();
52
- const results = [];
53
- for (const row of rows) {
54
- const [id, key, namespace, content, embeddingJson] = row;
55
- const parsed = safeParseEmbedding(embeddingJson);
56
- if (!parsed || parsed.length !== dimensions)
57
- continue;
58
- results.push({
59
- id,
60
- vector: new Float32Array(parsed),
61
- entry: { id, key: key || id, namespace: namespace || 'default', content: content || '' },
62
- });
45
+ const Database = (await import('better-sqlite3')).default;
46
+ const db = new Database(dbPath, { readonly: true });
47
+ try {
48
+ const rows = db.prepare(`SELECT id, key, namespace, content, embedding FROM memory_entries WHERE status = 'active' AND embedding IS NOT NULL ORDER BY rowid DESC LIMIT 10000`).all();
49
+ for (const row of rows) {
50
+ const parsed = safeParseEmbedding(row.embedding);
51
+ if (!parsed || parsed.length !== dimensions)
52
+ continue;
53
+ results.push({
54
+ id: row.id,
55
+ vector: new Float32Array(parsed),
56
+ entry: { id: row.id, key: row.key || row.id, namespace: row.namespace || 'default', content: row.content || '' },
57
+ });
58
+ }
59
+ }
60
+ finally {
61
+ db.close();
63
62
  }
64
63
  return results;
65
64
  }
66
- finally {
67
- db.close();
65
+ catch {
66
+ // better-sqlite3 native binding unavailable — fall back to sql.js (WASM)
67
+ }
68
+ try {
69
+ const initSqlJs = (await import('sql.js')).default;
70
+ const SQL = await initSqlJs();
71
+ const fileBuffer = fs.readFileSync(dbPath);
72
+ const db = new SQL.Database(fileBuffer);
73
+ try {
74
+ const stmt = db.prepare(`SELECT id, key, namespace, content, embedding FROM memory_entries WHERE status = 'active' AND embedding IS NOT NULL ORDER BY rowid DESC LIMIT 10000`);
75
+ while (stmt.step()) {
76
+ const [id, key, namespace, content, embeddingJson] = stmt.get();
77
+ const parsed = safeParseEmbedding(embeddingJson);
78
+ if (!parsed || parsed.length !== dimensions)
79
+ continue;
80
+ results.push({
81
+ id,
82
+ vector: new Float32Array(parsed),
83
+ entry: { id, key: key || id, namespace: namespace || 'default', content: content || '' },
84
+ });
85
+ }
86
+ stmt.free();
87
+ }
88
+ finally {
89
+ db.close();
90
+ }
91
+ }
92
+ catch {
93
+ // Neither SQLite driver available
68
94
  }
95
+ return results;
69
96
  }
70
97
  /**
71
98
  * Get or create the HNSW index singleton
@@ -204,7 +231,7 @@ export async function searchHNSWIndex(queryEmbedding, options) {
204
231
  export function getHNSWStatus() {
205
232
  // ADR-053: If bridge was previously loaded, report availability
206
233
  if (_bridge && _bridge !== null) {
207
- // Bridge is loaded — HNSW-equivalent is available via LanceDB
234
+ // Bridge is loaded — HNSW-equivalent is available via the SQLite backend
208
235
  return {
209
236
  available: true,
210
237
  initialized: true,
@@ -847,7 +847,7 @@ export async function recordStep(step) {
847
847
  }
848
848
  try {
849
849
  // Generate embedding if not provided
850
- // ADR-053: Try LanceDB bridge embedder first
850
+ // ADR-053: Try SQLite-backed memory bridge embedder first
851
851
  let embedding = step.embedding;
852
852
  if (!embedding) {
853
853
  try {
@@ -952,7 +952,7 @@ export async function findSimilarPatterns(query, options) {
952
952
  return [];
953
953
  }
954
954
  try {
955
- // ADR-053: Try LanceDB bridge embedder first
955
+ // ADR-053: Try SQLite-backed memory bridge embedder first
956
956
  let queryEmbedding = null;
957
957
  try {
958
958
  const bridge = await import('./memory-bridge.js');
@@ -1,7 +1,10 @@
1
1
  /**
2
- * Memory Bridge — Routes CLI memory operations through LanceDB
2
+ * Memory Bridge — Routes CLI memory operations through SQLite
3
3
  *
4
- * Uses LanceDBBackend from @monoes/memory.
4
+ * Uses SQLiteBackend (better-sqlite3, sql.js WASM fallback) from @monoes/memory.
5
+ * LanceDB was replaced by this SQLite engine 2026-07; the on-disk data
6
+ * directory is still named `lancedb` for legacy/back-compat path resolution
7
+ * (see getDbPath below) but no longer holds LanceDB data.
5
8
  * All exported function signatures are unchanged.
6
9
  *
7
10
  * @module v1/cli/memory-bridge
@@ -15,7 +18,8 @@ export declare function safeParseEmbedding(raw: string | null | undefined): numb
15
18
  export declare function getGlobalBrainDir(): string;
16
19
  /** Sentinel callers pass as dbPath to address the global brain. */
17
20
  export declare const GLOBAL_BRAIN = "@global";
18
- /** Resolve the real on-disk LanceDB path for a given custom path (or the default). */
21
+ /** Resolve the real on-disk SQLite data-dir path for a given custom path (or the
22
+ * default) — the dir is still named `lancedb` for legacy path back-compat. */
19
23
  export declare function bridgeGetDbPath(customPath?: string): string;
20
24
  export declare function bridgeStoreEntry(options: {
21
25
  key: string;
@@ -1,7 +1,10 @@
1
1
  /**
2
- * Memory Bridge — Routes CLI memory operations through LanceDB
2
+ * Memory Bridge — Routes CLI memory operations through SQLite
3
3
  *
4
- * Uses LanceDBBackend from @monoes/memory.
4
+ * Uses SQLiteBackend (better-sqlite3, sql.js WASM fallback) from @monoes/memory.
5
+ * LanceDB was replaced by this SQLite engine 2026-07; the on-disk data
6
+ * directory is still named `lancedb` for legacy/back-compat path resolution
7
+ * (see getDbPath below) but no longer holds LanceDB data.
5
8
  * All exported function signatures are unchanged.
6
9
  *
7
10
  * @module v1/cli/memory-bridge
@@ -103,7 +106,8 @@ function getDbPath(customPath) {
103
106
  return resolved;
104
107
  return defaultDir;
105
108
  }
106
- /** Resolve the real on-disk LanceDB path for a given custom path (or the default). */
109
+ /** Resolve the real on-disk SQLite data-dir path for a given custom path (or the
110
+ * default) — the dir is still named `lancedb` for legacy path back-compat. */
107
111
  export function bridgeGetDbPath(customPath) {
108
112
  return getDbPath(customPath);
109
113
  }
@@ -161,6 +165,7 @@ function generateId(prefix) {
161
165
  return `${prefix}_${Date.now()}_${crypto.randomBytes(8).toString('hex')}`;
162
166
  }
163
167
  const backendSlots = new Map();
168
+ const MAX_BACKEND_SLOTS = 5;
164
169
  let _embedder = null;
165
170
  let _embedderPromise = null;
166
171
  const MAX_INIT_ATTEMPTS = 3;
@@ -203,6 +208,15 @@ async function getBackend(dbPath) {
203
208
  const dir = getDbPath(dbPath);
204
209
  let slot = backendSlots.get(dir);
205
210
  if (!slot) {
211
+ if (backendSlots.size >= MAX_BACKEND_SLOTS) {
212
+ const oldest = backendSlots.keys().next().value;
213
+ const evicted = backendSlots.get(oldest);
214
+ try {
215
+ evicted?.instance?.close?.();
216
+ }
217
+ catch { /* best effort */ }
218
+ backendSlots.delete(oldest);
219
+ }
206
220
  slot = { promise: null, instance: null, available: null, attempts: 0 };
207
221
  backendSlots.set(dir, slot);
208
222
  }
@@ -608,7 +622,7 @@ export async function bridgeGetBackendStats(dbPath) {
608
622
  return null;
609
623
  }
610
624
  }
611
- // ===== HNSW (replaced by LanceDB ANN — stubs kept for API compat) =====
625
+ // ===== HNSW (replaced by the SQLite backend's ANN search — stubs kept for API compat) =====
612
626
  export async function bridgeGetHNSWStatus(dbPath) {
613
627
  const backend = await getBackend(dbPath);
614
628
  if (!backend)
@@ -622,7 +636,7 @@ export async function bridgeGetHNSWStatus(dbPath) {
622
636
  }
623
637
  }
624
638
  export async function bridgeSearchHNSW(options) {
625
- // Delegate to bridgeSearchEntries which uses LanceDB ANN
639
+ // Delegate to bridgeSearchEntries which uses the SQLite backend's ANN search
626
640
  const result = await bridgeSearchEntries({
627
641
  query: options.query,
628
642
  namespace: options.namespace,
@@ -639,7 +653,7 @@ export async function bridgeSearchHNSW(options) {
639
653
  };
640
654
  }
641
655
  export async function bridgeAddToHNSW(options) {
642
- // LanceDB indexes entries automatically on store — this is a no-op
656
+ // The SQLite backend indexes entries automatically on store — this is a no-op
643
657
  const backend = await getBackend(options.dbPath);
644
658
  if (!backend)
645
659
  return null;
@@ -651,7 +665,7 @@ export async function bridgeAddToHNSW(options) {
651
665
  return { success: true };
652
666
  }
653
667
  }
654
- // ===== Controller stubs (LanceDB has no equivalent controllers) =====
668
+ // ===== Controller stubs (the SQLite backend has no equivalent controllers) =====
655
669
  export async function bridgeGetController(controllerName, dbPath) {
656
670
  await getBackend(dbPath);
657
671
  return null;
@@ -932,13 +946,13 @@ export async function bridgeRouteTask(options) {
932
946
  export async function bridgeHealthCheck(dbPath) {
933
947
  const backend = await getBackend(dbPath);
934
948
  if (!backend)
935
- return { healthy: false, backend: 'lancedb', error: 'unavailable' };
949
+ return { healthy: false, backend: 'sqlite', error: 'unavailable' };
936
950
  try {
937
951
  const health = await backend.healthCheck?.();
938
952
  const stats = await backend.getStats?.();
939
953
  return {
940
954
  healthy: health?.healthy ?? true,
941
- backend: 'lancedb',
955
+ backend: 'sqlite',
942
956
  stats: stats ? {
943
957
  totalEntries: stats.totalEntries ?? 0,
944
958
  namespaces: Object.keys(stats.entriesByNamespace ?? {}),
@@ -946,7 +960,7 @@ export async function bridgeHealthCheck(dbPath) {
946
960
  };
947
961
  }
948
962
  catch {
949
- return { healthy: false, backend: 'lancedb' };
963
+ return { healthy: false, backend: 'sqlite' };
950
964
  }
951
965
  }
952
966
  // ===== Hierarchical memory =====
@@ -214,7 +214,7 @@ export async function verifyMemoryInit(dbPath, options) {
214
214
  * This bypasses MCP and writes directly to the database
215
215
  */
216
216
  export async function storeEntry(options) {
217
- // ADR-053: Try LanceDB memory bridge first
217
+ // ADR-053: Try SQLite-backed memory bridge first
218
218
  const bridge = await getBridge();
219
219
  if (bridge) {
220
220
  const bridgeResult = await bridge.bridgeStoreEntry(options);
@@ -335,7 +335,7 @@ export async function storeEntry(options) {
335
335
  * Issue #980: Properly supports namespaced entries
336
336
  */
337
337
  export async function deleteEntry(options) {
338
- // ADR-053: Try LanceDB memory bridge first
338
+ // ADR-053: Try SQLite-backed memory bridge first
339
339
  const bridge = await getBridge();
340
340
  if (bridge) {
341
341
  const bridgeResult = await bridge.bridgeDeleteEntry(options);
@@ -3,8 +3,8 @@
3
3
  * Properly initializes the memory database with sql.js (WASM SQLite)
4
4
  * Includes pattern tables, vector embeddings, migration state tracking
5
5
  *
6
- * ADR-053: Routes through ControllerRegistry → LanceDB when available,
7
- * falls back to raw sql.js for backwards compatibility.
6
+ * ADR-053: Routes through ControllerRegistry → SQLite-backed memory bridge
7
+ * when available, falls back to raw sql.js for backwards compatibility.
8
8
  *
9
9
  * @module v1/cli/memory-initializer
10
10
  */
@@ -3,8 +3,8 @@
3
3
  * Properly initializes the memory database with sql.js (WASM SQLite)
4
4
  * Includes pattern tables, vector embeddings, migration state tracking
5
5
  *
6
- * ADR-053: Routes through ControllerRegistry → LanceDB when available,
7
- * falls back to raw sql.js for backwards compatibility.
6
+ * ADR-053: Routes through ControllerRegistry → SQLite-backed memory bridge
7
+ * when available, falls back to raw sql.js for backwards compatibility.
8
8
  *
9
9
  * @module v1/cli/memory-initializer
10
10
  */
@@ -12,7 +12,7 @@ import * as fs from 'fs';
12
12
  import * as path from 'path';
13
13
  /** Maximum SQLite database file size accepted before read (256 MB). */
14
14
  const MAX_DB_FILE_BYTES = 256 * 1024 * 1024;
15
- // ADR-053: Lazy import of LanceDB memory bridge
15
+ // ADR-053: Lazy import of SQLite-backed memory bridge
16
16
  let _bridge;
17
17
  async function getBridge() {
18
18
  if (_bridge === null)
@@ -71,15 +71,15 @@ INSERT OR IGNORE INTO vector_indexes (id, name, dimensions) VALUES
71
71
  `;
72
72
  }
73
73
  /**
74
- * ADR-053: Activate ControllerRegistry so LanceDB controllers
74
+ * ADR-053: Activate ControllerRegistry so memory-backend controllers
75
75
  * (ReasoningBank, SkillLibrary, ExplainableRecall, etc.) are instantiated.
76
76
  *
77
77
  * Uses the memory-bridge's getControllerRegistry() which lazily creates
78
78
  * a singleton ControllerRegistry and initializes it with the given dbPath.
79
79
  * After this call, all enabled controllers are ready for immediate use.
80
80
  *
81
- * Failures are isolated: if @monomind/memory or LanceDB is not available,
82
- * this returns an empty result without throwing.
81
+ * Failures are isolated: if @monomind/memory or the SQLite backend is not
82
+ * available, this returns an empty result without throwing.
83
83
  */
84
84
  async function activateControllerRegistry(dbPath, verbose) {
85
85
  const startTime = performance.now();
@@ -14,7 +14,7 @@ import { searchHNSWIndex } from './hnsw-operations.js';
14
14
  import { cosineSimilarity as cosineSim } from '../utils/cosine-similarity.js';
15
15
  /** Maximum SQLite database file size accepted before read (256 MB). */
16
16
  const MAX_DB_FILE_BYTES = 256 * 1024 * 1024;
17
- // ADR-053: Lazy import of LanceDB memory bridge
17
+ // ADR-053: Lazy import of SQLite-backed memory bridge
18
18
  let _bridge;
19
19
  async function getBridge() {
20
20
  if (_bridge === null)
@@ -35,7 +35,7 @@ async function getBridge() {
35
35
  * Uses HNSW index for 150x faster search when available
36
36
  */
37
37
  export async function searchEntries(options) {
38
- // ADR-053: Try LanceDB memory bridge first
38
+ // ADR-053: Try SQLite-backed memory bridge first
39
39
  const bridge = await getBridge();
40
40
  if (bridge) {
41
41
  const bridgeResult = await bridge.bridgeSearchEntries(options);
@@ -143,7 +143,7 @@ export async function searchEntries(options) {
143
143
  * List all entries from the memory database
144
144
  */
145
145
  export async function listEntries(options) {
146
- // ADR-053: Try LanceDB memory bridge first
146
+ // ADR-053: Try SQLite-backed memory bridge first
147
147
  const bridge = await getBridge();
148
148
  if (bridge) {
149
149
  const bridgeResult = await bridge.bridgeListEntries(options);
@@ -241,7 +241,7 @@ export async function listEntries(options) {
241
241
  * Get a specific entry from the memory database
242
242
  */
243
243
  export async function getEntry(options) {
244
- // ADR-053: Try LanceDB memory bridge first
244
+ // ADR-053: Try SQLite-backed memory bridge first
245
245
  const bridge = await getBridge();
246
246
  if (bridge) {
247
247
  const bridgeResult = await bridge.bridgeGetEntry(options);
@@ -47,15 +47,17 @@ export class OrgDaemon {
47
47
  const cwd = join(this.root, ORG_DIR, name, 'workspace');
48
48
  mkdirSync(cwd, { recursive: true });
49
49
  const bus = new OrgBus(name, run, dir);
50
- // Bounded in-memory tail: bus.jsonl on disk is the full durable record;
51
- // this buffer only backs busEvents() (test-loop, /api/history) and would
52
- // otherwise grow without limit for a long-running scheduled org — each
53
- // Write's captured content snapshot alone can be up to 200KB (policy.ts).
54
- const MAX_COLLECTED = 5000;
50
+ // Lightweight in-memory tail for busEvents() (test-loop, /api/history).
51
+ // Full events (including Write content snapshots) live on disk in bus.jsonl;
52
+ // the in-memory copy strips bulky data.content to keep RAM flat.
53
+ const MAX_COLLECTED = 1000;
55
54
  const collected = [];
56
55
  let lastActivity = Date.now();
57
56
  bus.subscribe(e => {
58
- collected.push(e);
57
+ const slim = e.data?.content != null
58
+ ? { ...e, data: { ...e.data, content: undefined } }
59
+ : e;
60
+ collected.push(slim);
59
61
  if (collected.length > MAX_COLLECTED)
60
62
  collected.splice(0, collected.length - MAX_COLLECTED);
61
63
  // The watchdog's own nudge event must not count as org activity, or a
@@ -543,6 +545,10 @@ export class OrgDaemon {
543
545
  catch (err) {
544
546
  console.error(`org ${name}: could not write run history:`, err instanceof Error ? err.message : err);
545
547
  }
548
+ finally {
549
+ this.recallUsage.delete(name);
550
+ this.orgLearnedRuns.delete(`${name}:${org.run}`);
551
+ }
546
552
  // the "org stopped" event above triggers the forwarder's final org:complete /
547
553
  // session:complete POST — without waiting for it here, the CLI process can exit
548
554
  // (and kill the in-flight fetch) before that last event reaches the dashboard,
@@ -14,6 +14,7 @@ export interface OrgUserMessage {
14
14
  * stream() is passed as the `prompt` of query() to keep the session open.
15
15
  */
16
16
  export declare class Mailbox {
17
+ private static readonly MAX_QUEUE;
17
18
  private queue;
18
19
  private wake;
19
20
  private closed;
@@ -4,6 +4,7 @@
4
4
  * stream() is passed as the `prompt` of query() to keep the session open.
5
5
  */
6
6
  export class Mailbox {
7
+ static MAX_QUEUE = 500;
7
8
  queue = [];
8
9
  wake = null;
9
10
  closed = false;
@@ -12,6 +13,9 @@ export class Mailbox {
12
13
  push(text) {
13
14
  if (this.closed)
14
15
  return;
16
+ if (this.queue.length >= Mailbox.MAX_QUEUE) {
17
+ this.queue.shift();
18
+ }
15
19
  this.queue.push(text);
16
20
  this.wake?.();
17
21
  this.wake = null;
@@ -10,7 +10,7 @@ const READ_TOOLS = new Set(['Read', 'Glob', 'Grep']);
10
10
  const WEB_TOOLS = new Set(['WebFetch', 'WebSearch']);
11
11
  /** Cap for inline content snapshots on 'asset' events (bytes, UTF-16 chars) — keeps
12
12
  * bus.jsonl / the dashboard's per-session event log from bloating on large writes. */
13
- const SNAPSHOT_MAX_CHARS = 200_000;
13
+ const SNAPSHOT_MAX_CHARS = 20_000;
14
14
  const REGEX_METACHARS = new Set('.+^${}()|[]\\'.split(''));
15
15
  /**
16
16
  * tiny glob→RegExp: `**\/` matches zero-or-more leading directories (so
@@ -27,6 +27,31 @@ export async function startOrgServer(daemon, port = 0) {
27
27
  }
28
28
  });
29
29
  }
30
+ else if (req.method === 'POST' && req.url === '/api/human-message') {
31
+ let body = '';
32
+ req.on('data', c => { body += c; });
33
+ req.on('end', () => {
34
+ (async () => {
35
+ try {
36
+ const payload = JSON.parse(body || '{}');
37
+ if (!payload.org || !payload.role || !payload.text) {
38
+ res.writeHead(400, { 'Content-Type': 'application/json' });
39
+ res.end(JSON.stringify({ ok: false, error: 'org, role, text are required' }));
40
+ return;
41
+ }
42
+ const receipt = await daemon.deliver(payload.org, 'human', payload.role, 'message from human', payload.text);
43
+ const ok = !receipt.startsWith('ERROR:');
44
+ res.writeHead(ok ? 200 : 404, { 'Content-Type': 'application/json' });
45
+ res.end(JSON.stringify({ ok, receipt }));
46
+ }
47
+ catch (err) {
48
+ res.writeHead(400, { 'Content-Type': 'application/json' });
49
+ res.end(JSON.stringify({ ok: false, error: err instanceof Error ? err.message : 'bad request' }));
50
+ }
51
+ })();
52
+ });
53
+ return;
54
+ }
30
55
  else if (req.method === 'POST' && req.url === '/api/answer-question') {
31
56
  let body = '';
32
57
  req.on('data', c => { body += c; });