moflo 4.0.3 → 4.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/v3/@claude-flow/cli/dist/src/commands/hooks.js +4 -1
- package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.js +116 -173
- package/v3/@claude-flow/memory/dist/controller-registry.js +2 -2
- package/v3/@claude-flow/memory/dist/database-provider.d.ts +1 -1
- package/v3/@claude-flow/memory/dist/database-provider.js +4 -39
- package/v3/@claude-flow/memory/dist/database-provider.test.js +3 -3
- package/v3/@claude-flow/memory/dist/index.d.ts +0 -1
- package/v3/@claude-flow/memory/dist/index.js +0 -1
- package/v3/@claude-flow/memory/dist/rvf-migration.js +9 -29
- package/v3/@claude-flow/memory/dist/sqlite-backend.js +78 -70
- package/v3/@claude-flow/memory/package.json +42 -46
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "moflo",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.4",
|
|
4
4
|
"description": "MoFlo — AI agent orchestration for Claude Code. Forked from ruflo/claude-flow with patches applied to source, plus feature-level orchestration.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -59,7 +59,8 @@ const preEditCommand = {
|
|
|
59
59
|
output.printInfo(`Analyzing context for: ${output.highlight(filePath)}`);
|
|
60
60
|
try {
|
|
61
61
|
// Call MCP tool for pre-edit hook
|
|
62
|
-
|
|
62
|
+
// Wrap MCP call in timeout to prevent indefinite hang (#13)
|
|
63
|
+
const mcpPromise = callMCPTool('hooks_pre-edit', {
|
|
63
64
|
filePath,
|
|
64
65
|
operation,
|
|
65
66
|
context: ctx.flags.context,
|
|
@@ -183,6 +184,8 @@ const postEditCommand = {
|
|
|
183
184
|
metrics,
|
|
184
185
|
timestamp: Date.now(),
|
|
185
186
|
});
|
|
187
|
+
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('session-end timed out after 3s')), 3000));
|
|
188
|
+
const result = await Promise.race([mcpPromise, timeoutPromise]);
|
|
186
189
|
if (ctx.flags.format === 'json') {
|
|
187
190
|
output.printJson(result);
|
|
188
191
|
return { success: true, data: result };
|
|
@@ -17,11 +17,10 @@
|
|
|
17
17
|
* @module v3/cli/memory-bridge
|
|
18
18
|
*/
|
|
19
19
|
import * as path from 'path';
|
|
20
|
-
import * as fs from 'fs';
|
|
21
20
|
import * as crypto from 'crypto';
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
// ===== Project root resolution =====
|
|
22
|
+
// When run via npx, CWD may be node_modules/moflo — walk up to find actual project
|
|
23
|
+
import * as fs from 'fs';
|
|
25
24
|
let _projectRoot;
|
|
26
25
|
function getProjectRoot() {
|
|
27
26
|
if (_projectRoot)
|
|
@@ -41,11 +40,41 @@ function getProjectRoot() {
|
|
|
41
40
|
_projectRoot = dir;
|
|
42
41
|
return _projectRoot;
|
|
43
42
|
}
|
|
43
|
+
// Skip node_modules directories
|
|
44
|
+
if (path.basename(dir) === 'node_modules') {
|
|
45
|
+
dir = path.dirname(dir);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
44
48
|
dir = path.dirname(dir);
|
|
45
49
|
}
|
|
46
50
|
_projectRoot = process.cwd();
|
|
47
51
|
return _projectRoot;
|
|
48
52
|
}
|
|
53
|
+
// ===== Transformers.js fallback embedder =====
|
|
54
|
+
let _tfEmbedder = null;
|
|
55
|
+
let _tfFailed = false;
|
|
56
|
+
async function getFallbackEmbedder() {
|
|
57
|
+
if (_tfFailed)
|
|
58
|
+
return null;
|
|
59
|
+
if (_tfEmbedder)
|
|
60
|
+
return _tfEmbedder;
|
|
61
|
+
try {
|
|
62
|
+
const { pipeline } = await import('@xenova/transformers');
|
|
63
|
+
_tfEmbedder = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2');
|
|
64
|
+
return _tfEmbedder;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
_tfFailed = true;
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async function fallbackEmbed(text) {
|
|
72
|
+
const embedder = await getFallbackEmbedder();
|
|
73
|
+
if (!embedder)
|
|
74
|
+
return null;
|
|
75
|
+
const result = await embedder(text, { pooling: 'mean', normalize: true });
|
|
76
|
+
return Array.from(result.data);
|
|
77
|
+
}
|
|
49
78
|
// ===== Lazy singleton =====
|
|
50
79
|
let registryPromise = null;
|
|
51
80
|
let registryInstance = null;
|
|
@@ -56,15 +85,15 @@ let bridgeAvailable = null;
|
|
|
56
85
|
* or the special ':memory:' path.
|
|
57
86
|
*/
|
|
58
87
|
function getDbPath(customPath) {
|
|
59
|
-
const
|
|
60
|
-
const swarmDir = path.resolve(projectRoot, '.swarm');
|
|
88
|
+
const swarmDir = path.resolve(getProjectRoot(), '.swarm');
|
|
61
89
|
if (!customPath)
|
|
62
90
|
return path.join(swarmDir, 'memory.db');
|
|
63
91
|
if (customPath === ':memory:')
|
|
64
92
|
return ':memory:';
|
|
65
93
|
const resolved = path.resolve(customPath);
|
|
66
|
-
// Ensure the path doesn't escape the
|
|
67
|
-
|
|
94
|
+
// Ensure the path doesn't escape the working directory
|
|
95
|
+
const cwd = getProjectRoot();
|
|
96
|
+
if (!resolved.startsWith(cwd)) {
|
|
68
97
|
return path.join(swarmDir, 'memory.db'); // fallback to safe default
|
|
69
98
|
}
|
|
70
99
|
return resolved;
|
|
@@ -76,10 +105,8 @@ function generateId(prefix) {
|
|
|
76
105
|
return `${prefix}_${Date.now()}_${crypto.randomBytes(8).toString('hex')}`;
|
|
77
106
|
}
|
|
78
107
|
/**
|
|
79
|
-
* Lazily initialize the ControllerRegistry singleton
|
|
80
|
-
*
|
|
81
|
-
* The registry exposes .get(name), .getAgentDB(), .listControllers(), and .initialize()
|
|
82
|
-
* to maintain compatibility with all bridge consumers.
|
|
108
|
+
* Lazily initialize the ControllerRegistry singleton.
|
|
109
|
+
* Returns null if @claude-flow/memory is not available.
|
|
83
110
|
*/
|
|
84
111
|
async function getRegistry(dbPath) {
|
|
85
112
|
if (bridgeAvailable === false)
|
|
@@ -89,81 +116,13 @@ async function getRegistry(dbPath) {
|
|
|
89
116
|
if (!registryPromise) {
|
|
90
117
|
registryPromise = (async () => {
|
|
91
118
|
try {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const memoryRelPath = '../../../../memory/dist/index.js';
|
|
95
|
-
try {
|
|
96
|
-
memoryModule = await import(memoryRelPath);
|
|
97
|
-
}
|
|
98
|
-
catch {
|
|
99
|
-
try {
|
|
100
|
-
const req = createRequire(path.join(getProjectRoot(), 'package.json'));
|
|
101
|
-
const resolved = req.resolve('@claude-flow/memory');
|
|
102
|
-
memoryModule = await import(pathToFileURL(resolved).href);
|
|
103
|
-
}
|
|
104
|
-
catch {
|
|
105
|
-
// @claude-flow/memory not available
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
// If @claude-flow/memory has ControllerRegistry, use it directly
|
|
109
|
-
if (memoryModule?.ControllerRegistry) {
|
|
110
|
-
const { ControllerRegistry } = memoryModule;
|
|
111
|
-
const registry = new ControllerRegistry();
|
|
112
|
-
// Suppress noisy console.log during init
|
|
113
|
-
const origLog = console.log;
|
|
114
|
-
console.log = (...args) => {
|
|
115
|
-
const msg = String(args[0] ?? '');
|
|
116
|
-
if (msg.includes('Transformers.js') ||
|
|
117
|
-
msg.includes('better-sqlite3') ||
|
|
118
|
-
msg.includes('[AgentDB]') ||
|
|
119
|
-
msg.includes('[HNSWLibBackend]') ||
|
|
120
|
-
msg.includes('RuVector graph'))
|
|
121
|
-
return;
|
|
122
|
-
origLog.apply(console, args);
|
|
123
|
-
};
|
|
124
|
-
try {
|
|
125
|
-
await registry.initialize({
|
|
126
|
-
dbPath: dbPath || getDbPath(),
|
|
127
|
-
dimension: 384,
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
finally {
|
|
131
|
-
console.log = origLog;
|
|
132
|
-
}
|
|
133
|
-
registryInstance = registry;
|
|
134
|
-
return registry;
|
|
135
|
-
}
|
|
136
|
-
// Fallback: direct agentdb import with manual adapter
|
|
137
|
-
let agentdbModule = null;
|
|
138
|
-
try {
|
|
139
|
-
agentdbModule = await import('agentdb');
|
|
140
|
-
}
|
|
141
|
-
catch {
|
|
142
|
-
try {
|
|
143
|
-
const req = createRequire(path.join(getProjectRoot(), 'package.json'));
|
|
144
|
-
const resolved = req.resolve('agentdb');
|
|
145
|
-
agentdbModule = await import(pathToFileURL(resolved).href);
|
|
146
|
-
}
|
|
147
|
-
catch {
|
|
148
|
-
// Not available
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
if (!agentdbModule) {
|
|
152
|
-
bridgeAvailable = false;
|
|
153
|
-
registryPromise = null;
|
|
154
|
-
return null;
|
|
155
|
-
}
|
|
156
|
-
const { AgentDB } = agentdbModule;
|
|
157
|
-
const agentdb = new AgentDB({
|
|
158
|
-
dbPath: dbPath || getDbPath(),
|
|
159
|
-
dimension: 384,
|
|
160
|
-
});
|
|
119
|
+
const { ControllerRegistry } = await import('@claude-flow/memory');
|
|
120
|
+
const registry = new ControllerRegistry();
|
|
161
121
|
// Suppress noisy console.log during init
|
|
162
122
|
const origLog = console.log;
|
|
163
123
|
console.log = (...args) => {
|
|
164
124
|
const msg = String(args[0] ?? '');
|
|
165
125
|
if (msg.includes('Transformers.js') ||
|
|
166
|
-
msg.includes('better-sqlite3') ||
|
|
167
126
|
msg.includes('[AgentDB]') ||
|
|
168
127
|
msg.includes('[HNSWLibBackend]') ||
|
|
169
128
|
msg.includes('RuVector graph'))
|
|
@@ -171,38 +130,22 @@ async function getRegistry(dbPath) {
|
|
|
171
130
|
origLog.apply(console, args);
|
|
172
131
|
};
|
|
173
132
|
try {
|
|
174
|
-
await
|
|
133
|
+
await registry.initialize({
|
|
134
|
+
dbPath: dbPath || getDbPath(),
|
|
135
|
+
dimension: 384,
|
|
136
|
+
controllers: {
|
|
137
|
+
reasoningBank: true,
|
|
138
|
+
learningBridge: false,
|
|
139
|
+
tieredCache: true,
|
|
140
|
+
hierarchicalMemory: true,
|
|
141
|
+
memoryConsolidation: true,
|
|
142
|
+
memoryGraph: true, // issue #1214: enable MemoryGraph for graph-aware ranking
|
|
143
|
+
},
|
|
144
|
+
});
|
|
175
145
|
}
|
|
176
146
|
finally {
|
|
177
147
|
console.log = origLog;
|
|
178
148
|
}
|
|
179
|
-
// Build a registry-compatible adapter over AgentDB
|
|
180
|
-
const registry = {
|
|
181
|
-
_agentdb: agentdb,
|
|
182
|
-
get(name) {
|
|
183
|
-
try {
|
|
184
|
-
return agentdb.getController(name);
|
|
185
|
-
}
|
|
186
|
-
catch {
|
|
187
|
-
return null;
|
|
188
|
-
}
|
|
189
|
-
},
|
|
190
|
-
getAgentDB() {
|
|
191
|
-
return agentdb;
|
|
192
|
-
},
|
|
193
|
-
listControllers() {
|
|
194
|
-
return [
|
|
195
|
-
'reflexion', 'skills', 'reasoningBank',
|
|
196
|
-
'causalGraph', 'causalRecall', 'learningSystem',
|
|
197
|
-
'explainableRecall', 'nightlyLearner',
|
|
198
|
-
'graphTransformer', 'mutationGuard',
|
|
199
|
-
'attestationLog', 'vectorBackend'
|
|
200
|
-
].filter(name => {
|
|
201
|
-
try { return agentdb.getController(name) != null; }
|
|
202
|
-
catch { return false; }
|
|
203
|
-
});
|
|
204
|
-
},
|
|
205
|
-
};
|
|
206
149
|
registryInstance = registry;
|
|
207
150
|
bridgeAvailable = true;
|
|
208
151
|
return registry;
|
|
@@ -352,25 +295,25 @@ function getDb(registry) {
|
|
|
352
295
|
const db = agentdb.database;
|
|
353
296
|
// Ensure memory_entries table exists (idempotent)
|
|
354
297
|
try {
|
|
355
|
-
db.exec(`CREATE TABLE IF NOT EXISTS memory_entries (
|
|
356
|
-
id TEXT PRIMARY KEY,
|
|
357
|
-
key TEXT NOT NULL,
|
|
358
|
-
namespace TEXT DEFAULT 'default',
|
|
359
|
-
content TEXT NOT NULL,
|
|
360
|
-
type TEXT DEFAULT 'semantic',
|
|
361
|
-
embedding TEXT,
|
|
362
|
-
embedding_model TEXT DEFAULT 'local',
|
|
363
|
-
embedding_dimensions INTEGER,
|
|
364
|
-
tags TEXT,
|
|
365
|
-
metadata TEXT,
|
|
366
|
-
owner_id TEXT,
|
|
367
|
-
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),
|
|
368
|
-
updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),
|
|
369
|
-
expires_at INTEGER,
|
|
370
|
-
last_accessed_at INTEGER,
|
|
371
|
-
access_count INTEGER DEFAULT 0,
|
|
372
|
-
status TEXT DEFAULT 'active',
|
|
373
|
-
UNIQUE(namespace, key)
|
|
298
|
+
db.exec(`CREATE TABLE IF NOT EXISTS memory_entries (
|
|
299
|
+
id TEXT PRIMARY KEY,
|
|
300
|
+
key TEXT NOT NULL,
|
|
301
|
+
namespace TEXT DEFAULT 'default',
|
|
302
|
+
content TEXT NOT NULL,
|
|
303
|
+
type TEXT DEFAULT 'semantic',
|
|
304
|
+
embedding TEXT,
|
|
305
|
+
embedding_model TEXT DEFAULT 'local',
|
|
306
|
+
embedding_dimensions INTEGER,
|
|
307
|
+
tags TEXT,
|
|
308
|
+
metadata TEXT,
|
|
309
|
+
owner_id TEXT,
|
|
310
|
+
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),
|
|
311
|
+
updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),
|
|
312
|
+
expires_at INTEGER,
|
|
313
|
+
last_accessed_at INTEGER,
|
|
314
|
+
access_count INTEGER DEFAULT 0,
|
|
315
|
+
status TEXT DEFAULT 'active',
|
|
316
|
+
UNIQUE(namespace, key)
|
|
374
317
|
)`);
|
|
375
318
|
// Ensure indexes
|
|
376
319
|
db.exec(`CREATE INDEX IF NOT EXISTS idx_bridge_ns ON memory_entries(namespace)`);
|
|
@@ -424,17 +367,17 @@ export async function bridgeStoreEntry(options) {
|
|
|
424
367
|
// Embedding failed — store without
|
|
425
368
|
}
|
|
426
369
|
}
|
|
427
|
-
//
|
|
370
|
+
// sql.js uses synchronous .run() with positional params
|
|
428
371
|
const insertSql = options.upsert
|
|
429
|
-
? `INSERT OR REPLACE INTO memory_entries (
|
|
430
|
-
id, key, namespace, content, type,
|
|
431
|
-
embedding, embedding_dimensions, embedding_model,
|
|
432
|
-
tags, metadata, created_at, updated_at, expires_at, status
|
|
372
|
+
? `INSERT OR REPLACE INTO memory_entries (
|
|
373
|
+
id, key, namespace, content, type,
|
|
374
|
+
embedding, embedding_dimensions, embedding_model,
|
|
375
|
+
tags, metadata, created_at, updated_at, expires_at, status
|
|
433
376
|
) VALUES (?, ?, ?, ?, 'semantic', ?, ?, ?, ?, ?, ?, ?, ?, 'active')`
|
|
434
|
-
: `INSERT INTO memory_entries (
|
|
435
|
-
id, key, namespace, content, type,
|
|
436
|
-
embedding, embedding_dimensions, embedding_model,
|
|
437
|
-
tags, metadata, created_at, updated_at, expires_at, status
|
|
377
|
+
: `INSERT INTO memory_entries (
|
|
378
|
+
id, key, namespace, content, type,
|
|
379
|
+
embedding, embedding_dimensions, embedding_model,
|
|
380
|
+
tags, metadata, created_at, updated_at, expires_at, status
|
|
438
381
|
) VALUES (?, ?, ?, ?, 'semantic', ?, ?, ?, ?, ?, ?, ?, ?, 'active')`;
|
|
439
382
|
const stmt = ctx.db.prepare(insertSql);
|
|
440
383
|
stmt.run(id, key, namespace, value, embeddingJson, dimensions || null, model, tags.length > 0 ? JSON.stringify(tags) : null, '{}', now, now, ttl ? now + (ttl * 1000) : null);
|
|
@@ -485,17 +428,17 @@ export async function bridgeSearchEntries(options) {
|
|
|
485
428
|
catch {
|
|
486
429
|
// Fall back to keyword search
|
|
487
430
|
}
|
|
488
|
-
//
|
|
431
|
+
// Prepare/all returns array of objects
|
|
489
432
|
const nsFilter = namespace !== 'all'
|
|
490
433
|
? `AND namespace = ?`
|
|
491
434
|
: '';
|
|
492
435
|
let rows;
|
|
493
436
|
try {
|
|
494
|
-
const stmt = ctx.db.prepare(`
|
|
495
|
-
SELECT id, key, namespace, content, embedding
|
|
496
|
-
FROM memory_entries
|
|
497
|
-
WHERE status = 'active' ${nsFilter}
|
|
498
|
-
LIMIT 1000
|
|
437
|
+
const stmt = ctx.db.prepare(`
|
|
438
|
+
SELECT id, key, namespace, content, embedding
|
|
439
|
+
FROM memory_entries
|
|
440
|
+
WHERE status = 'active' ${nsFilter}
|
|
441
|
+
LIMIT 1000
|
|
499
442
|
`);
|
|
500
443
|
rows = namespace !== 'all' ? stmt.all(namespace) : stmt.all();
|
|
501
444
|
}
|
|
@@ -585,12 +528,12 @@ export async function bridgeListEntries(options) {
|
|
|
585
528
|
// List
|
|
586
529
|
const entries = [];
|
|
587
530
|
try {
|
|
588
|
-
const stmt = ctx.db.prepare(`
|
|
589
|
-
SELECT id, key, namespace, content, embedding, access_count, created_at, updated_at
|
|
590
|
-
FROM memory_entries
|
|
591
|
-
WHERE status = 'active' ${nsFilter}
|
|
592
|
-
ORDER BY updated_at DESC
|
|
593
|
-
LIMIT ? OFFSET ?
|
|
531
|
+
const stmt = ctx.db.prepare(`
|
|
532
|
+
SELECT id, key, namespace, content, embedding, access_count, created_at, updated_at
|
|
533
|
+
FROM memory_entries
|
|
534
|
+
WHERE status = 'active' ${nsFilter}
|
|
535
|
+
ORDER BY updated_at DESC
|
|
536
|
+
LIMIT ? OFFSET ?
|
|
594
537
|
`);
|
|
595
538
|
const rows = stmt.all(...nsParams, limit, offset);
|
|
596
539
|
for (const row of rows) {
|
|
@@ -653,11 +596,11 @@ export async function bridgeGetEntry(options) {
|
|
|
653
596
|
}
|
|
654
597
|
let row;
|
|
655
598
|
try {
|
|
656
|
-
const stmt = ctx.db.prepare(`
|
|
657
|
-
SELECT id, key, namespace, content, embedding, access_count, created_at, updated_at, tags
|
|
658
|
-
FROM memory_entries
|
|
659
|
-
WHERE status = 'active' AND key = ? AND namespace = ?
|
|
660
|
-
LIMIT 1
|
|
599
|
+
const stmt = ctx.db.prepare(`
|
|
600
|
+
SELECT id, key, namespace, content, embedding, access_count, created_at, updated_at, tags
|
|
601
|
+
FROM memory_entries
|
|
602
|
+
WHERE status = 'active' AND key = ? AND namespace = ?
|
|
603
|
+
LIMIT 1
|
|
661
604
|
`);
|
|
662
605
|
row = stmt.get(key, namespace);
|
|
663
606
|
}
|
|
@@ -721,10 +664,10 @@ export async function bridgeDeleteEntry(options) {
|
|
|
721
664
|
// Soft delete using parameterized query
|
|
722
665
|
let changes = 0;
|
|
723
666
|
try {
|
|
724
|
-
const result = ctx.db.prepare(`
|
|
725
|
-
UPDATE memory_entries
|
|
726
|
-
SET status = 'deleted', updated_at = ?
|
|
727
|
-
WHERE key = ? AND namespace = ? AND status = 'active'
|
|
667
|
+
const result = ctx.db.prepare(`
|
|
668
|
+
UPDATE memory_entries
|
|
669
|
+
SET status = 'deleted', updated_at = ?
|
|
670
|
+
WHERE key = ? AND namespace = ? AND status = 'active'
|
|
728
671
|
`).run(Date.now(), key, namespace);
|
|
729
672
|
changes = result?.changes ?? 0;
|
|
730
673
|
}
|
|
@@ -869,11 +812,11 @@ export async function bridgeSearchHNSW(queryEmbedding, options, dbPath) {
|
|
|
869
812
|
: '';
|
|
870
813
|
let rows;
|
|
871
814
|
try {
|
|
872
|
-
const stmt = ctx.db.prepare(`
|
|
873
|
-
SELECT id, key, namespace, content, embedding
|
|
874
|
-
FROM memory_entries
|
|
875
|
-
WHERE status = 'active' AND embedding IS NOT NULL ${nsFilter}
|
|
876
|
-
LIMIT 10000
|
|
815
|
+
const stmt = ctx.db.prepare(`
|
|
816
|
+
SELECT id, key, namespace, content, embedding
|
|
817
|
+
FROM memory_entries
|
|
818
|
+
WHERE status = 'active' AND embedding IS NOT NULL ${nsFilter}
|
|
819
|
+
LIMIT 10000
|
|
877
820
|
`);
|
|
878
821
|
rows = nsFilter
|
|
879
822
|
? stmt.all(options.namespace)
|
|
@@ -925,12 +868,12 @@ export async function bridgeAddToHNSW(id, embedding, entry, dbPath) {
|
|
|
925
868
|
try {
|
|
926
869
|
const now = Date.now();
|
|
927
870
|
const embeddingJson = JSON.stringify(embedding);
|
|
928
|
-
ctx.db.prepare(`
|
|
929
|
-
INSERT OR REPLACE INTO memory_entries (
|
|
930
|
-
id, key, namespace, content, type,
|
|
931
|
-
embedding, embedding_dimensions, embedding_model,
|
|
932
|
-
created_at, updated_at, status
|
|
933
|
-
) VALUES (?, ?, ?, ?, 'semantic', ?, ?, 'Xenova/all-MiniLM-L6-v2', ?, ?, 'active')
|
|
871
|
+
ctx.db.prepare(`
|
|
872
|
+
INSERT OR REPLACE INTO memory_entries (
|
|
873
|
+
id, key, namespace, content, type,
|
|
874
|
+
embedding, embedding_dimensions, embedding_model,
|
|
875
|
+
created_at, updated_at, status
|
|
876
|
+
) VALUES (?, ?, ?, ?, 'semantic', ?, ?, 'Xenova/all-MiniLM-L6-v2', ?, ?, 'active')
|
|
934
877
|
`).run(id, entry.key, entry.namespace, entry.content, embeddingJson, embedding.length, now, now);
|
|
935
878
|
return true;
|
|
936
879
|
}
|
|
@@ -1198,9 +1141,9 @@ export async function bridgeRecordCausalEdge(options) {
|
|
|
1198
1141
|
const ctx = getDb(registry);
|
|
1199
1142
|
if (ctx) {
|
|
1200
1143
|
try {
|
|
1201
|
-
ctx.db.prepare(`
|
|
1202
|
-
INSERT OR REPLACE INTO memory_entries (id, key, namespace, content, type, created_at, updated_at, status)
|
|
1203
|
-
VALUES (?, ?, 'causal-edges', ?, 'procedural', ?, ?, 'active')
|
|
1144
|
+
ctx.db.prepare(`
|
|
1145
|
+
INSERT OR REPLACE INTO memory_entries (id, key, namespace, content, type, created_at, updated_at, status)
|
|
1146
|
+
VALUES (?, ?, 'causal-edges', ?, 'procedural', ?, ?, 'active')
|
|
1204
1147
|
`).run(generateId('edge'), `${options.sourceId}→${options.targetId}`, JSON.stringify(options), Date.now(), Date.now());
|
|
1205
1148
|
return { success: true, controller: 'bridge-fallback' };
|
|
1206
1149
|
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* @module @claude-flow/memory/controller-registry
|
|
11
11
|
*/
|
|
12
12
|
import { EventEmitter } from 'node:events';
|
|
13
|
+
import * as path from 'node:path';
|
|
13
14
|
import { LearningBridge } from './learning-bridge.js';
|
|
14
15
|
import { MemoryGraph } from './memory-graph.js';
|
|
15
16
|
import { TieredCacheManager } from './cache-manager.js';
|
|
@@ -290,7 +291,7 @@ export class ControllerRegistry extends EventEmitter {
|
|
|
290
291
|
// Validate dbPath to prevent path traversal
|
|
291
292
|
const dbPath = config.dbPath || ':memory:';
|
|
292
293
|
if (dbPath !== ':memory:') {
|
|
293
|
-
const resolved =
|
|
294
|
+
const resolved = path.resolve(dbPath);
|
|
294
295
|
if (resolved.includes('..')) {
|
|
295
296
|
this.emit('agentdb:unavailable', { reason: 'Invalid dbPath' });
|
|
296
297
|
return;
|
|
@@ -309,7 +310,6 @@ export class ControllerRegistry extends EventEmitter {
|
|
|
309
310
|
const suppressFilter = (args) => {
|
|
310
311
|
const msg = String(args[0] ?? '');
|
|
311
312
|
return msg.includes('Transformers.js') ||
|
|
312
|
-
msg.includes('better-sqlite3') ||
|
|
313
313
|
msg.includes('[AgentDB]');
|
|
314
314
|
};
|
|
315
315
|
console.log = (...args) => {
|
|
@@ -12,7 +12,7 @@ import { IMemoryBackend } from './types.js';
|
|
|
12
12
|
/**
|
|
13
13
|
* Available database provider types
|
|
14
14
|
*/
|
|
15
|
-
export type DatabaseProvider = '
|
|
15
|
+
export type DatabaseProvider = 'sql.js' | 'json' | 'rvf' | 'auto';
|
|
16
16
|
/**
|
|
17
17
|
* Database creation options
|
|
18
18
|
*/
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { platform } from 'node:os';
|
|
12
12
|
import { existsSync } from 'node:fs';
|
|
13
|
-
import { SQLiteBackend } from './sqlite-backend.js';
|
|
14
13
|
import { SqlJsBackend } from './sqljs-backend.js';
|
|
15
14
|
/**
|
|
16
15
|
* Detect platform and recommend provider
|
|
@@ -21,7 +20,7 @@ function detectPlatform() {
|
|
|
21
20
|
const isMacOS = os === 'darwin';
|
|
22
21
|
const isLinux = os === 'linux';
|
|
23
22
|
// Recommend better-sqlite3 for Unix-like systems, sql.js for Windows
|
|
24
|
-
const recommendedProvider =
|
|
23
|
+
const recommendedProvider = 'sql.js';
|
|
25
24
|
return {
|
|
26
25
|
os,
|
|
27
26
|
isWindows,
|
|
@@ -36,19 +35,9 @@ function detectPlatform() {
|
|
|
36
35
|
async function testRvf() {
|
|
37
36
|
return true;
|
|
38
37
|
}
|
|
39
|
-
/**
|
|
40
|
-
* Test if better-sqlite3 is available and working
|
|
41
|
-
*/
|
|
38
|
+
/** better-sqlite3 removed — always returns false */
|
|
42
39
|
async function testBetterSqlite3() {
|
|
43
|
-
|
|
44
|
-
const Database = (await import('better-sqlite3')).default;
|
|
45
|
-
const testDb = new Database(':memory:');
|
|
46
|
-
testDb.close();
|
|
47
|
-
return true;
|
|
48
|
-
}
|
|
49
|
-
catch (error) {
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
40
|
+
return false;
|
|
52
41
|
}
|
|
53
42
|
/**
|
|
54
43
|
* Test if sql.js is available and working
|
|
@@ -87,19 +76,7 @@ async function selectProvider(preferred, verbose = false) {
|
|
|
87
76
|
}
|
|
88
77
|
return 'rvf';
|
|
89
78
|
}
|
|
90
|
-
// Try
|
|
91
|
-
if (platformInfo.recommendedProvider === 'better-sqlite3') {
|
|
92
|
-
if (await testBetterSqlite3()) {
|
|
93
|
-
if (verbose) {
|
|
94
|
-
console.log('[DatabaseProvider] better-sqlite3 available and working');
|
|
95
|
-
}
|
|
96
|
-
return 'better-sqlite3';
|
|
97
|
-
}
|
|
98
|
-
else if (verbose) {
|
|
99
|
-
console.log('[DatabaseProvider] better-sqlite3 not available, trying sql.js');
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
// Try sql.js as fallback
|
|
79
|
+
// Try sql.js (moflo: sql.js is the only SQLite backend)
|
|
103
80
|
if (await testSqlJs()) {
|
|
104
81
|
if (verbose) {
|
|
105
82
|
console.log('[DatabaseProvider] sql.js available and working');
|
|
@@ -147,18 +124,6 @@ export async function createDatabase(path, options = {}) {
|
|
|
147
124
|
}
|
|
148
125
|
let backend;
|
|
149
126
|
switch (selectedProvider) {
|
|
150
|
-
case 'better-sqlite3': {
|
|
151
|
-
const config = {
|
|
152
|
-
databasePath: path,
|
|
153
|
-
walMode,
|
|
154
|
-
optimize,
|
|
155
|
-
defaultNamespace,
|
|
156
|
-
maxEntries,
|
|
157
|
-
verbose,
|
|
158
|
-
};
|
|
159
|
-
backend = new SQLiteBackend(config);
|
|
160
|
-
break;
|
|
161
|
-
}
|
|
162
127
|
case 'sql.js': {
|
|
163
128
|
const config = {
|
|
164
129
|
databasePath: path,
|
|
@@ -33,7 +33,7 @@ describe('DatabaseProvider', () => {
|
|
|
33
33
|
expect(info.recommendedProvider).toBe('sql.js');
|
|
34
34
|
}
|
|
35
35
|
else {
|
|
36
|
-
expect(info.recommendedProvider).toBe('
|
|
36
|
+
expect(info.recommendedProvider).toBe('sql.js');
|
|
37
37
|
}
|
|
38
38
|
});
|
|
39
39
|
});
|
|
@@ -100,7 +100,7 @@ describe('DatabaseProvider', () => {
|
|
|
100
100
|
return;
|
|
101
101
|
}
|
|
102
102
|
const db = await createDatabase(':memory:', {
|
|
103
|
-
provider: '
|
|
103
|
+
provider: 'sql.js',
|
|
104
104
|
verbose: false,
|
|
105
105
|
});
|
|
106
106
|
expect(db).toBeDefined();
|
|
@@ -142,7 +142,7 @@ describe('DatabaseProvider', () => {
|
|
|
142
142
|
const available = await getAvailableProviders();
|
|
143
143
|
const providers = [];
|
|
144
144
|
if (available.betterSqlite3)
|
|
145
|
-
providers.push('
|
|
145
|
+
providers.push('sql.js');
|
|
146
146
|
if (available.sqlJs)
|
|
147
147
|
providers.push('sql.js');
|
|
148
148
|
providers.push('json'); // Always available
|
|
@@ -63,7 +63,6 @@ export { AgentDBAdapter } from './agentdb-adapter.js';
|
|
|
63
63
|
export type { AgentDBAdapterConfig } from './agentdb-adapter.js';
|
|
64
64
|
export { AgentDBBackend } from './agentdb-backend.js';
|
|
65
65
|
export type { AgentDBBackendConfig } from './agentdb-backend.js';
|
|
66
|
-
export { SQLiteBackend } from './sqlite-backend.js';
|
|
67
66
|
export type { SQLiteBackendConfig } from './sqlite-backend.js';
|
|
68
67
|
export { SqlJsBackend } from './sqljs-backend.js';
|
|
69
68
|
export type { SqlJsBackendConfig } from './sqljs-backend.js';
|
|
@@ -61,7 +61,6 @@ export { ControllerRegistry, INIT_LEVELS } from './controller-registry.js';
|
|
|
61
61
|
// ===== Core Components =====
|
|
62
62
|
export { AgentDBAdapter } from './agentdb-adapter.js';
|
|
63
63
|
export { AgentDBBackend } from './agentdb-backend.js';
|
|
64
|
-
export { SQLiteBackend } from './sqlite-backend.js';
|
|
65
64
|
export { SqlJsBackend } from './sqljs-backend.js';
|
|
66
65
|
export { HybridBackend } from './hybrid-backend.js';
|
|
67
66
|
export { RvfBackend } from './rvf-backend.js';
|
|
@@ -90,38 +90,18 @@ function normalizeSqliteRow(row) {
|
|
|
90
90
|
return out;
|
|
91
91
|
}
|
|
92
92
|
async function readSqliteRows(dbPath) {
|
|
93
|
-
//
|
|
93
|
+
// Use sql.js (WASM) for SQLite reading
|
|
94
94
|
try {
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
finally {
|
|
102
|
-
db.close();
|
|
103
|
-
}
|
|
95
|
+
const initSqlJs = (await import('sql.js')).default;
|
|
96
|
+
const SQL = await initSqlJs();
|
|
97
|
+
const fs = await import('node:fs');
|
|
98
|
+
const buf = fs.readFileSync(dbPath);
|
|
99
|
+
const db = new SQL.Database(buf);
|
|
100
|
+
return { exec: (sql) => db.exec(sql), close: () => db.close() };
|
|
104
101
|
}
|
|
105
|
-
catch {
|
|
106
|
-
|
|
107
|
-
try {
|
|
108
|
-
const mod = await import('sql.js');
|
|
109
|
-
const SQL = await (mod.default ?? mod)();
|
|
110
|
-
const db = new SQL.Database(await readFile(dbPath));
|
|
111
|
-
try {
|
|
112
|
-
const stmt = db.prepare('SELECT * FROM memory_entries');
|
|
113
|
-
const rows = [];
|
|
114
|
-
while (stmt.step())
|
|
115
|
-
rows.push(stmt.getAsObject());
|
|
116
|
-
stmt.free();
|
|
117
|
-
return rows;
|
|
118
|
-
}
|
|
119
|
-
finally {
|
|
120
|
-
db.close();
|
|
121
|
-
}
|
|
102
|
+
catch {
|
|
103
|
+
throw new Error('Cannot read SQLite: install sql.js');
|
|
122
104
|
}
|
|
123
|
-
catch { /* unavailable */ }
|
|
124
|
-
throw new Error('Cannot read SQLite: install better-sqlite3 or sql.js');
|
|
125
105
|
}
|
|
126
106
|
// -- Batch migration helper -------------------------------------------------
|
|
127
107
|
async function migrateBatches(items, rvfPath, options, normalize) {
|
|
@@ -8,7 +8,15 @@
|
|
|
8
8
|
* @module v3/memory/sqlite-backend
|
|
9
9
|
*/
|
|
10
10
|
import { EventEmitter } from 'node:events';
|
|
11
|
-
import
|
|
11
|
+
// Lazy import — better-sqlite3 is optional (requires native build tools)
|
|
12
|
+
let Database = null;
|
|
13
|
+
async function loadBetterSqlite3() {
|
|
14
|
+
if (!Database) {
|
|
15
|
+
const mod = await import('better-sqlite3');
|
|
16
|
+
Database = mod.default;
|
|
17
|
+
}
|
|
18
|
+
return Database;
|
|
19
|
+
}
|
|
12
20
|
/**
|
|
13
21
|
* Default configuration values
|
|
14
22
|
*/
|
|
@@ -91,19 +99,19 @@ export class SQLiteBackend extends EventEmitter {
|
|
|
91
99
|
async store(entry) {
|
|
92
100
|
this.ensureInitialized();
|
|
93
101
|
const startTime = performance.now();
|
|
94
|
-
const stmt = this.db.prepare(`
|
|
95
|
-
INSERT OR REPLACE INTO memory_entries (
|
|
96
|
-
id, key, content, type, namespace, tags, metadata,
|
|
97
|
-
owner_id, access_level, created_at, updated_at, expires_at,
|
|
98
|
-
version, "references", access_count, last_accessed_at
|
|
99
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
102
|
+
const stmt = this.db.prepare(`
|
|
103
|
+
INSERT OR REPLACE INTO memory_entries (
|
|
104
|
+
id, key, content, type, namespace, tags, metadata,
|
|
105
|
+
owner_id, access_level, created_at, updated_at, expires_at,
|
|
106
|
+
version, "references", access_count, last_accessed_at
|
|
107
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
100
108
|
`);
|
|
101
109
|
stmt.run(entry.id, entry.key, entry.content, entry.type, entry.namespace, JSON.stringify(entry.tags), JSON.stringify(entry.metadata), entry.ownerId || null, entry.accessLevel, entry.createdAt, entry.updatedAt, entry.expiresAt || null, entry.version, JSON.stringify(entry.references), entry.accessCount, entry.lastAccessedAt);
|
|
102
110
|
// Store embedding separately (as BLOB)
|
|
103
111
|
if (entry.embedding) {
|
|
104
|
-
const embeddingStmt = this.db.prepare(`
|
|
105
|
-
INSERT OR REPLACE INTO memory_embeddings (entry_id, embedding)
|
|
106
|
-
VALUES (?, ?)
|
|
112
|
+
const embeddingStmt = this.db.prepare(`
|
|
113
|
+
INSERT OR REPLACE INTO memory_embeddings (entry_id, embedding)
|
|
114
|
+
VALUES (?, ?)
|
|
107
115
|
`);
|
|
108
116
|
embeddingStmt.run(entry.id, Buffer.from(entry.embedding.buffer));
|
|
109
117
|
}
|
|
@@ -128,9 +136,9 @@ export class SQLiteBackend extends EventEmitter {
|
|
|
128
136
|
*/
|
|
129
137
|
async getByKey(namespace, key) {
|
|
130
138
|
this.ensureInitialized();
|
|
131
|
-
const stmt = this.db.prepare(`
|
|
132
|
-
SELECT * FROM memory_entries
|
|
133
|
-
WHERE namespace = ? AND key = ?
|
|
139
|
+
const stmt = this.db.prepare(`
|
|
140
|
+
SELECT * FROM memory_entries
|
|
141
|
+
WHERE namespace = ? AND key = ?
|
|
134
142
|
`);
|
|
135
143
|
const row = stmt.get(namespace, key);
|
|
136
144
|
if (!row)
|
|
@@ -244,9 +252,9 @@ export class SQLiteBackend extends EventEmitter {
|
|
|
244
252
|
}
|
|
245
253
|
// Use parameterized query with JSON functions
|
|
246
254
|
const tagPlaceholders = query.tags.map(() => '?').join(', ');
|
|
247
|
-
sql += ` AND EXISTS (
|
|
248
|
-
SELECT 1 FROM json_each(tags) AS t
|
|
249
|
-
WHERE t.value IN (${tagPlaceholders})
|
|
255
|
+
sql += ` AND EXISTS (
|
|
256
|
+
SELECT 1 FROM json_each(tags) AS t
|
|
257
|
+
WHERE t.value IN (${tagPlaceholders})
|
|
250
258
|
)`;
|
|
251
259
|
params.push(...query.tags);
|
|
252
260
|
}
|
|
@@ -342,9 +350,9 @@ export class SQLiteBackend extends EventEmitter {
|
|
|
342
350
|
const deleteEntries = this.db.prepare('DELETE FROM memory_entries WHERE namespace = ?');
|
|
343
351
|
const result = deleteEntries.run(namespace);
|
|
344
352
|
// Clean up orphaned embeddings
|
|
345
|
-
this.db.prepare(`
|
|
346
|
-
DELETE FROM memory_embeddings
|
|
347
|
-
WHERE entry_id NOT IN (SELECT id FROM memory_entries)
|
|
353
|
+
this.db.prepare(`
|
|
354
|
+
DELETE FROM memory_embeddings
|
|
355
|
+
WHERE entry_id NOT IN (SELECT id FROM memory_entries)
|
|
348
356
|
`).run();
|
|
349
357
|
return result.changes;
|
|
350
358
|
}
|
|
@@ -354,10 +362,10 @@ export class SQLiteBackend extends EventEmitter {
|
|
|
354
362
|
async getStats() {
|
|
355
363
|
this.ensureInitialized();
|
|
356
364
|
// Count by namespace
|
|
357
|
-
const namespaceStmt = this.db.prepare(`
|
|
358
|
-
SELECT namespace, COUNT(*) as count
|
|
359
|
-
FROM memory_entries
|
|
360
|
-
GROUP BY namespace
|
|
365
|
+
const namespaceStmt = this.db.prepare(`
|
|
366
|
+
SELECT namespace, COUNT(*) as count
|
|
367
|
+
FROM memory_entries
|
|
368
|
+
GROUP BY namespace
|
|
361
369
|
`);
|
|
362
370
|
const namespaceRows = namespaceStmt.all();
|
|
363
371
|
const entriesByNamespace = {};
|
|
@@ -365,10 +373,10 @@ export class SQLiteBackend extends EventEmitter {
|
|
|
365
373
|
entriesByNamespace[row.namespace] = row.count;
|
|
366
374
|
}
|
|
367
375
|
// Count by type
|
|
368
|
-
const typeStmt = this.db.prepare(`
|
|
369
|
-
SELECT type, COUNT(*) as count
|
|
370
|
-
FROM memory_entries
|
|
371
|
-
GROUP BY type
|
|
376
|
+
const typeStmt = this.db.prepare(`
|
|
377
|
+
SELECT type, COUNT(*) as count
|
|
378
|
+
FROM memory_entries
|
|
379
|
+
GROUP BY type
|
|
372
380
|
`);
|
|
373
381
|
const typeRows = typeStmt.all();
|
|
374
382
|
const entriesByType = {
|
|
@@ -475,40 +483,40 @@ export class SQLiteBackend extends EventEmitter {
|
|
|
475
483
|
if (!this.db)
|
|
476
484
|
return;
|
|
477
485
|
// Main entries table
|
|
478
|
-
this.db.exec(`
|
|
479
|
-
CREATE TABLE IF NOT EXISTS memory_entries (
|
|
480
|
-
id TEXT PRIMARY KEY,
|
|
481
|
-
key TEXT NOT NULL,
|
|
482
|
-
content TEXT NOT NULL,
|
|
483
|
-
type TEXT NOT NULL,
|
|
484
|
-
namespace TEXT NOT NULL,
|
|
485
|
-
tags TEXT NOT NULL,
|
|
486
|
-
metadata TEXT NOT NULL,
|
|
487
|
-
owner_id TEXT,
|
|
488
|
-
access_level TEXT NOT NULL,
|
|
489
|
-
created_at INTEGER NOT NULL,
|
|
490
|
-
updated_at INTEGER NOT NULL,
|
|
491
|
-
expires_at INTEGER,
|
|
492
|
-
version INTEGER NOT NULL,
|
|
493
|
-
"references" TEXT NOT NULL,
|
|
494
|
-
access_count INTEGER NOT NULL,
|
|
495
|
-
last_accessed_at INTEGER NOT NULL
|
|
496
|
-
);
|
|
497
|
-
|
|
498
|
-
CREATE INDEX IF NOT EXISTS idx_namespace ON memory_entries(namespace);
|
|
499
|
-
CREATE INDEX IF NOT EXISTS idx_key ON memory_entries(key);
|
|
500
|
-
CREATE INDEX IF NOT EXISTS idx_namespace_key ON memory_entries(namespace, key);
|
|
501
|
-
CREATE INDEX IF NOT EXISTS idx_type ON memory_entries(type);
|
|
502
|
-
CREATE INDEX IF NOT EXISTS idx_owner_id ON memory_entries(owner_id);
|
|
503
|
-
CREATE INDEX IF NOT EXISTS idx_created_at ON memory_entries(created_at);
|
|
504
|
-
CREATE INDEX IF NOT EXISTS idx_updated_at ON memory_entries(updated_at);
|
|
505
|
-
CREATE INDEX IF NOT EXISTS idx_expires_at ON memory_entries(expires_at);
|
|
506
|
-
|
|
507
|
-
CREATE TABLE IF NOT EXISTS memory_embeddings (
|
|
508
|
-
entry_id TEXT PRIMARY KEY,
|
|
509
|
-
embedding BLOB,
|
|
510
|
-
FOREIGN KEY (entry_id) REFERENCES memory_entries(id) ON DELETE CASCADE
|
|
511
|
-
);
|
|
486
|
+
this.db.exec(`
|
|
487
|
+
CREATE TABLE IF NOT EXISTS memory_entries (
|
|
488
|
+
id TEXT PRIMARY KEY,
|
|
489
|
+
key TEXT NOT NULL,
|
|
490
|
+
content TEXT NOT NULL,
|
|
491
|
+
type TEXT NOT NULL,
|
|
492
|
+
namespace TEXT NOT NULL,
|
|
493
|
+
tags TEXT NOT NULL,
|
|
494
|
+
metadata TEXT NOT NULL,
|
|
495
|
+
owner_id TEXT,
|
|
496
|
+
access_level TEXT NOT NULL,
|
|
497
|
+
created_at INTEGER NOT NULL,
|
|
498
|
+
updated_at INTEGER NOT NULL,
|
|
499
|
+
expires_at INTEGER,
|
|
500
|
+
version INTEGER NOT NULL,
|
|
501
|
+
"references" TEXT NOT NULL,
|
|
502
|
+
access_count INTEGER NOT NULL,
|
|
503
|
+
last_accessed_at INTEGER NOT NULL
|
|
504
|
+
);
|
|
505
|
+
|
|
506
|
+
CREATE INDEX IF NOT EXISTS idx_namespace ON memory_entries(namespace);
|
|
507
|
+
CREATE INDEX IF NOT EXISTS idx_key ON memory_entries(key);
|
|
508
|
+
CREATE INDEX IF NOT EXISTS idx_namespace_key ON memory_entries(namespace, key);
|
|
509
|
+
CREATE INDEX IF NOT EXISTS idx_type ON memory_entries(type);
|
|
510
|
+
CREATE INDEX IF NOT EXISTS idx_owner_id ON memory_entries(owner_id);
|
|
511
|
+
CREATE INDEX IF NOT EXISTS idx_created_at ON memory_entries(created_at);
|
|
512
|
+
CREATE INDEX IF NOT EXISTS idx_updated_at ON memory_entries(updated_at);
|
|
513
|
+
CREATE INDEX IF NOT EXISTS idx_expires_at ON memory_entries(expires_at);
|
|
514
|
+
|
|
515
|
+
CREATE TABLE IF NOT EXISTS memory_embeddings (
|
|
516
|
+
entry_id TEXT PRIMARY KEY,
|
|
517
|
+
embedding BLOB,
|
|
518
|
+
FOREIGN KEY (entry_id) REFERENCES memory_entries(id) ON DELETE CASCADE
|
|
519
|
+
);
|
|
512
520
|
`);
|
|
513
521
|
}
|
|
514
522
|
rowToEntry(row) {
|
|
@@ -543,18 +551,18 @@ export class SQLiteBackend extends EventEmitter {
|
|
|
543
551
|
* Synchronous store for use in transactions
|
|
544
552
|
*/
|
|
545
553
|
storeSync(entry) {
|
|
546
|
-
const stmt = this.db.prepare(`
|
|
547
|
-
INSERT OR REPLACE INTO memory_entries (
|
|
548
|
-
id, key, content, type, namespace, tags, metadata,
|
|
549
|
-
owner_id, access_level, created_at, updated_at, expires_at,
|
|
550
|
-
version, "references", access_count, last_accessed_at
|
|
551
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
554
|
+
const stmt = this.db.prepare(`
|
|
555
|
+
INSERT OR REPLACE INTO memory_entries (
|
|
556
|
+
id, key, content, type, namespace, tags, metadata,
|
|
557
|
+
owner_id, access_level, created_at, updated_at, expires_at,
|
|
558
|
+
version, "references", access_count, last_accessed_at
|
|
559
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
552
560
|
`);
|
|
553
561
|
stmt.run(entry.id, entry.key, entry.content, entry.type, entry.namespace, JSON.stringify(entry.tags), JSON.stringify(entry.metadata), entry.ownerId || null, entry.accessLevel, entry.createdAt, entry.updatedAt, entry.expiresAt || null, entry.version, JSON.stringify(entry.references), entry.accessCount, entry.lastAccessedAt);
|
|
554
562
|
if (entry.embedding) {
|
|
555
|
-
const embeddingStmt = this.db.prepare(`
|
|
556
|
-
INSERT OR REPLACE INTO memory_embeddings (entry_id, embedding)
|
|
557
|
-
VALUES (?, ?)
|
|
563
|
+
const embeddingStmt = this.db.prepare(`
|
|
564
|
+
INSERT OR REPLACE INTO memory_embeddings (entry_id, embedding)
|
|
565
|
+
VALUES (?, ?)
|
|
558
566
|
`);
|
|
559
567
|
embeddingStmt.run(entry.id, Buffer.from(entry.embedding.buffer));
|
|
560
568
|
}
|
|
@@ -1,46 +1,42 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@claude-flow/memory",
|
|
3
|
-
"version": "3.0.0-alpha.11",
|
|
4
|
-
"type": "module",
|
|
5
|
-
"description": "Memory module - AgentDB unification, HNSW indexing, vector search, hybrid SQLite+AgentDB backend (ADR-009)",
|
|
6
|
-
"main": "dist/index.js",
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": "./dist/index.js",
|
|
10
|
-
"./*": "./dist/*.js"
|
|
11
|
-
},
|
|
12
|
-
"scripts": {
|
|
13
|
-
"test": "vitest run",
|
|
14
|
-
"bench": "vitest bench",
|
|
15
|
-
"build": "tsc"
|
|
16
|
-
},
|
|
17
|
-
"dependencies": {
|
|
18
|
-
"agentdb": "^3.0.0-alpha.10",
|
|
19
|
-
"sql.js": "^1.10.3"
|
|
20
|
-
},
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
]
|
|
42
|
-
|
|
43
|
-
"x64",
|
|
44
|
-
"arm64"
|
|
45
|
-
]
|
|
46
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@claude-flow/memory",
|
|
3
|
+
"version": "3.0.0-alpha.11",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Memory module - AgentDB unification, HNSW indexing, vector search, hybrid SQLite+AgentDB backend (ADR-009)",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./dist/index.js",
|
|
10
|
+
"./*": "./dist/*.js"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"test": "vitest run",
|
|
14
|
+
"bench": "vitest bench",
|
|
15
|
+
"build": "tsc"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"agentdb": "^3.0.0-alpha.10",
|
|
19
|
+
"sql.js": "^1.10.3"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/sql.js": "^1.4.9",
|
|
23
|
+
"vitest": "^4.0.16"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public",
|
|
31
|
+
"tag": "v3alpha"
|
|
32
|
+
},
|
|
33
|
+
"os": [
|
|
34
|
+
"darwin",
|
|
35
|
+
"linux",
|
|
36
|
+
"win32"
|
|
37
|
+
],
|
|
38
|
+
"cpu": [
|
|
39
|
+
"x64",
|
|
40
|
+
"arm64"
|
|
41
|
+
]
|
|
42
|
+
}
|