mnemon-mcp 1.0.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 (104) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/CONTRIBUTING.md +59 -0
  3. package/LICENSE +21 -0
  4. package/README.md +434 -0
  5. package/SECURITY.md +29 -0
  6. package/config.example.json +52 -0
  7. package/dist/.tsbuildinfo +1 -0
  8. package/dist/db.d.ts +16 -0
  9. package/dist/db.d.ts.map +1 -0
  10. package/dist/db.js +360 -0
  11. package/dist/db.js.map +1 -0
  12. package/dist/embedder.d.ts +22 -0
  13. package/dist/embedder.d.ts.map +1 -0
  14. package/dist/embedder.js +109 -0
  15. package/dist/embedder.js.map +1 -0
  16. package/dist/import/cli.d.ts +12 -0
  17. package/dist/import/cli.d.ts.map +1 -0
  18. package/dist/import/cli.js +105 -0
  19. package/dist/import/cli.js.map +1 -0
  20. package/dist/import/config-loader.d.ts +29 -0
  21. package/dist/import/config-loader.d.ts.map +1 -0
  22. package/dist/import/config-loader.js +140 -0
  23. package/dist/import/config-loader.js.map +1 -0
  24. package/dist/import/kb-config.d.ts +27 -0
  25. package/dist/import/kb-config.d.ts.map +1 -0
  26. package/dist/import/kb-config.js +10 -0
  27. package/dist/import/kb-config.js.map +1 -0
  28. package/dist/import/kb-import.d.ts +45 -0
  29. package/dist/import/kb-import.d.ts.map +1 -0
  30. package/dist/import/kb-import.js +285 -0
  31. package/dist/import/kb-import.js.map +1 -0
  32. package/dist/import/md-parser.d.ts +35 -0
  33. package/dist/import/md-parser.d.ts.map +1 -0
  34. package/dist/import/md-parser.js +104 -0
  35. package/dist/import/md-parser.js.map +1 -0
  36. package/dist/index-http.d.ts +12 -0
  37. package/dist/index-http.d.ts.map +1 -0
  38. package/dist/index-http.js +202 -0
  39. package/dist/index-http.js.map +1 -0
  40. package/dist/index.d.ts +9 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +56 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/server.d.ts +16 -0
  45. package/dist/server.d.ts.map +1 -0
  46. package/dist/server.js +341 -0
  47. package/dist/server.js.map +1 -0
  48. package/dist/stemmer.d.ts +29 -0
  49. package/dist/stemmer.d.ts.map +1 -0
  50. package/dist/stemmer.js +68 -0
  51. package/dist/stemmer.js.map +1 -0
  52. package/dist/stop-words.d.ts +12 -0
  53. package/dist/stop-words.d.ts.map +1 -0
  54. package/dist/stop-words.js +112 -0
  55. package/dist/stop-words.js.map +1 -0
  56. package/dist/tools/memory-add.d.ts +15 -0
  57. package/dist/tools/memory-add.d.ts.map +1 -0
  58. package/dist/tools/memory-add.js +120 -0
  59. package/dist/tools/memory-add.js.map +1 -0
  60. package/dist/tools/memory-delete.d.ts +10 -0
  61. package/dist/tools/memory-delete.d.ts.map +1 -0
  62. package/dist/tools/memory-delete.js +39 -0
  63. package/dist/tools/memory-delete.js.map +1 -0
  64. package/dist/tools/memory-export.d.ts +10 -0
  65. package/dist/tools/memory-export.d.ts.map +1 -0
  66. package/dist/tools/memory-export.js +102 -0
  67. package/dist/tools/memory-export.js.map +1 -0
  68. package/dist/tools/memory-health.d.ts +11 -0
  69. package/dist/tools/memory-health.d.ts.map +1 -0
  70. package/dist/tools/memory-health.js +79 -0
  71. package/dist/tools/memory-health.js.map +1 -0
  72. package/dist/tools/memory-inspect.d.ts +10 -0
  73. package/dist/tools/memory-inspect.d.ts.map +1 -0
  74. package/dist/tools/memory-inspect.js +139 -0
  75. package/dist/tools/memory-inspect.js.map +1 -0
  76. package/dist/tools/memory-search.d.ts +16 -0
  77. package/dist/tools/memory-search.d.ts.map +1 -0
  78. package/dist/tools/memory-search.js +459 -0
  79. package/dist/tools/memory-search.js.map +1 -0
  80. package/dist/tools/memory-update.d.ts +11 -0
  81. package/dist/tools/memory-update.d.ts.map +1 -0
  82. package/dist/tools/memory-update.js +142 -0
  83. package/dist/tools/memory-update.js.map +1 -0
  84. package/dist/tools/style-extract.d.ts +40 -0
  85. package/dist/tools/style-extract.d.ts.map +1 -0
  86. package/dist/tools/style-extract.js +43 -0
  87. package/dist/tools/style-extract.js.map +1 -0
  88. package/dist/tools/utils.d.ts +28 -0
  89. package/dist/tools/utils.d.ts.map +1 -0
  90. package/dist/tools/utils.js +33 -0
  91. package/dist/tools/utils.js.map +1 -0
  92. package/dist/types.d.ts +216 -0
  93. package/dist/types.d.ts.map +1 -0
  94. package/dist/types.js +6 -0
  95. package/dist/types.js.map +1 -0
  96. package/dist/validation.d.ts +122 -0
  97. package/dist/validation.d.ts.map +1 -0
  98. package/dist/validation.js +96 -0
  99. package/dist/validation.js.map +1 -0
  100. package/dist/vector.d.ts +30 -0
  101. package/dist/vector.d.ts.map +1 -0
  102. package/dist/vector.js +90 -0
  103. package/dist/vector.js.map +1 -0
  104. package/package.json +75 -0
