monomind 2.5.3 → 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.
- package/README.md +10 -3
- package/package.json +1 -1
- package/packages/@monomind/cli/.claude/commands/mastermind/topology.md +121 -0
- package/packages/@monomind/cli/.claude/commands/mastermind.md +121 -102
- package/packages/@monomind/cli/.claude/helpers/control-start.cjs +22 -2
- package/packages/@monomind/cli/.claude/helpers/control-stop.cjs +95 -0
- package/packages/@monomind/cli/.claude/helpers/graphify-freshen.cjs +6 -0
- package/packages/@monomind/cli/.claude/helpers/handlers/gates-handler.cjs +5 -0
- package/packages/@monomind/cli/.claude/helpers/hook-handler.cjs +13 -5
- package/packages/@monomind/cli/.claude/helpers/utils/monograph.cjs +33 -10
- package/packages/@monomind/cli/.claude/helpers/utils/system-pressure.cjs +46 -0
- package/packages/@monomind/cli/.claude/settings.json +448 -0
- package/packages/@monomind/cli/README.md +10 -3
- package/packages/@monomind/cli/dist/src/commands/agent-lifecycle.js +1 -1
- package/packages/@monomind/cli/dist/src/commands/agent-ops.js +1 -1
- package/packages/@monomind/cli/dist/src/commands/cleanup.js +53 -1
- package/packages/@monomind/cli/dist/src/commands/config.js +1 -1
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.js +1 -1
- package/packages/@monomind/cli/dist/src/commands/hooks.js +1 -1
- package/packages/@monomind/cli/dist/src/commands/index.js +4 -0
- package/packages/@monomind/cli/dist/src/commands/init-wizard.js +3 -3
- package/packages/@monomind/cli/dist/src/commands/init.js +2 -2
- package/packages/@monomind/cli/dist/src/commands/mcp.js +2 -2
- package/packages/@monomind/cli/dist/src/commands/memory-admin.js +8 -7
- package/packages/@monomind/cli/dist/src/commands/memory-list.js +2 -2
- package/packages/@monomind/cli/dist/src/commands/memory.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/commands/memory.js +4 -4
- package/packages/@monomind/cli/dist/src/commands/neural-core.js +1 -1
- package/packages/@monomind/cli/dist/src/commands/performance.js +8 -9
- package/packages/@monomind/cli/dist/src/commands/status.js +2 -2
- package/packages/@monomind/cli/dist/src/commands/swarm.js +1 -1
- package/packages/@monomind/cli/dist/src/index.js +1 -1
- package/packages/@monomind/cli/dist/src/init/claudemd-generator.js +2 -2
- package/packages/@monomind/cli/dist/src/init/executor.js +13 -6
- package/packages/@monomind/cli/dist/src/init/helpers-generator.js +66 -3
- package/packages/@monomind/cli/dist/src/init/shared-instructions-generator.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/init/shared-instructions-generator.js +1 -1
- package/packages/@monomind/cli/dist/src/init/statusline-generator.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/init/statusline-generator.js +2 -2
- package/packages/@monomind/cli/dist/src/init/types.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/knowledge/document-pipeline.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/knowledge/document-pipeline.js +3 -3
- package/packages/@monomind/cli/dist/src/mcp-client.d.ts +13 -51
- package/packages/@monomind/cli/dist/src/mcp-client.js +79 -134
- package/packages/@monomind/cli/dist/src/mcp-server.js +4 -3
- package/packages/@monomind/cli/dist/src/mcp-tools/browser-tools.js +45 -19
- package/packages/@monomind/cli/dist/src/mcp-tools/guidance-tools.js +1 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/hive-mind-tools.js +2 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-intelligence.js +2 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/knowledge-tools.js +1 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-compat.js +2 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +22 -9
- package/packages/@monomind/cli/dist/src/mcp-tools/neural-tools.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/neural-tools.js +3 -3
- package/packages/@monomind/cli/dist/src/mcp-tools/system-tools.js +1 -1
- package/packages/@monomind/cli/dist/src/memory/embedding-operations.js +2 -2
- package/packages/@monomind/cli/dist/src/memory/hnsw-operations.js +53 -26
- package/packages/@monomind/cli/dist/src/memory/intelligence.js +2 -2
- package/packages/@monomind/cli/dist/src/memory/memory-bridge.d.ts +7 -3
- package/packages/@monomind/cli/dist/src/memory/memory-bridge.js +24 -10
- package/packages/@monomind/cli/dist/src/memory/memory-crud.js +2 -2
- package/packages/@monomind/cli/dist/src/memory/memory-initializer.d.ts +2 -2
- package/packages/@monomind/cli/dist/src/memory/memory-initializer.js +6 -6
- package/packages/@monomind/cli/dist/src/memory/memory-read.js +4 -4
- package/packages/@monomind/cli/dist/src/orgrt/daemon.js +12 -6
- package/packages/@monomind/cli/dist/src/orgrt/mailbox.d.ts +1 -0
- package/packages/@monomind/cli/dist/src/orgrt/mailbox.js +4 -0
- package/packages/@monomind/cli/dist/src/orgrt/policy.js +1 -1
- package/packages/@monomind/cli/dist/src/orgrt/server.js +25 -0
- package/packages/@monomind/cli/dist/src/ui/dashboard.html +327 -611
- package/packages/@monomind/cli/dist/src/ui/server.mjs +165 -22
- 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
|
|
766
|
-
*
|
|
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 =
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
42
|
-
const
|
|
43
|
-
|
|
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
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
rows
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
67
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2
|
+
* Memory Bridge — Routes CLI memory operations through SQLite
|
|
3
3
|
*
|
|
4
|
-
* Uses
|
|
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
|
|
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
|
|
2
|
+
* Memory Bridge — Routes CLI memory operations through SQLite
|
|
3
3
|
*
|
|
4
|
-
* Uses
|
|
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
|
|
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
|
|
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
|
|
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
|
-
//
|
|
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 (
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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
|
|
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
|
|
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 →
|
|
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 →
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
|
|
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
|
-
|
|
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,
|
|
@@ -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 =
|
|
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; });
|