@aman_asmuei/amem 0.19.0 → 0.20.0

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 (85) hide show
  1. package/dist/cli.js +6 -7
  2. package/dist/cli.js.map +1 -1
  3. package/dist/dashboard.d.ts +1 -1
  4. package/dist/dashboard.js +1 -1
  5. package/dist/dashboard.js.map +1 -1
  6. package/dist/index.js +3 -7
  7. package/dist/index.js.map +1 -1
  8. package/dist/tools/advanced.d.ts +1 -1
  9. package/dist/tools/advanced.js +1 -5
  10. package/dist/tools/advanced.js.map +1 -1
  11. package/dist/tools/graph.d.ts +1 -1
  12. package/dist/tools/graph.js +1 -2
  13. package/dist/tools/graph.js.map +1 -1
  14. package/dist/tools/index.d.ts +2 -2
  15. package/dist/tools/index.js +2 -2
  16. package/dist/tools/index.js.map +1 -1
  17. package/dist/tools/log.d.ts +1 -1
  18. package/dist/tools/log.js +1 -2
  19. package/dist/tools/log.js.map +1 -1
  20. package/dist/tools/memory.d.ts +1 -2
  21. package/dist/tools/memory.js +16 -22
  22. package/dist/tools/memory.js.map +1 -1
  23. package/dist/tools/reminders.d.ts +1 -1
  24. package/dist/tools/reminders.js +4 -5
  25. package/dist/tools/reminders.js.map +1 -1
  26. package/dist/tools/versions.d.ts +1 -1
  27. package/dist/tools/versions.js +3 -4
  28. package/dist/tools/versions.js.map +1 -1
  29. package/dist/tools.test.js +1 -2
  30. package/dist/tools.test.js.map +1 -1
  31. package/package.json +3 -9
  32. package/dist/ann.d.ts +0 -26
  33. package/dist/ann.js +0 -155
  34. package/dist/ann.js.map +0 -1
  35. package/dist/auto-relate.d.ts +0 -14
  36. package/dist/auto-relate.js +0 -47
  37. package/dist/auto-relate.js.map +0 -1
  38. package/dist/config.d.ts +0 -51
  39. package/dist/config.js +0 -130
  40. package/dist/config.js.map +0 -1
  41. package/dist/database.d.ts +0 -165
  42. package/dist/database.js +0 -831
  43. package/dist/database.js.map +0 -1
  44. package/dist/database.test.d.ts +0 -1
  45. package/dist/database.test.js +0 -275
  46. package/dist/database.test.js.map +0 -1
  47. package/dist/doctor.d.ts +0 -23
  48. package/dist/doctor.js +0 -107
  49. package/dist/doctor.js.map +0 -1
  50. package/dist/embeddings.d.ts +0 -38
  51. package/dist/embeddings.js +0 -251
  52. package/dist/embeddings.js.map +0 -1
  53. package/dist/embeddings.test.d.ts +0 -1
  54. package/dist/embeddings.test.js +0 -106
  55. package/dist/embeddings.test.js.map +0 -1
  56. package/dist/extractor.d.ts +0 -13
  57. package/dist/extractor.js +0 -89
  58. package/dist/extractor.js.map +0 -1
  59. package/dist/memory.d.ts +0 -134
  60. package/dist/memory.js +0 -432
  61. package/dist/memory.js.map +0 -1
  62. package/dist/memory.test.d.ts +0 -1
  63. package/dist/memory.test.js +0 -172
  64. package/dist/memory.test.js.map +0 -1
  65. package/dist/query-expand.d.ts +0 -9
  66. package/dist/query-expand.js +0 -71
  67. package/dist/query-expand.js.map +0 -1
  68. package/dist/reflection.d.ts +0 -82
  69. package/dist/reflection.js +0 -414
  70. package/dist/reflection.js.map +0 -1
  71. package/dist/reflection.test.d.ts +0 -1
  72. package/dist/reflection.test.js +0 -453
  73. package/dist/reflection.test.js.map +0 -1
  74. package/dist/repair.d.ts +0 -8
  75. package/dist/repair.js +0 -90
  76. package/dist/repair.js.map +0 -1
  77. package/dist/schemas.d.ts +0 -1075
  78. package/dist/schemas.js +0 -311
  79. package/dist/schemas.js.map +0 -1
  80. package/dist/sync.d.ts +0 -85
  81. package/dist/sync.js +0 -304
  82. package/dist/sync.js.map +0 -1
  83. package/dist/tools/helpers.d.ts +0 -7
  84. package/dist/tools/helpers.js +0 -23
  85. package/dist/tools/helpers.js.map +0 -1