@@ -0,0 +1,459 @@
1
+ /**
2
+ * memory_search — FTS5-backed search with layer/entity/date/scope filtering.
3
+ *
4
+ * Default mode: 'fts' — tokenize query into words, build FTS5 AND query.
5
+ * Scores via FTS5 bm25(), normalized to 0–1 range.
6
+ * Superseded entries excluded unless include_superseded=true.
7
+ *
8
+ * Search modes:
9
+ * fts — FTS5 tokenized search (default)
10
+ * exact — LIKE substring match, fixed score 1.0
11
+ */
12
+ import { isStopWord } from "../stop-words.js";
13
+ import { stemWord } from "../stemmer.js";
14
+ import { knnSearch, isVecLoaded } from "../vector.js";
15
+ const DEFAULT_LIMIT = 10;
16
+ const SNIPPET_TOKENS = 64;
17
+ /** Resolve entity alias to canonical name. Returns the input if no alias exists. */
18
+ function resolveEntityName(db, name) {
19
+ const row = db.prepare(`SELECT canonical FROM entity_aliases WHERE alias = ?`).get(name);
20
+ return row ? row.canonical : name;
21
+ }
22
+ /** Escape FTS5 special characters and trailing punctuation to prevent syntax errors */
23
+ function escapeFtsToken(token) {
24
+ // Remove FTS5 query syntax chars + general punctuation from natural language queries
25
+ // Note: hyphens (-) NOT stripped — unicode61 tokenizer uses them as separators
26
+ return token.replace(/["^*():?!.,;—–\/]/g, "").replace(/\b(AND|OR|NOT|NEAR)\b/gi, "");
27
+ }
28
+ /**
29
+ * Build FTS5 MATCH query from user query string.
30
+ * 1. Splits on whitespace
31
+ * 2. Removes stop words (Russian + English) to avoid over-restrictive AND queries
32
+ * 3. Escapes FTS5 special chars
33
+ * 4. Applies prefix matching for tokens ≥ 4 chars (morphological variants)
34
+ *
35
+ * If ALL tokens are stop words, falls back to using original tokens
36
+ * (graceful degradation — better to search with stop words than return nothing).
37
+ */
38
+ /** Convert a single token into an FTS5 prefix term: escape, stem, quote */
39
+ function tokenToFts(token) {
40
+ const escaped = escapeFtsToken(token);
41
+ if (!escaped)
42
+ return "";
43
+ const stemmed = stemWord(escaped);
44
+ const stem = stemmed.length < escaped.length ? stemmed : escaped;
45
+ if (stem.length >= 2)
46
+ return `"${stem}"*`;
47
+ if (escaped.length >= 2)
48
+ return `"${escaped}"*`;
49
+ return `"${escaped}"`;
50
+ }
51
+ function buildFtsQuery(query, operator = "AND") {
52
+ // Split on whitespace, em/en-dash, AND hyphens (FTS5 unicode61 tokenizes hyphens as separators,
53
+ // so "рэп-архив" must become separate tokens to match the stemmed index)
54
+ const rawTokens = query
55
+ .trim()
56
+ .split(/[\s\u2013\u2014\u2015—–\-]+/)
57
+ .filter((t) => t.length > 0);
58
+ // Filter stop words. Strip trailing punctuation before lookup so "Никиты?" → "никиты"
59
+ const normalizeForStopword = (t) => t.replace(/[?!.,;:—–\u2014\u2013]+$/, "").toLowerCase();
60
+ const contentTokens = rawTokens.filter((t) => {
61
+ const norm = normalizeForStopword(t);
62
+ if (isStopWord(norm))
63
+ return false;
64
+ if (norm.length <= 1)
65
+ return false;
66
+ if (/^\d{1,2}$/.test(norm))
67
+ return false;
68
+ return true;
69
+ });
70
+ const effectiveTokens = contentTokens.length > 0 ? contentTokens : rawTokens;
71
+ const ftsTokens = effectiveTokens
72
+ .map(tokenToFts)
73
+ .filter((t) => t !== "" && t !== '""');
74
+ if (ftsTokens.length === 0) {
75
+ throw new Error("Query must contain at least one non-empty term");
76
+ }
77
+ return ftsTokens.join(` ${operator} `);
78
+ }
79
+ /** Normalize BM25 score (negative rank) to 0–1 */
80
+ function normalizeBm25(rank) {
81
+ return 1 / (1 + Math.abs(rank));
82
+ }
83
+ /**
84
+ * Ebbinghaus decay — layer-specific half-lives.
85
+ * Semantic and procedural memories do NOT decay (facts and rules don't "forget").
86
+ * Episodic decays at 30-day half-life, resource at 90 days.
87
+ */
88
+ const DECAY_HALF_LIFE_DAYS = {
89
+ episodic: 30,
90
+ resource: 90,
91
+ semantic: null,
92
+ procedural: null,
93
+ };
94
+ function decayFactor(layer, referenceDate) {
95
+ const halfLife = DECAY_HALF_LIFE_DAYS[layer];
96
+ if (halfLife === null)
97
+ return 1.0;
98
+ const daysSince = (Date.now() - new Date(referenceDate).getTime()) / (1000 * 60 * 60 * 24);
99
+ if (daysSince <= 0)
100
+ return 1.0;
101
+ return Math.exp(-Math.LN2 * daysSince / halfLife);
102
+ }
103
+ /** Generate a plain-text snippet from content (first SNIPPET_TOKENS words) */
104
+ function makeSnippet(content) {
105
+ const words = content.split(/\s+/);
106
+ if (words.length <= SNIPPET_TOKENS)
107
+ return content;
108
+ return words.slice(0, SNIPPET_TOKENS).join(" ") + "…";
109
+ }
110
+ export async function memorySearch(db, input, embedder) {
111
+ const startMs = Date.now();
112
+ const limit = input.limit ?? DEFAULT_LIMIT;
113
+ const offset = input.offset ?? 0;
114
+ const mode = input.mode ?? "fts";
115
+ let ids;
116
+ if (mode === "vector") {
117
+ if (!embedder) {
118
+ throw new Error("Vector search requires an embedding provider. Set MNEMON_EMBEDDING_PROVIDER env var.");
119
+ }
120
+ if (!isVecLoaded()) {
121
+ throw new Error("Vector search requires sqlite-vec. Install: npm install sqlite-vec");
122
+ }
123
+ ids = await vectorSearch(db, input, embedder, limit + offset);
124
+ }
125
+ else if (mode === "hybrid") {
126
+ if (!embedder) {
127
+ throw new Error("Hybrid search requires an embedding provider. Set MNEMON_EMBEDDING_PROVIDER env var.");
128
+ }
129
+ if (!isVecLoaded()) {
130
+ throw new Error("Hybrid search requires sqlite-vec. Install: npm install sqlite-vec");
131
+ }
132
+ ids = await hybridSearch(db, input, embedder, limit + offset);
133
+ }
134
+ else if (mode === "exact") {
135
+ ids = exactSearch(db, input, limit + offset);
136
+ }
137
+ else {
138
+ ids = ftsSearch(db, input, limit + offset);
139
+ }
140
+ if (ids.length === 0) {
141
+ return { memories: [], total_found: 0, query_time_ms: Date.now() - startMs };
142
+ }
143
+ // Fetch full rows for matched IDs
144
+ const idList = ids.map((r) => r.id);
145
+ const placeholders = idList.map(() => "?").join(", ");
146
+ const rows = db
147
+ .prepare(`SELECT id, layer, title, content, entity_type, entity_name,
148
+ confidence, importance, scope, created_at, event_at,
149
+ last_accessed, superseded_by
150
+ FROM memories
151
+ WHERE id IN (${placeholders})`)
152
+ .all(...idList);
153
+ // Map back scores, boost by importance and decay for ranking
154
+ // Formula: final_score = bm25_score * (0.3 + 0.7 * importance) * decay(layer, age)
155
+ // Importance range 0.3–1.0 (wider than old 0.5–1.0)
156
+ // Decay: episodic/resource decay over time, semantic/procedural don't decay
157
+ const scoreMap = new Map(ids.map((r) => [r.id, r.score]));
158
+ const memories = rows
159
+ .map((row) => {
160
+ const bm25Score = scoreMap.get(row.id) ?? 0;
161
+ const importanceBoost = 0.3 + 0.7 * row.importance;
162
+ const decay = decayFactor(row.layer, row.last_accessed ?? row.created_at);
163
+ return {
164
+ id: row.id,
165
+ layer: row.layer,
166
+ title: row.title,
167
+ content: row.content,
168
+ snippet: makeSnippet(row.content),
169
+ score: bm25Score * importanceBoost * decay,
170
+ entity_type: row.entity_type,
171
+ entity_name: row.entity_name,
172
+ confidence: row.confidence,
173
+ importance: row.importance,
174
+ scope: row.scope,
175
+ created_at: row.created_at,
176
+ event_at: row.event_at,
177
+ };
178
+ })
179
+ .sort((a, b) => b.score - a.score)
180
+ .slice(offset, offset + limit);
181
+ // Update access tracking for returned results
182
+ if (memories.length > 0) {
183
+ const updateIds = memories.map((m) => m.id);
184
+ const ph = updateIds.map(() => "?").join(", ");
185
+ db.prepare(`UPDATE memories SET access_count = access_count + 1,
186
+ last_accessed = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
187
+ WHERE id IN (${ph})`).run(...updateIds);
188
+ }
189
+ return {
190
+ memories,
191
+ total_found: memories.length,
192
+ query_time_ms: Date.now() - startMs,
193
+ };
194
+ }
195
+ function ftsSearch(db, input, limit) {
196
+ let ftsQuery;
197
+ try {
198
+ ftsQuery = buildFtsQuery(input.query, "AND");
199
+ }
200
+ catch (err) {
201
+ throw new Error(`Invalid search query: ${err instanceof Error ? err.message : String(err)}`);
202
+ }
203
+ const conditions = ["fts.id = m.id"];
204
+ if (!input.include_superseded) {
205
+ conditions.push("m.superseded_by IS NULL");
206
+ }
207
+ // Exclude expired memories
208
+ conditions.push("(m.expires_at IS NULL OR m.expires_at > strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))");
209
+ if (input.layers && input.layers.length > 0) {
210
+ const placeholders = input.layers.map(() => "?").join(", ");
211
+ conditions.push(`m.layer IN (${placeholders})`);
212
+ }
213
+ // Resolve entity alias to canonical name
214
+ const resolvedEntity = input.entity_name ? resolveEntityName(db, input.entity_name) : undefined;
215
+ if (resolvedEntity) {
216
+ conditions.push("m.entity_name = ?");
217
+ }
218
+ if (input.scope) {
219
+ conditions.push("m.scope = ?");
220
+ }
221
+ if (input.date_from) {
222
+ conditions.push("COALESCE(m.event_at, m.created_at) >= ?");
223
+ }
224
+ if (input.date_to) {
225
+ conditions.push("COALESCE(m.event_at, m.created_at) <= ?");
226
+ }
227
+ // Temporal fact windows: filter by as_of date (use datetime() for safe comparison)
228
+ if (input.as_of) {
229
+ conditions.push("(m.valid_from IS NULL OR datetime(m.valid_from) <= datetime(?))");
230
+ conditions.push("(m.valid_until IS NULL OR datetime(m.valid_until) >= datetime(?))");
231
+ }
232
+ if (input.min_confidence !== undefined) {
233
+ conditions.push("m.confidence >= ?");
234
+ }
235
+ if (input.min_importance !== undefined) {
236
+ conditions.push("m.importance >= ?");
237
+ }
238
+ const whereClause = conditions.length > 0 ? `AND ${conditions.join(" AND ")}` : "";
239
+ // Field weights for bm25(): title=3x, content=1x, entity_name=2x
240
+ const sql = `
241
+ SELECT fts.id, bm25(memories_fts, 3.0, 1.0, 2.0) AS rank
242
+ FROM memories_fts fts
243
+ JOIN memories m ON fts.id = m.id
244
+ WHERE memories_fts MATCH ?
245
+ ${whereClause}
246
+ ORDER BY rank
247
+ LIMIT ?
248
+ `;
249
+ // Build filter params separately from FTS query for clean OR-fallback reuse
250
+ const filterParams = [];
251
+ if (input.layers && input.layers.length > 0) {
252
+ filterParams.push(...input.layers);
253
+ }
254
+ if (resolvedEntity)
255
+ filterParams.push(resolvedEntity);
256
+ if (input.scope)
257
+ filterParams.push(input.scope);
258
+ if (input.date_from)
259
+ filterParams.push(input.date_from);
260
+ if (input.date_to)
261
+ filterParams.push(input.date_to);
262
+ if (input.as_of) {
263
+ filterParams.push(input.as_of);
264
+ filterParams.push(input.as_of);
265
+ }
266
+ if (input.min_confidence !== undefined)
267
+ filterParams.push(input.min_confidence);
268
+ if (input.min_importance !== undefined)
269
+ filterParams.push(input.min_importance);
270
+ const params = [ftsQuery, ...filterParams, limit];
271
+ const runQuery = (matchExpr, penalty = 1.0) => {
272
+ try {
273
+ const p = [matchExpr, ...filterParams, limit];
274
+ const rows = db.prepare(sql).all(...p);
275
+ return rows.map((r) => ({ id: r.id, score: normalizeBm25(r.rank) * penalty }));
276
+ }
277
+ catch {
278
+ return [];
279
+ }
280
+ };
281
+ try {
282
+ let results = runQuery(ftsQuery);
283
+ // Progressive AND relaxation: when full AND with 4+ tokens returns too few results,
284
+ // try AND with just the 3 longest (most specific) stems before falling back to OR.
285
+ if (results.length < limit) {
286
+ const contentTokens = ftsQuery.split(/ AND /);
287
+ if (contentTokens.length >= 4) {
288
+ // Sort by stem length descending (longer stems = more specific)
289
+ const top3 = [...contentTokens].sort((a, b) => b.length - a.length).slice(0, 3);
290
+ const relaxedQuery = top3.join(" AND ");
291
+ const relaxedResults = runQuery(relaxedQuery, 0.9);
292
+ const existingIds = new Set(results.map((r) => r.id));
293
+ const newOnly = relaxedResults.filter((r) => !existingIds.has(r.id));
294
+ results = [...results, ...newOnly];
295
+ }
296
+ }
297
+ // Supplement with OR results when AND returns fewer than limit results.
298
+ if (results.length < limit && input.query.trim().split(/[\s\-]+/).length > 1) {
299
+ const orQuery = buildFtsQuery(input.query, "OR");
300
+ const orResults = runQuery(orQuery, 0.8);
301
+ const existingIds = new Set(results.map((r) => r.id));
302
+ const orOnly = orResults.filter((r) => !existingIds.has(r.id));
303
+ results = [...results, ...orOnly];
304
+ }
305
+ return results;
306
+ }
307
+ catch (err) {
308
+ const message = err instanceof Error ? err.message : String(err);
309
+ throw new Error(`FTS5 query failed: ${message}`);
310
+ }
311
+ }
312
+ function exactSearch(db, input, limit) {
313
+ const escaped = input.query.replace(/%/g, "\\%").replace(/_/g, "\\_");
314
+ const conditions = ["content LIKE ? ESCAPE '\\'"];
315
+ const params = [`%${escaped}%`];
316
+ if (!input.include_superseded) {
317
+ conditions.push("superseded_by IS NULL");
318
+ }
319
+ // Exclude expired memories
320
+ conditions.push("(expires_at IS NULL OR expires_at > strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))");
321
+ if (input.layers && input.layers.length > 0) {
322
+ const placeholders = input.layers.map(() => "?").join(", ");
323
+ conditions.push(`layer IN (${placeholders})`);
324
+ params.push(...input.layers);
325
+ }
326
+ if (input.entity_name) {
327
+ const resolvedEntity = resolveEntityName(db, input.entity_name);
328
+ conditions.push("entity_name = ?");
329
+ params.push(resolvedEntity);
330
+ }
331
+ if (input.scope) {
332
+ conditions.push("scope = ?");
333
+ params.push(input.scope);
334
+ }
335
+ if (input.date_from) {
336
+ conditions.push("COALESCE(event_at, created_at) >= ?");
337
+ params.push(input.date_from);
338
+ }
339
+ if (input.date_to) {
340
+ conditions.push("COALESCE(event_at, created_at) <= ?");
341
+ params.push(input.date_to);
342
+ }
343
+ // Temporal fact windows: filter by as_of date (use datetime() for safe comparison)
344
+ if (input.as_of) {
345
+ conditions.push("(valid_from IS NULL OR datetime(valid_from) <= datetime(?))");
346
+ conditions.push("(valid_until IS NULL OR datetime(valid_until) >= datetime(?))");
347
+ params.push(input.as_of);
348
+ params.push(input.as_of);
349
+ }
350
+ if (input.min_confidence !== undefined) {
351
+ conditions.push("confidence >= ?");
352
+ params.push(input.min_confidence);
353
+ }
354
+ if (input.min_importance !== undefined) {
355
+ conditions.push("importance >= ?");
356
+ params.push(input.min_importance);
357
+ }
358
+ params.push(limit);
359
+ const sql = `
360
+ SELECT id FROM memories
361
+ WHERE ${conditions.join(" AND ")}
362
+ ORDER BY importance DESC, confidence DESC
363
+ LIMIT ?
364
+ `;
365
+ const rows = db.prepare(sql).all(...params);
366
+ // Exact match gets a fixed score of 1.0
367
+ return rows.map((r) => ({ id: r.id, score: 1.0 }));
368
+ }
369
+ /**
370
+ * Build SQL filter conditions from search input (shared by vector and hybrid).
371
+ * Returns [conditions[], params[]] to add to a WHERE clause.
372
+ */
373
+ function buildMemoryFilters(db, input) {
374
+ const conditions = [];
375
+ const params = [];
376
+ if (!input.include_superseded) {
377
+ conditions.push("superseded_by IS NULL");
378
+ }
379
+ conditions.push("(expires_at IS NULL OR expires_at > strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))");
380
+ if (input.layers && input.layers.length > 0) {
381
+ conditions.push(`layer IN (${input.layers.map(() => "?").join(", ")})`);
382
+ params.push(...input.layers);
383
+ }
384
+ if (input.entity_name) {
385
+ const resolved = resolveEntityName(db, input.entity_name);
386
+ conditions.push("entity_name = ?");
387
+ params.push(resolved);
388
+ }
389
+ if (input.scope) {
390
+ conditions.push("scope = ?");
391
+ params.push(input.scope);
392
+ }
393
+ if (input.date_from) {
394
+ conditions.push("COALESCE(event_at, created_at) >= ?");
395
+ params.push(input.date_from);
396
+ }
397
+ if (input.date_to) {
398
+ conditions.push("COALESCE(event_at, created_at) <= ?");
399
+ params.push(input.date_to);
400
+ }
401
+ if (input.as_of) {
402
+ conditions.push("(valid_from IS NULL OR datetime(valid_from) <= datetime(?))");
403
+ conditions.push("(valid_until IS NULL OR datetime(valid_until) >= datetime(?))");
404
+ params.push(input.as_of, input.as_of);
405
+ }
406
+ if (input.min_confidence !== undefined) {
407
+ conditions.push("confidence >= ?");
408
+ params.push(input.min_confidence);
409
+ }
410
+ if (input.min_importance !== undefined) {
411
+ conditions.push("importance >= ?");
412
+ params.push(input.min_importance);
413
+ }
414
+ return { conditions, params };
415
+ }
416
+ async function vectorSearch(db, input, embedder, limit) {
417
+ const queryVec = await embedder.embed(input.query);
418
+ // Over-fetch to account for filter losses
419
+ const knnLimit = Math.min(limit * 3, 200);
420
+ const results = knnSearch(db, queryVec, knnLimit, !input.include_superseded);
421
+ if (results.length === 0)
422
+ return [];
423
+ // Apply the same filters as FTS/exact search
424
+ const { conditions, params } = buildMemoryFilters(db, input);
425
+ const knnIds = results.map((r) => r.memory_id);
426
+ const idPlaceholders = knnIds.map(() => "?").join(", ");
427
+ const allConditions = [`id IN (${idPlaceholders})`, ...conditions];
428
+ const filtered = db
429
+ .prepare(`SELECT id FROM memories WHERE ${allConditions.join(" AND ")}`)
430
+ .all(...knnIds, ...params);
431
+ const filteredSet = new Set(filtered.map((r) => r.id));
432
+ return results
433
+ .filter((r) => filteredSet.has(r.memory_id))
434
+ .map((r) => ({ id: r.memory_id, score: Math.max(0, 1 - r.distance) }))
435
+ .slice(0, limit);
436
+ }
437
+ async function hybridSearch(db, input, embedder, limit) {
438
+ // Run FTS and vector search in parallel
439
+ const [ftsResults, vecResults] = await Promise.all([
440
+ Promise.resolve(ftsSearch(db, input, limit)),
441
+ vectorSearch(db, input, embedder, limit),
442
+ ]);
443
+ // Reciprocal Rank Fusion (k=60)
444
+ const RRF_K = 60;
445
+ const scores = new Map();
446
+ ftsResults.forEach((r, i) => {
447
+ const rank = i + 1;
448
+ scores.set(r.id, (scores.get(r.id) ?? 0) + 1.0 / (RRF_K + rank));
449
+ });
450
+ vecResults.forEach((r, i) => {
451
+ const rank = i + 1;
452
+ scores.set(r.id, (scores.get(r.id) ?? 0) + 1.0 / (RRF_K + rank));
453
+ });
454
+ return Array.from(scores.entries())
455
+ .map(([id, score]) => ({ id, score }))
456
+ .sort((a, b) => b.score - a.score)
457
+ .slice(0, limit);
458
+ }
459
+ //# sourceMappingURL=memory-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-search.js","sourceRoot":"","sources":["../../src/tools/memory-search.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * memory_update — update fields in place or create a superseding entry.
3
+ *
4
+ * When supersede=true: creates a new memory entry that supersedes the old one.
5
+ * The old entry's superseded_by is set to the new entry's ID.
6
+ * When supersede=false (default): directly UPDATE the fields in place.
7
+ */
8
+ import type Database from "better-sqlite3";
9
+ import type { MemoryUpdateInput, MemoryUpdateOutput } from "../types.js";
10
+ export declare function memoryUpdate(db: Database.Database, input: MemoryUpdateInput): MemoryUpdateOutput;
11
+ //# sourceMappingURL=memory-update.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-update.d.ts","sourceRoot":"","sources":["../../src/tools/memory-update.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AA0BzE,wBAAgB,YAAY,CAC1B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,KAAK,EAAE,iBAAiB,GACvB,kBAAkB,CA0BpB"}
@@ -0,0 +1,142 @@
1
+ /**
2
+ * memory_update — update fields in place or create a superseding entry.
3
+ *
4
+ * When supersede=true: creates a new memory entry that supersedes the old one.
5
+ * The old entry's superseded_by is set to the new entry's ID.
6
+ * When supersede=false (default): directly UPDATE the fields in place.
7
+ */
8
+ import { stemText } from "../stemmer.js";
9
+ import { generateId, insertMemory } from "./utils.js";
10
+ export function memoryUpdate(db, input) {
11
+ // Fetch existing record
12
+ const existing = db
13
+ .prepare(`SELECT id, layer, content, title, source, source_file, session_id,
14
+ event_at, expires_at, confidence, importance, supersedes,
15
+ superseded_by, entity_type, entity_name, scope, meta,
16
+ valid_from, valid_until
17
+ FROM memories WHERE id = ?`)
18
+ .get(input.id);
19
+ if (!existing) {
20
+ throw new Error(`Memory not found: ${input.id}`);
21
+ }
22
+ if (input.supersede === true) {
23
+ if (existing.superseded_by !== null) {
24
+ throw new Error(`Cannot supersede memory ${input.id}: already superseded by ${existing.superseded_by}`);
25
+ }
26
+ return createSupersedingEntry(db, existing, input);
27
+ }
28
+ return updateInPlace(db, existing, input);
29
+ }
30
+ function updateInPlace(db, existing, input) {
31
+ const setClauses = [];
32
+ const params = [];
33
+ if (input.content !== undefined) {
34
+ setClauses.push("content = ?");
35
+ params.push(input.content);
36
+ setClauses.push("stemmed_content = ?");
37
+ params.push(stemText(input.content));
38
+ }
39
+ if (input.title !== undefined) {
40
+ setClauses.push("title = ?");
41
+ params.push(input.title);
42
+ setClauses.push("stemmed_title = ?");
43
+ params.push(stemText(input.title));
44
+ }
45
+ if (input.confidence !== undefined) {
46
+ setClauses.push("confidence = ?");
47
+ params.push(input.confidence);
48
+ }
49
+ if (input.importance !== undefined) {
50
+ setClauses.push("importance = ?");
51
+ params.push(input.importance);
52
+ }
53
+ if (input.meta !== undefined) {
54
+ // Merge with existing meta
55
+ let existingMeta = {};
56
+ try {
57
+ existingMeta = JSON.parse(existing.meta);
58
+ }
59
+ catch {
60
+ // existing meta is malformed — start fresh
61
+ }
62
+ const merged = { ...existingMeta, ...input.meta };
63
+ setClauses.push("meta = ?");
64
+ params.push(JSON.stringify(merged));
65
+ }
66
+ if (setClauses.length === 0) {
67
+ // Nothing to update
68
+ return { updated_id: existing.id, superseded: false };
69
+ }
70
+ // updated_at is handled by the trigger, but only when updated_at is NOT
71
+ // being explicitly set. We force a change by touching updated_at ourselves.
72
+ setClauses.push("updated_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')");
73
+ params.push(existing.id);
74
+ const newContent = input.content ?? existing.content;
75
+ const run = db.transaction(() => {
76
+ db.prepare(`UPDATE memories SET ${setClauses.join(", ")} WHERE id = ?`).run(...params);
77
+ db.prepare(`INSERT INTO event_log (memory_id, event_type, actor, old_content, new_content)
78
+ VALUES (?, 'updated', ?, ?, ?)`).run(existing.id, existing.source, existing.content, newContent);
79
+ });
80
+ run();
81
+ return { updated_id: existing.id, superseded: false };
82
+ }
83
+ function createSupersedingEntry(db, existing, input) {
84
+ const newId = generateId();
85
+ const newContent = input.new_content ?? input.content ?? existing.content;
86
+ // Merge meta if provided
87
+ let metaJson = existing.meta;
88
+ if (input.meta !== undefined) {
89
+ let existingMeta = {};
90
+ try {
91
+ existingMeta = JSON.parse(existing.meta);
92
+ }
93
+ catch {
94
+ // start fresh
95
+ }
96
+ metaJson = JSON.stringify({ ...existingMeta, ...input.meta });
97
+ }
98
+ const newTitle = input.title ?? existing.title;
99
+ // Don't inherit expired expires_at — superseding entry should not start expired
100
+ const expiresAt = existing.expires_at && new Date(existing.expires_at) > new Date()
101
+ ? existing.expires_at
102
+ : null;
103
+ const run = db.transaction(() => {
104
+ insertMemory(db, {
105
+ id: newId,
106
+ layer: existing.layer,
107
+ content: newContent,
108
+ title: newTitle,
109
+ source: existing.source,
110
+ source_file: existing.source_file,
111
+ session_id: existing.session_id,
112
+ event_at: existing.event_at,
113
+ expires_at: expiresAt,
114
+ confidence: input.confidence ?? existing.confidence,
115
+ importance: input.importance ?? existing.importance,
116
+ supersedes: existing.id,
117
+ entity_type: existing.entity_type,
118
+ entity_name: existing.entity_name,
119
+ scope: existing.scope,
120
+ meta: metaJson,
121
+ valid_from: existing.valid_from,
122
+ valid_until: existing.valid_until,
123
+ });
124
+ // Mark the old entry as superseded
125
+ db.prepare(`UPDATE memories SET superseded_by = ? WHERE id = ?`).run(newId, existing.id);
126
+ db.prepare(`
127
+ INSERT INTO event_log (memory_id, event_type, actor, old_content, new_content)
128
+ VALUES (?, ?, ?, ?, ?)
129
+ `).run(existing.id, "superseded", existing.source, existing.content, null);
130
+ db.prepare(`
131
+ INSERT INTO event_log (memory_id, event_type, actor, old_content, new_content)
132
+ VALUES (?, ?, ?, ?, ?)
133
+ `).run(newId, "created", existing.source, null, newContent);
134
+ });
135
+ run();
136
+ return {
137
+ updated_id: existing.id,
138
+ new_id: newId,
139
+ superseded: true,
140
+ };
141
+ }
142
+ //# sourceMappingURL=memory-update.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-update.js","sourceRoot":"","sources":["../../src/tools/memory-update.ts"],"names":[],"mappings":"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"}