package/dist/database.js DELETED
@@ -1,831 +0,0 @@
1
- import Database from "better-sqlite3";
2
- import { randomUUID, createHash } from "node:crypto";
3
- function rowToMemory(row) {
4
- return {
5
- id: row.id,
6
- content: row.content,
7
- type: row.type,
8
- tags: JSON.parse(row.tags),
9
- confidence: row.confidence,
10
- accessCount: row.access_count,
11
- createdAt: row.created_at,
12
- lastAccessed: row.last_accessed,
13
- source: row.source,
14
- embedding: row.embedding
15
- ? new Float32Array(row.embedding.buffer, row.embedding.byteOffset, row.embedding.byteLength / 4)
16
- : null,
17
- scope: row.scope,
18
- validFrom: row.valid_from ?? row.created_at,
19
- validUntil: row.valid_until ?? null,
20
- tier: (row.tier ?? 'archival'),
21
- utilityScore: row.utility_score ?? 0,
22
- };
23
- }
24
- function rowToSummary(row) {
25
- return {
26
- id: row.id,
27
- sessionId: row.session_id,
28
- summary: row.summary,
29
- keyDecisions: JSON.parse(row.key_decisions),
30
- keyCorrections: JSON.parse(row.key_corrections),
31
- memoriesExtracted: row.memories_extracted,
32
- project: row.project,
33
- createdAt: row.created_at,
34
- };
35
- }
36
- function rowToRelation(r) {
37
- return {
38
- id: r.id,
39
- fromId: r.from_id,
40
- toId: r.to_id,
41
- relationshipType: r.relationship_type,
42
- strength: r.strength,
43
- createdAt: r.created_at,
44
- validFrom: r.valid_from ?? null,
45
- validUntil: r.valid_until ?? null,
46
- };
47
- }
48
- export function createDatabase(dbPath) {
49
- const db = new Database(dbPath);
50
- db.pragma("journal_mode = WAL");
51
- db.pragma("foreign_keys = ON");
52
- db.pragma("busy_timeout = 5000"); // Wait up to 5s for lock — enables multi-process safety
53
- // Expose transaction support for atomic multi-step operations
54
- const runTransaction = db.transaction((fn) => fn());
55
- db.exec(`
56
- CREATE TABLE IF NOT EXISTS memories (
57
- id TEXT PRIMARY KEY,
58
- content TEXT NOT NULL,
59
- type TEXT NOT NULL,
60
- tags TEXT NOT NULL DEFAULT '[]',
61
- confidence REAL NOT NULL DEFAULT 1.0,
62
- access_count INTEGER NOT NULL DEFAULT 0,
63
- created_at INTEGER NOT NULL,
64
- last_accessed INTEGER NOT NULL,
65
- source TEXT NOT NULL,
66
- embedding BLOB,
67
- scope TEXT NOT NULL DEFAULT 'global'
68
- );
69
-
70
- CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(type);
71
- CREATE INDEX IF NOT EXISTS idx_memories_created_at ON memories(created_at);
72
- CREATE INDEX IF NOT EXISTS idx_memories_confidence ON memories(confidence);
73
-
74
- -- Lossless raw conversation log (append-only)
75
- CREATE TABLE IF NOT EXISTS conversation_log (
76
- id TEXT PRIMARY KEY,
77
- session_id TEXT NOT NULL,
78
- role TEXT NOT NULL CHECK(role IN ('user','assistant','system')),
79
- content TEXT NOT NULL,
80
- timestamp INTEGER NOT NULL,
81
- project TEXT NOT NULL DEFAULT 'global',
82
- metadata TEXT NOT NULL DEFAULT '{}'
83
- );
84
- CREATE INDEX IF NOT EXISTS idx_log_session ON conversation_log(session_id);
85
- CREATE INDEX IF NOT EXISTS idx_log_timestamp ON conversation_log(timestamp);
86
- CREATE INDEX IF NOT EXISTS idx_log_project ON conversation_log(project);
87
-
88
- -- FTS for full-text search on memories
89
- CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
90
- id UNINDEXED,
91
- content,
92
- tags,
93
- content='memories',
94
- content_rowid='rowid'
95
- );
96
-
97
- -- FTS for full-text search on conversation log
98
- CREATE VIRTUAL TABLE IF NOT EXISTS log_fts USING fts5(
99
- id UNINDEXED,
100
- content,
101
- content='conversation_log',
102
- content_rowid='rowid'
103
- );
104
-
105
- -- Memory version history
106
- CREATE TABLE IF NOT EXISTS memory_versions (
107
- version_id TEXT PRIMARY KEY,
108
- memory_id TEXT NOT NULL,
109
- content TEXT NOT NULL,
110
- confidence REAL NOT NULL,
111
- edited_at INTEGER NOT NULL,
112
- reason TEXT NOT NULL DEFAULT 'manual edit',
113
- FOREIGN KEY(memory_id) REFERENCES memories(id) ON DELETE CASCADE
114
- );
115
- CREATE INDEX IF NOT EXISTS idx_versions_memory_id ON memory_versions(memory_id);
116
-
117
- -- Knowledge graph: relations between memories
118
- CREATE TABLE IF NOT EXISTS memory_relations (
119
- id TEXT PRIMARY KEY,
120
- from_id TEXT NOT NULL,
121
- to_id TEXT NOT NULL,
122
- relationship_type TEXT NOT NULL,
123
- strength REAL NOT NULL DEFAULT 0.8,
124
- created_at INTEGER NOT NULL,
125
- FOREIGN KEY(from_id) REFERENCES memories(id) ON DELETE CASCADE,
126
- FOREIGN KEY(to_id) REFERENCES memories(id) ON DELETE CASCADE,
127
- UNIQUE(from_id, to_id, relationship_type)
128
- );
129
- CREATE INDEX IF NOT EXISTS idx_relations_from ON memory_relations(from_id);
130
- CREATE INDEX IF NOT EXISTS idx_relations_to ON memory_relations(to_id);
131
-
132
- -- Reminders
133
- CREATE TABLE IF NOT EXISTS reminders (
134
- id TEXT PRIMARY KEY,
135
- content TEXT NOT NULL,
136
- due_at INTEGER,
137
- completed INTEGER NOT NULL DEFAULT 0,
138
- created_at INTEGER NOT NULL,
139
- scope TEXT NOT NULL DEFAULT 'global'
140
- );
141
- CREATE INDEX IF NOT EXISTS idx_reminders_due ON reminders(due_at);
142
- CREATE INDEX IF NOT EXISTS idx_reminders_completed ON reminders(completed);
143
-
144
- -- Session summaries
145
- CREATE TABLE IF NOT EXISTS session_summaries (
146
- id TEXT PRIMARY KEY,
147
- session_id TEXT NOT NULL,
148
- summary TEXT NOT NULL,
149
- key_decisions TEXT NOT NULL DEFAULT '[]',
150
- key_corrections TEXT NOT NULL DEFAULT '[]',
151
- memories_extracted INTEGER NOT NULL DEFAULT 0,
152
- project TEXT NOT NULL DEFAULT 'global',
153
- created_at INTEGER NOT NULL,
154
- UNIQUE(session_id)
155
- );
156
- CREATE INDEX IF NOT EXISTS idx_summaries_session ON session_summaries(session_id);
157
- CREATE INDEX IF NOT EXISTS idx_summaries_project ON session_summaries(project);
158
- `);
159
- // ── Schema migrations ──────────────────────────────────
160
- // Versioned migration system — each migration runs exactly once.
161
- // New migrations MUST be appended to the end of this array.
162
- db.exec(`
163
- CREATE TABLE IF NOT EXISTS schema_migrations (
164
- version INTEGER PRIMARY KEY,
165
- name TEXT NOT NULL,
166
- applied_at INTEGER NOT NULL
167
- )
168
- `);
169
- const appliedVersions = new Set(db.prepare("SELECT version FROM schema_migrations").all().map(r => r.version));
170
- const hasColumn = (table, column) => {
171
- const cols = db.pragma(`table_info(${table})`);
172
- return cols.some(c => c.name === column);
173
- };
174
- const migrations = [
175
- {
176
- version: 1,
177
- name: "add_scope_column",
178
- up: () => {
179
- if (!hasColumn("memories", "scope")) {
180
- db.exec(`ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'global'`);
181
- }
182
- db.exec(`CREATE INDEX IF NOT EXISTS idx_memories_scope ON memories(scope)`);
183
- },
184
- },
185
- {
186
- version: 2,
187
- name: "add_content_hash",
188
- up: () => {
189
- if (!hasColumn("memories", "content_hash")) {
190
- db.exec(`ALTER TABLE memories ADD COLUMN content_hash TEXT`);
191
- const allRows = db.prepare("SELECT id, content FROM memories").all();
192
- const updateHash = db.prepare("UPDATE memories SET content_hash = ? WHERE id = ?");
193
- for (const row of allRows) {
194
- updateHash.run(createHash("sha256").update(row.content).digest("hex").slice(0, 16), row.id);
195
- }
196
- }
197
- db.exec(`CREATE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash)`);
198
- },
199
- },
200
- {
201
- version: 3,
202
- name: "add_temporal_validity_and_tiers",
203
- up: () => {
204
- if (!hasColumn("memories", "valid_from")) {
205
- db.exec(`ALTER TABLE memories ADD COLUMN valid_from INTEGER`);
206
- db.exec(`ALTER TABLE memories ADD COLUMN valid_until INTEGER`);
207
- db.exec(`ALTER TABLE memories ADD COLUMN tier TEXT NOT NULL DEFAULT 'archival'`);
208
- db.exec(`UPDATE memories SET valid_from = created_at WHERE valid_from IS NULL`);
209
- }
210
- db.exec(`CREATE INDEX IF NOT EXISTS idx_memories_valid_until ON memories(valid_until)`);
211
- db.exec(`CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)`);
212
- },
213
- },
214
- {
215
- version: 4,
216
- name: "add_temporal_relations",
217
- up: () => {
218
- if (!hasColumn("memory_relations", "valid_from")) {
219
- db.exec(`ALTER TABLE memory_relations ADD COLUMN valid_from INTEGER`);
220
- db.exec(`ALTER TABLE memory_relations ADD COLUMN valid_until INTEGER`);
221
- db.exec(`UPDATE memory_relations SET valid_from = created_at WHERE valid_from IS NULL`);
222
- }
223
- },
224
- },
225
- {
226
- version: 5,
227
- name: "add_self_evolving_loop_support",
228
- up: () => {
229
- if (!hasColumn("memories", "utility_score")) {
230
- db.exec("ALTER TABLE memories ADD COLUMN utility_score INTEGER NOT NULL DEFAULT 0");
231
- }
232
- db.exec("CREATE TABLE IF NOT EXISTS synthesis_lineage (synthesis_id TEXT NOT NULL, source_id TEXT NOT NULL, created_at INTEGER NOT NULL, PRIMARY KEY(synthesis_id, source_id), FOREIGN KEY(synthesis_id) REFERENCES memories(id) ON DELETE CASCADE, FOREIGN KEY(source_id) REFERENCES memories(id) ON DELETE CASCADE)");
233
- db.exec("CREATE INDEX IF NOT EXISTS idx_lineage_synthesis ON synthesis_lineage(synthesis_id)");
234
- db.exec("CREATE INDEX IF NOT EXISTS idx_lineage_source ON synthesis_lineage(source_id)");
235
- db.exec("CREATE TABLE IF NOT EXISTS knowledge_gaps (id TEXT PRIMARY KEY, query_pattern TEXT NOT NULL, hit_count INTEGER NOT NULL DEFAULT 1, avg_confidence REAL NOT NULL DEFAULT 0, avg_results INTEGER NOT NULL DEFAULT 0, first_seen INTEGER NOT NULL, last_seen INTEGER NOT NULL, resolved INTEGER NOT NULL DEFAULT 0)");
236
- db.exec("CREATE INDEX IF NOT EXISTS idx_gaps_resolved ON knowledge_gaps(resolved)");
237
- db.exec("CREATE INDEX IF NOT EXISTS idx_gaps_hit_count ON knowledge_gaps(hit_count)");
238
- db.exec("CREATE TABLE IF NOT EXISTS reflection_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL, updated_at INTEGER NOT NULL)");
239
- },
240
- },
241
- ];
242
- const insertMigration = db.prepare("INSERT INTO schema_migrations (version, name, applied_at) VALUES (?, ?, ?)");
243
- for (const m of migrations) {
244
- if (appliedVersions.has(m.version))
245
- continue;
246
- m.up();
247
- insertMigration.run(m.version, m.name, Date.now());
248
- }
249
- const stmts = {
250
- insert: db.prepare(`
251
- INSERT INTO memories (id, content, type, tags, confidence, access_count, created_at, last_accessed, source, embedding, scope, content_hash, valid_from, valid_until, tier)
252
- VALUES (?, ?, ?, ?, ?, 0, ?, ?, ?, ?, ?, ?, ?, ?, ?)
253
- `),
254
- getById: db.prepare(`SELECT * FROM memories WHERE id = ?`),
255
- searchByType: db.prepare(`SELECT * FROM memories WHERE type = ? ORDER BY last_accessed DESC`),
256
- searchByTag: db.prepare(`SELECT * FROM memories WHERE tags LIKE ? ORDER BY last_accessed DESC`),
257
- getAllWithEmbeddings: db.prepare(`SELECT * FROM memories WHERE embedding IS NOT NULL`),
258
- getRecentWithEmbeddings: db.prepare(`SELECT * FROM memories WHERE embedding IS NOT NULL ORDER BY last_accessed DESC LIMIT ?`),
259
- getAll: db.prepare(`SELECT * FROM memories ORDER BY last_accessed DESC`),
260
- updateConfidence: db.prepare(`
261
- UPDATE memories SET confidence = ?, access_count = access_count + 1, last_accessed = ? WHERE id = ?
262
- `),
263
- updateEmbedding: db.prepare(`UPDATE memories SET embedding = ? WHERE id = ?`),
264
- touchAccess: db.prepare(`
265
- UPDATE memories SET access_count = access_count + 1, last_accessed = ? WHERE id = ?
266
- `),
267
- deleteMemory: db.prepare(`DELETE FROM memories WHERE id = ?`),
268
- countAll: db.prepare(`SELECT COUNT(*) as count FROM memories`),
269
- countHighConf: db.prepare(`SELECT COUNT(*) as count FROM memories WHERE confidence >= 0.8`),
270
- countMedConf: db.prepare(`SELECT COUNT(*) as count FROM memories WHERE confidence >= 0.5 AND confidence < 0.8`),
271
- countLowConf: db.prepare(`SELECT COUNT(*) as count FROM memories WHERE confidence < 0.5`),
272
- countWithEmbeddings: db.prepare(`SELECT COUNT(*) as count FROM memories WHERE embedding IS NOT NULL`),
273
- countByType: db.prepare(`SELECT type, COUNT(*) as count FROM memories GROUP BY type`),
274
- listTables: db.prepare(`SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name`),
275
- searchByScope: db.prepare(`SELECT * FROM memories WHERE scope = ? ORDER BY last_accessed DESC`),
276
- getAllForProject: db.prepare(`SELECT * FROM memories WHERE scope = 'global' OR scope = ? ORDER BY last_accessed DESC`),
277
- updateContent: db.prepare(`UPDATE memories SET content = ?, last_accessed = ? WHERE id = ?`),
278
- updateType: db.prepare(`UPDATE memories SET type = ?, last_accessed = ? WHERE id = ?`),
279
- updateTags: db.prepare(`UPDATE memories SET tags = ?, last_accessed = ? WHERE id = ?`),
280
- getByDateRange: db.prepare(`SELECT * FROM memories WHERE created_at BETWEEN ? AND ? ORDER BY created_at DESC`),
281
- getSince: db.prepare(`SELECT * FROM memories WHERE created_at >= ? ORDER BY created_at DESC`),
282
- // Log
283
- insertLog: db.prepare(`
284
- INSERT INTO conversation_log (id, session_id, role, content, timestamp, project, metadata)
285
- VALUES (?, ?, ?, ?, ?, ?, ?)
286
- `),
287
- getLogBySession: db.prepare(`SELECT * FROM conversation_log WHERE session_id = ? ORDER BY timestamp ASC`),
288
- getRecentLog: db.prepare(`SELECT * FROM conversation_log ORDER BY timestamp DESC LIMIT ?`),
289
- getRecentLogByProject: db.prepare(`SELECT * FROM conversation_log WHERE project = ? ORDER BY timestamp DESC LIMIT ?`),
290
- deleteLogBefore: db.prepare(`DELETE FROM conversation_log WHERE timestamp < ?`),
291
- countLog: db.prepare(`SELECT COUNT(*) as count FROM conversation_log`),
292
- // Versions
293
- insertVersion: db.prepare(`
294
- INSERT INTO memory_versions (version_id, memory_id, content, confidence, edited_at, reason)
295
- VALUES (?, ?, ?, ?, ?, ?)
296
- `),
297
- getVersions: db.prepare(`SELECT * FROM memory_versions WHERE memory_id = ? ORDER BY edited_at DESC`),
298
- // Relations
299
- insertRelation: db.prepare(`
300
- INSERT OR REPLACE INTO memory_relations (id, from_id, to_id, relationship_type, strength, created_at, valid_from)
301
- VALUES (?, ?, ?, ?, ?, ?, ?)
302
- `),
303
- getRelationsFrom: db.prepare(`SELECT * FROM memory_relations WHERE from_id = ?`),
304
- getRelationsTo: db.prepare(`SELECT * FROM memory_relations WHERE to_id = ?`),
305
- deleteRelation: db.prepare(`DELETE FROM memory_relations WHERE id = ?`),
306
- // Reminders
307
- insertReminder: db.prepare("INSERT INTO reminders (id, content, due_at, completed, created_at, scope) VALUES (?, ?, ?, 0, ?, ?)"),
308
- listReminders: db.prepare("SELECT * FROM reminders WHERE completed = 0 ORDER BY due_at ASC NULLS LAST"),
309
- listAllReminders: db.prepare("SELECT * FROM reminders ORDER BY due_at ASC NULLS LAST"),
310
- listRemindersByScope: db.prepare("SELECT * FROM reminders WHERE completed = 0 AND (scope = 'global' OR scope = ?) ORDER BY due_at ASC NULLS LAST"),
311
- listAllRemindersByScope: db.prepare("SELECT * FROM reminders WHERE (scope = 'global' OR scope = ?) ORDER BY due_at ASC NULLS LAST"),
312
- completeReminder: db.prepare("UPDATE reminders SET completed = 1 WHERE id = ?"),
313
- // Content hash dedup
314
- findByContentHash: db.prepare("SELECT * FROM memories WHERE content_hash = ? LIMIT 1"),
315
- // ID resolution via SQL prefix match (avoids full table scan)
316
- resolveIdPrefix: db.prepare("SELECT id FROM memories WHERE id LIKE ? LIMIT 2"),
317
- resolveReminderIdPrefix: db.prepare("SELECT id FROM reminders WHERE id LIKE ? LIMIT 2"),
318
- // Batch relation loading
319
- getAllRelations: db.prepare("SELECT * FROM memory_relations ORDER BY created_at DESC"),
320
- // Temporal validity
321
- getValidMemories: db.prepare(`SELECT * FROM memories WHERE (valid_until IS NULL OR valid_until > ?) ORDER BY last_accessed DESC`),
322
- expireMemory: db.prepare(`UPDATE memories SET valid_until = ? WHERE id = ?`),
323
- updateTier: db.prepare(`UPDATE memories SET tier = ?, last_accessed = ? WHERE id = ?`),
324
- getByTier: db.prepare(`SELECT * FROM memories WHERE tier = ? ORDER BY last_accessed DESC`),
325
- getByTierAndScope: db.prepare(`SELECT * FROM memories WHERE tier = ? AND (scope = 'global' OR scope = ?) ORDER BY last_accessed DESC`),
326
- // Session summaries
327
- insertSummary: db.prepare(`INSERT OR REPLACE INTO session_summaries (id, session_id, summary, key_decisions, key_corrections, memories_extracted, project, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`),
328
- getSummaryBySession: db.prepare(`SELECT * FROM session_summaries WHERE session_id = ?`),
329
- getRecentSummaries: db.prepare(`SELECT * FROM session_summaries WHERE project = ? ORDER BY created_at DESC LIMIT ?`),
330
- // Temporal relations
331
- expireRelation: db.prepare(`UPDATE memory_relations SET valid_until = ? WHERE id = ?`),
332
- getValidRelations: db.prepare(`SELECT * FROM memory_relations WHERE (valid_until IS NULL OR valid_until > ?) ORDER BY created_at DESC`),
333
- // Self-evolving loop
334
- insertLineage: db.prepare(`INSERT OR IGNORE INTO synthesis_lineage (synthesis_id, source_id, created_at) VALUES (?, ?, ?)`),
335
- getLineageSources: db.prepare(`SELECT source_id FROM synthesis_lineage WHERE synthesis_id = ?`),
336
- getLineageBySrc: db.prepare(`SELECT DISTINCT synthesis_id FROM synthesis_lineage WHERE source_id = ?`),
337
- findGapByPattern: db.prepare(`SELECT * FROM knowledge_gaps WHERE query_pattern = ? AND resolved = 0 LIMIT 1`),
338
- insertGap: db.prepare(`INSERT INTO knowledge_gaps (id, query_pattern, hit_count, avg_confidence, avg_results, first_seen, last_seen, resolved) VALUES (?, ?, 1, ?, ?, ?, ?, 0)`),
339
- updateGap: db.prepare(`UPDATE knowledge_gaps SET avg_confidence = (avg_confidence * hit_count + ?) / (hit_count + 1), avg_results = (avg_results * hit_count + ?) / (hit_count + 1), hit_count = hit_count + 1, last_seen = ? WHERE id = ?`),
340
- getActiveGaps: db.prepare(`SELECT * FROM knowledge_gaps WHERE resolved = 0 ORDER BY hit_count DESC LIMIT ?`),
341
- resolveGap: db.prepare(`UPDATE knowledge_gaps SET resolved = 1 WHERE id = ?`),
342
- bumpUtility: db.prepare(`UPDATE memories SET utility_score = utility_score + 1 WHERE id = ?`),
343
- getMeta: db.prepare(`SELECT value FROM reflection_meta WHERE key = ?`),
344
- setMeta: db.prepare(`INSERT OR REPLACE INTO reflection_meta (key, value, updated_at) VALUES (?, ?, ?)`),
345
- };
346
- // Keep FTS index in sync via triggers.
347
- // External-content FTS5 tables require explicit rowid in all operations.
348
- // Drop and recreate triggers to pick up the rowid fix for existing DBs.
349
- db.exec(`
350
- DROP TRIGGER IF EXISTS memories_ai;
351
- DROP TRIGGER IF EXISTS memories_ad;
352
- DROP TRIGGER IF EXISTS memories_au;
353
- DROP TRIGGER IF EXISTS log_ai;
354
- DROP TRIGGER IF EXISTS log_ad;
355
- DROP TRIGGER IF EXISTS log_au;
356
-
357
- CREATE TRIGGER memories_ai AFTER INSERT ON memories BEGIN
358
- INSERT INTO memories_fts(rowid, id, content, tags) VALUES (new.rowid, new.id, new.content, new.tags);
359
- END;
360
- CREATE TRIGGER memories_ad AFTER DELETE ON memories BEGIN
361
- INSERT INTO memories_fts(memories_fts, rowid, id, content, tags) VALUES ('delete', old.rowid, old.id, old.content, old.tags);
362
- END;
363
- CREATE TRIGGER memories_au AFTER UPDATE ON memories BEGIN
364
- INSERT INTO memories_fts(memories_fts, rowid, id, content, tags) VALUES ('delete', old.rowid, old.id, old.content, old.tags);
365
- INSERT INTO memories_fts(rowid, id, content, tags) VALUES (new.rowid, new.id, new.content, new.tags);
366
- END;
367
- CREATE TRIGGER log_ai AFTER INSERT ON conversation_log BEGIN
368
- INSERT INTO log_fts(rowid, id, content) VALUES (new.rowid, new.id, new.content);
369
- END;
370
- CREATE TRIGGER log_ad AFTER DELETE ON conversation_log BEGIN
371
- INSERT INTO log_fts(log_fts, rowid, id, content) VALUES ('delete', old.rowid, old.id, old.content);
372
- END;
373
- CREATE TRIGGER log_au AFTER UPDATE ON conversation_log BEGIN
374
- INSERT INTO log_fts(log_fts, rowid, id, content) VALUES ('delete', old.rowid, old.id, old.content);
375
- INSERT INTO log_fts(rowid, id, content) VALUES (new.rowid, new.id, new.content);
376
- END;
377
- `);
378
- return {
379
- insertMemory(input) {
380
- const id = randomUUID();
381
- const now = Date.now();
382
- const embeddingBuffer = input.embedding
383
- ? Buffer.from(input.embedding.buffer, input.embedding.byteOffset, input.embedding.byteLength)
384
- : null;
385
- const contentHash = createHash("sha256").update(input.content).digest("hex").slice(0, 16);
386
- stmts.insert.run(id, input.content, input.type, JSON.stringify(input.tags), input.confidence, now, now, input.source, embeddingBuffer, input.scope, contentHash, input.validFrom ?? now, input.validUntil ?? null, input.tier ?? "archival");
387
- return id;
388
- },
389
- findByContentHash(content) {
390
- const hash = createHash("sha256").update(content).digest("hex").slice(0, 16);
391
- const row = stmts.findByContentHash.get(hash);
392
- return row ? rowToMemory(row) : null;
393
- },
394
- getById(id) {
395
- const row = stmts.getById.get(id);
396
- return row ? rowToMemory(row) : null;
397
- },
398
- searchByType(type) {
399
- const rows = stmts.searchByType.all(type);
400
- return rows.map(rowToMemory);
401
- },
402
- searchByTag(tag) {
403
- const pattern = `%"${tag}"%`;
404
- const rows = stmts.searchByTag.all(pattern);
405
- return rows.map(rowToMemory);
406
- },
407
- getAllWithEmbeddings() {
408
- const rows = stmts.getAllWithEmbeddings.all();
409
- return rows.map(rowToMemory);
410
- },
411
- getRecentWithEmbeddings(limit) {
412
- const rows = stmts.getRecentWithEmbeddings.all(limit);
413
- return rows.map(rowToMemory);
414
- },
415
- getAll() {
416
- const rows = stmts.getAll.all();
417
- return rows.map(rowToMemory);
418
- },
419
- updateConfidence(id, confidence) {
420
- stmts.updateConfidence.run(confidence, Date.now(), id);
421
- },
422
- updateEmbedding(id, embedding) {
423
- const buffer = Buffer.from(embedding.buffer, embedding.byteOffset, embedding.byteLength);
424
- stmts.updateEmbedding.run(buffer, id);
425
- },
426
- touchAccess(id) {
427
- stmts.touchAccess.run(Date.now(), id);
428
- },
429
- deleteMemory(id) {
430
- stmts.deleteMemory.run(id);
431
- },
432
- getStats() {
433
- const total = stmts.countAll.get().count;
434
- const typeCounts = stmts.countByType.all();
435
- const byType = {};
436
- for (const row of typeCounts) {
437
- byType[row.type] = row.count;
438
- }
439
- return { total, byType };
440
- },
441
- getConfidenceStats() {
442
- return {
443
- high: stmts.countHighConf.get().count,
444
- medium: stmts.countMedConf.get().count,
445
- low: stmts.countLowConf.get().count,
446
- };
447
- },
448
- getEmbeddingCount() {
449
- return stmts.countWithEmbeddings.get().count;
450
- },
451
- searchByScope(scope) {
452
- const rows = stmts.searchByScope.all(scope);
453
- return rows.map(rowToMemory);
454
- },
455
- getAllForProject(project) {
456
- const rows = stmts.getAllForProject.all(project);
457
- return rows.map(rowToMemory);
458
- },
459
- listTables() {
460
- const rows = stmts.listTables.all();
461
- return rows.map((r) => r.name);
462
- },
463
- close() {
464
- db.close();
465
- },
466
- // ── Raw log ──────────────────────────────────────────────
467
- appendLog(entry) {
468
- const id = randomUUID();
469
- stmts.insertLog.run(id, entry.sessionId, entry.role, entry.content, Date.now(), entry.project, JSON.stringify(entry.metadata ?? {}));
470
- return id;
471
- },
472
- getLogBySession(sessionId) {
473
- const rows = stmts.getLogBySession.all(sessionId);
474
- return rows.map(r => ({
475
- id: r.id,
476
- sessionId: r.session_id,
477
- role: r.role,
478
- content: r.content,
479
- timestamp: r.timestamp,
480
- project: r.project,
481
- metadata: JSON.parse(r.metadata),
482
- }));
483
- },
484
- searchLog(query, limit = 20) {
485
- const mapRow = (r) => ({
486
- id: r.id,
487
- sessionId: r.session_id,
488
- role: r.role,
489
- content: r.content,
490
- timestamp: r.timestamp,
491
- project: r.project,
492
- metadata: JSON.parse(r.metadata),
493
- });
494
- try {
495
- const stmt = db.prepare(`
496
- SELECT conversation_log.* FROM log_fts
497
- JOIN conversation_log ON conversation_log.id = log_fts.id
498
- WHERE log_fts.content MATCH ?
499
- ORDER BY rank
500
- LIMIT ?
501
- `);
502
- const rows = stmt.all(query, limit);
503
- return rows.map(mapRow);
504
- }
505
- catch (error) {
506
- // FTS5 may fail on special characters — fall back to LIKE
507
- console.error("[amem] FTS5 log search failed, falling back to LIKE:", error instanceof Error ? error.message : String(error));
508
- const escaped = query.replace(/[%_]/g, ch => "\\" + ch);
509
- const pattern = `%${escaped}%`;
510
- const stmt = db.prepare(`
511
- SELECT * FROM conversation_log WHERE content LIKE ? ESCAPE '\\' ORDER BY timestamp DESC LIMIT ?
512
- `);
513
- const rows = stmt.all(pattern, limit);
514
- return rows.map(mapRow);
515
- }
516
- },
517
- getRecentLog(limit, project) {
518
- const rows = (project
519
- ? stmts.getRecentLogByProject.all(project, limit)
520
- : stmts.getRecentLog.all(limit));
521
- return rows.map(r => ({
522
- id: r.id,
523
- sessionId: r.session_id,
524
- role: r.role,
525
- content: r.content,
526
- timestamp: r.timestamp,
527
- project: r.project,
528
- metadata: JSON.parse(r.metadata),
529
- }));
530
- },
531
- deleteLogBefore(timestamp) {
532
- const result = stmts.deleteLogBefore.run(timestamp);
533
- return result.changes;
534
- },
535
- getLogCount() {
536
- return stmts.countLog.get().count;
537
- },
538
- // ── Versioning ──────────────────────────────────────────
539
- snapshotVersion(memoryId, reason) {
540
- const mem = this.getById(memoryId);
541
- if (!mem)
542
- return;
543
- stmts.insertVersion.run(randomUUID(), mem.id, mem.content, mem.confidence, Date.now(), reason);
544
- },
545
- getVersionHistory(memoryId) {
546
- const rows = stmts.getVersions.all(memoryId);
547
- return rows.map(r => ({
548
- versionId: r.version_id,
549
- memoryId: r.memory_id,
550
- content: r.content,
551
- confidence: r.confidence,
552
- editedAt: r.edited_at,
553
- reason: r.reason,
554
- }));
555
- },
556
- patchMemory(id, patch) {
557
- const mem = this.getById(id);
558
- if (!mem)
559
- return false;
560
- // Snapshot before patching (can be skipped for batch operations like restore)
561
- if (!patch.skipSnapshot) {
562
- this.snapshotVersion(id, `before patch: ${patch.reason}`);
563
- }
564
- const now = Date.now();
565
- switch (patch.field) {
566
- case "content":
567
- stmts.updateContent.run(patch.value, now, id);
568
- break;
569
- case "confidence":
570
- stmts.updateConfidence.run(patch.value, now, id);
571
- break;
572
- case "tags":
573
- stmts.updateTags.run(JSON.stringify(patch.value), now, id);
574
- break;
575
- case "type":
576
- stmts.updateType.run(patch.value, now, id);
577
- break;
578
- default:
579
- return false;
580
- }
581
- return true;
582
- },
583
- // ── Relations ────────────────────────────────────────────
584
- addRelation(fromId, toId, type, strength = 0.8) {
585
- const id = randomUUID();
586
- const now = Date.now();
587
- stmts.insertRelation.run(id, fromId, toId, type, strength, now, now);
588
- return id;
589
- },
590
- getRelations(memoryId) {
591
- const from = stmts.getRelationsFrom.all(memoryId);
592
- const to = stmts.getRelationsTo.all(memoryId);
593
- return [...from, ...to].map(rowToRelation);
594
- },
595
- removeRelation(relationId) {
596
- stmts.deleteRelation.run(relationId);
597
- },
598
- getRelatedMemories(memoryId) {
599
- const relations = this.getRelations(memoryId);
600
- const ids = relations.map(r => r.fromId === memoryId ? r.toId : r.fromId);
601
- return ids
602
- .map(id => this.getById(id))
603
- .filter((m) => m !== null);
604
- },
605
- // ── Temporal queries ─────────────────────────────────────
606
- getMemoriesByDateRange(from, to) {
607
- const rows = stmts.getByDateRange.all(from, to);
608
- return rows.map(rowToMemory);
609
- },
610
- getMemoriesSince(timestamp) {
611
- const rows = stmts.getSince.all(timestamp);
612
- return rows.map(rowToMemory);
613
- },
614
- // ── Reminders ────────────────────────────────────────────
615
- insertReminder(content, dueAt, scope) {
616
- const id = randomUUID();
617
- stmts.insertReminder.run(id, content, dueAt, Date.now(), scope);
618
- return id;
619
- },
620
- listReminders(includeCompleted = false, scope) {
621
- const rows = (scope
622
- ? (includeCompleted ? stmts.listAllRemindersByScope.all(scope) : stmts.listRemindersByScope.all(scope))
623
- : (includeCompleted ? stmts.listAllReminders.all() : stmts.listReminders.all()));
624
- return rows.map(r => ({
625
- id: r.id, content: r.content, dueAt: r.due_at,
626
- completed: r.completed === 1, createdAt: r.created_at, scope: r.scope,
627
- }));
628
- },
629
- checkReminders() {
630
- const reminders = this.listReminders();
631
- const todayStart = new Date();
632
- todayStart.setHours(0, 0, 0, 0);
633
- const todayEnd = new Date();
634
- todayEnd.setHours(23, 59, 59, 999);
635
- const weekFromNow = Date.now() + 7 * 24 * 60 * 60 * 1000;
636
- return reminders
637
- .filter(r => r.dueAt !== null)
638
- .map(r => {
639
- let status;
640
- if (r.dueAt < todayStart.getTime())
641
- status = "overdue";
642
- else if (r.dueAt <= todayEnd.getTime())
643
- status = "today";
644
- else
645
- status = "upcoming";
646
- return { id: r.id, content: r.content, dueAt: r.dueAt, status, scope: r.scope };
647
- })
648
- .filter(r => r.status === "overdue" || r.status === "today" || r.dueAt <= weekFromNow);
649
- },
650
- completeReminder(id) {
651
- const result = stmts.completeReminder.run(id);
652
- return result.changes > 0;
653
- },
654
- // ── Full-text search ─────────────────────────────────────
655
- fullTextSearch(query, limit = 20, scopeProject) {
656
- try {
657
- // Sanitize query for FTS5: quote each token to prevent column prefix
658
- // interpretation (e.g. "partner:" being parsed as column "partner")
659
- const sanitized = query
660
- .replace(/["\u201C\u201D]/g, "") // strip existing quotes
661
- .split(/\s+/)
662
- .filter(Boolean)
663
- .map(token => `"${token.replace(/:/g, "")}"`) // quote tokens, strip colons
664
- .join(" ");
665
- if (!sanitized)
666
- throw new Error("empty query after sanitization");
667
- if (scopeProject) {
668
- const stmt = db.prepare(`
669
- SELECT memories.* FROM memories_fts
670
- JOIN memories ON memories.id = memories_fts.id
671
- WHERE memories_fts MATCH ? AND (memories.scope = 'global' OR memories.scope = ?)
672
- ORDER BY rank
673
- LIMIT ?
674
- `);
675
- const rows = stmt.all(sanitized, scopeProject, limit);
676
- return rows.map(rowToMemory);
677
- }
678
- const stmt = db.prepare(`
679
- SELECT memories.* FROM memories_fts
680
- JOIN memories ON memories.id = memories_fts.id
681
- WHERE memories_fts MATCH ?
682
- ORDER BY rank
683
- LIMIT ?
684
- `);
685
- const rows = stmt.all(sanitized, limit);
686
- return rows.map(rowToMemory);
687
- }
688
- catch (error) {
689
- // FTS may fail on complex queries — fall back to LIKE
690
- console.error("[amem] FTS5 memory search failed, falling back to LIKE:", error instanceof Error ? error.message : String(error));
691
- const escaped = query.replace(/[%_]/g, ch => "\\" + ch);
692
- const pattern = `%${escaped}%`;
693
- if (scopeProject) {
694
- const stmt = db.prepare(`
695
- SELECT * FROM memories WHERE (content LIKE ? ESCAPE '\\' OR tags LIKE ? ESCAPE '\\')
696
- AND (scope = 'global' OR scope = ?) ORDER BY last_accessed DESC LIMIT ?
697
- `);
698
- const rows = stmt.all(pattern, pattern, scopeProject, limit);
699
- return rows.map(rowToMemory);
700
- }
701
- const stmt = db.prepare(`
702
- SELECT * FROM memories WHERE content LIKE ? ESCAPE '\\' OR tags LIKE ? ESCAPE '\\' ORDER BY last_accessed DESC LIMIT ?
703
- `);
704
- const rows = stmt.all(pattern, pattern, limit);
705
- return rows.map(rowToMemory);
706
- }
707
- },
708
- // ── Utilities ────────────────────────────────────────────
709
- transaction(fn) {
710
- runTransaction(fn);
711
- },
712
- resolveId(partialId) {
713
- if (partialId.length >= 36) {
714
- const mem = this.getById(partialId);
715
- return mem ? partialId : null;
716
- }
717
- const rows = stmts.resolveIdPrefix.all(`${partialId}%`);
718
- if (rows.length === 1)
719
- return rows[0].id;
720
- return null;
721
- },
722
- resolveReminderId(partialId) {
723
- if (partialId.length >= 36)
724
- return partialId;
725
- const rows = stmts.resolveReminderIdPrefix.all(`${partialId}%`);
726
- if (rows.length === 1)
727
- return rows[0].id;
728
- return null;
729
- },
730
- getAllRelations() {
731
- const rows = stmts.getAllRelations.all();
732
- return rows.map(rowToRelation);
733
- },
734
- // ── Temporal validity ───────────────────────────────────
735
- expireMemory(id, timestamp) {
736
- stmts.expireMemory.run(timestamp ?? Date.now(), id);
737
- },
738
- getValidMemories(asOf) {
739
- const rows = stmts.getValidMemories.all(asOf ?? Date.now());
740
- return rows.map(rowToMemory);
741
- },
742
- updateTier(id, tier) {
743
- stmts.updateTier.run(tier, Date.now(), id);
744
- },
745
- getByTier(tier, scope) {
746
- const rows = scope
747
- ? stmts.getByTierAndScope.all(tier, scope)
748
- : stmts.getByTier.all(tier);
749
- return rows.map(rowToMemory);
750
- },
751
- // ── Session summaries ───────────────────────────────────
752
- insertSummary(input) {
753
- const id = randomUUID();
754
- stmts.insertSummary.run(id, input.sessionId, input.summary, JSON.stringify(input.keyDecisions), JSON.stringify(input.keyCorrections), input.memoriesExtracted, input.project, Date.now());
755
- return id;
756
- },
757
- getSummaryBySession(sessionId) {
758
- const row = stmts.getSummaryBySession.get(sessionId);
759
- return row ? rowToSummary(row) : null;
760
- },
761
- getRecentSummaries(project, limit = 10) {
762
- const rows = stmts.getRecentSummaries.all(project, limit);
763
- return rows.map(rowToSummary);
764
- },
765
- // ── Temporal relations ──────────────────────────────────
766
- expireRelation(relationId, timestamp) {
767
- stmts.expireRelation.run(timestamp ?? Date.now(), relationId);
768
- },
769
- getValidRelations(asOf) {
770
- const rows = stmts.getValidRelations.all(asOf ?? Date.now());
771
- return rows.map(rowToRelation);
772
- },
773
- // ── Self-evolving loop ─────────────────────────────────
774
- insertSynthesisLineage(synthesisId, sourceIds) {
775
- const now = Date.now();
776
- for (const sourceId of sourceIds) {
777
- stmts.insertLineage.run(synthesisId, sourceId, now);
778
- }
779
- },
780
- getSynthesisSources(synthesisId) {
781
- const rows = stmts.getLineageSources.all(synthesisId);
782
- return rows.map(r => r.source_id);
783
- },
784
- hasAnySynthesis(sourceIds) {
785
- for (const id of sourceIds) {
786
- const row = stmts.getLineageBySrc.get(id);
787
- if (row)
788
- return true;
789
- }
790
- return false;
791
- },
792
- upsertKnowledgeGap(queryPattern, avgConfidence, resultCount) {
793
- const now = Date.now();
794
- const existing = stmts.findGapByPattern.get(queryPattern);
795
- if (existing) {
796
- stmts.updateGap.run(avgConfidence, resultCount, now, existing.id);
797
- return existing.id;
798
- }
799
- const id = randomUUID();
800
- stmts.insertGap.run(id, queryPattern, avgConfidence, resultCount, now, now);
801
- return id;
802
- },
803
- getActiveKnowledgeGaps(limit = 10) {
804
- const rows = stmts.getActiveGaps.all(limit);
805
- return rows.map(r => ({
806
- id: r.id,
807
- queryPattern: r.query_pattern,
808
- hitCount: r.hit_count,
809
- avgConfidence: r.avg_confidence,
810
- avgResults: r.avg_results,
811
- firstSeen: r.first_seen,
812
- lastSeen: r.last_seen,
813
- resolved: r.resolved === 1,
814
- }));
815
- },
816
- resolveKnowledgeGap(id) {
817
- stmts.resolveGap.run(id);
818
- },
819
- bumpUtilityScore(id) {
820
- stmts.bumpUtility.run(id);
821
- },
822
- getReflectionMeta(key) {
823
- const row = stmts.getMeta.get(key);
824
- return row?.value ?? null;
825
- },
826
- setReflectionMeta(key, value) {
827
- stmts.setMeta.run(key, value, Date.now());
828
- },
829
- };
830
- }
831
- //# sourceMappingURL=database.js.map