@kybernesis/brain-core 0.1.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 (129) hide show
  1. package/dist/claude-call.d.ts +37 -0
  2. package/dist/claude-call.d.ts.map +1 -0
  3. package/dist/claude-call.js +55 -0
  4. package/dist/claude-call.js.map +1 -0
  5. package/dist/entity-graph.d.ts +79 -0
  6. package/dist/entity-graph.d.ts.map +1 -0
  7. package/dist/entity-graph.js +611 -0
  8. package/dist/entity-graph.js.map +1 -0
  9. package/dist/fact-contradiction.d.ts +25 -0
  10. package/dist/fact-contradiction.d.ts.map +1 -0
  11. package/dist/fact-contradiction.js +109 -0
  12. package/dist/fact-contradiction.js.map +1 -0
  13. package/dist/fact-extractor.d.ts +26 -0
  14. package/dist/fact-extractor.d.ts.map +1 -0
  15. package/dist/fact-extractor.js +98 -0
  16. package/dist/fact-extractor.js.map +1 -0
  17. package/dist/fact-retrieval.d.ts +51 -0
  18. package/dist/fact-retrieval.d.ts.map +1 -0
  19. package/dist/fact-retrieval.js +401 -0
  20. package/dist/fact-retrieval.js.map +1 -0
  21. package/dist/fact-store.d.ts +26 -0
  22. package/dist/fact-store.d.ts.map +1 -0
  23. package/dist/fact-store.js +181 -0
  24. package/dist/fact-store.js.map +1 -0
  25. package/dist/fact-temporal.d.ts +20 -0
  26. package/dist/fact-temporal.d.ts.map +1 -0
  27. package/dist/fact-temporal.js +79 -0
  28. package/dist/fact-temporal.js.map +1 -0
  29. package/dist/fts-sanitizer.d.ts +32 -0
  30. package/dist/fts-sanitizer.d.ts.map +1 -0
  31. package/dist/fts-sanitizer.js +61 -0
  32. package/dist/fts-sanitizer.js.map +1 -0
  33. package/dist/hybrid-search.d.ts +44 -0
  34. package/dist/hybrid-search.d.ts.map +1 -0
  35. package/dist/hybrid-search.js +410 -0
  36. package/dist/hybrid-search.js.map +1 -0
  37. package/dist/index.d.ts +33 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +19 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/ops.d.ts +28 -0
  42. package/dist/ops.d.ts.map +1 -0
  43. package/dist/ops.js +307 -0
  44. package/dist/ops.js.map +1 -0
  45. package/dist/queue.d.ts +13 -0
  46. package/dist/queue.d.ts.map +1 -0
  47. package/dist/queue.js +28 -0
  48. package/dist/queue.js.map +1 -0
  49. package/dist/relationship-extractor.d.ts +38 -0
  50. package/dist/relationship-extractor.d.ts.map +1 -0
  51. package/dist/relationship-extractor.js +119 -0
  52. package/dist/relationship-extractor.js.map +1 -0
  53. package/dist/sleep/config.d.ts +47 -0
  54. package/dist/sleep/config.d.ts.map +1 -0
  55. package/dist/sleep/config.js +46 -0
  56. package/dist/sleep/config.js.map +1 -0
  57. package/dist/sleep/db.d.ts +16 -0
  58. package/dist/sleep/db.d.ts.map +1 -0
  59. package/dist/sleep/db.js +114 -0
  60. package/dist/sleep/db.js.map +1 -0
  61. package/dist/sleep/index.d.ts +69 -0
  62. package/dist/sleep/index.d.ts.map +1 -0
  63. package/dist/sleep/index.js +99 -0
  64. package/dist/sleep/index.js.map +1 -0
  65. package/dist/sleep/steps/consolidate.d.ts +19 -0
  66. package/dist/sleep/steps/consolidate.d.ts.map +1 -0
  67. package/dist/sleep/steps/consolidate.js +74 -0
  68. package/dist/sleep/steps/consolidate.js.map +1 -0
  69. package/dist/sleep/steps/decay.d.ts +19 -0
  70. package/dist/sleep/steps/decay.d.ts.map +1 -0
  71. package/dist/sleep/steps/decay.js +121 -0
  72. package/dist/sleep/steps/decay.js.map +1 -0
  73. package/dist/sleep/steps/entity-hygiene.d.ts +29 -0
  74. package/dist/sleep/steps/entity-hygiene.d.ts.map +1 -0
  75. package/dist/sleep/steps/entity-hygiene.js +452 -0
  76. package/dist/sleep/steps/entity-hygiene.js.map +1 -0
  77. package/dist/sleep/steps/link.d.ts +20 -0
  78. package/dist/sleep/steps/link.d.ts.map +1 -0
  79. package/dist/sleep/steps/link.js +216 -0
  80. package/dist/sleep/steps/link.js.map +1 -0
  81. package/dist/sleep/steps/observe.d.ts +17 -0
  82. package/dist/sleep/steps/observe.d.ts.map +1 -0
  83. package/dist/sleep/steps/observe.js +192 -0
  84. package/dist/sleep/steps/observe.js.map +1 -0
  85. package/dist/sleep/steps/profile.d.ts +15 -0
  86. package/dist/sleep/steps/profile.d.ts.map +1 -0
  87. package/dist/sleep/steps/profile.js +26 -0
  88. package/dist/sleep/steps/profile.js.map +1 -0
  89. package/dist/sleep/steps/reasoning.d.ts +19 -0
  90. package/dist/sleep/steps/reasoning.d.ts.map +1 -0
  91. package/dist/sleep/steps/reasoning.js +173 -0
  92. package/dist/sleep/steps/reasoning.js.map +1 -0
  93. package/dist/sleep/steps/summarize.d.ts +21 -0
  94. package/dist/sleep/steps/summarize.d.ts.map +1 -0
  95. package/dist/sleep/steps/summarize.js +206 -0
  96. package/dist/sleep/steps/summarize.js.map +1 -0
  97. package/dist/sleep/steps/tag.d.ts +16 -0
  98. package/dist/sleep/steps/tag.d.ts.map +1 -0
  99. package/dist/sleep/steps/tag.js +84 -0
  100. package/dist/sleep/steps/tag.js.map +1 -0
  101. package/dist/sleep/steps/tier.d.ts +18 -0
  102. package/dist/sleep/steps/tier.d.ts.map +1 -0
  103. package/dist/sleep/steps/tier.js +112 -0
  104. package/dist/sleep/steps/tier.js.map +1 -0
  105. package/dist/sleep/utils/checkpoint.d.ts +11 -0
  106. package/dist/sleep/utils/checkpoint.d.ts.map +1 -0
  107. package/dist/sleep/utils/checkpoint.js +26 -0
  108. package/dist/sleep/utils/checkpoint.js.map +1 -0
  109. package/dist/sleep/utils/jaccard.d.ts +7 -0
  110. package/dist/sleep/utils/jaccard.d.ts.map +1 -0
  111. package/dist/sleep/utils/jaccard.js +19 -0
  112. package/dist/sleep/utils/jaccard.js.map +1 -0
  113. package/dist/store-conversation.d.ts +29 -0
  114. package/dist/store-conversation.d.ts.map +1 -0
  115. package/dist/store-conversation.js +227 -0
  116. package/dist/store-conversation.js.map +1 -0
  117. package/dist/timeline.d.ts +50 -0
  118. package/dist/timeline.d.ts.map +1 -0
  119. package/dist/timeline.js +389 -0
  120. package/dist/timeline.js.map +1 -0
  121. package/dist/user-profile.d.ts +30 -0
  122. package/dist/user-profile.d.ts.map +1 -0
  123. package/dist/user-profile.js +147 -0
  124. package/dist/user-profile.js.map +1 -0
  125. package/dist/vectors.d.ts +56 -0
  126. package/dist/vectors.d.ts.map +1 -0
  127. package/dist/vectors.js +132 -0
  128. package/dist/vectors.js.map +1 -0
  129. package/package.json +53 -0
@@ -0,0 +1,401 @@
1
+ /**
2
+ * Fact-first retrieval engine — port of KAD's fact-retrieval.ts.
3
+ *
4
+ * 4-layer retrieval:
5
+ * Layer 1: Direct FTS5 + semantic search on facts
6
+ * Layer 2: Entity expansion (1-3 hop BFS graph traversal)
7
+ * Layer 2.5: Scene expansion + bridge discovery
8
+ * Layer 3: Supporting context (source conversation segments)
9
+ * Layer 4: Context optimization (prune to token budget, deduplicate)
10
+ *
11
+ * Bug Fix #2 applied here: uses sanitizeFtsQuery for all FTS MATCH calls.
12
+ * Bug Fix #9 applied here: wordMatchRatio denominator is FTS-query token count.
13
+ */
14
+ import { getDb } from '@kybernesis/brain-storage-sqlite';
15
+ import { ensureFactsTable, getFactsForEntity } from './fact-store.js';
16
+ import { semanticSearch } from './vectors.js';
17
+ import { sanitizeFtsQuery } from './fts-sanitizer.js';
18
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
19
+ function estimateTokens(text) {
20
+ return Math.ceil(text.length / 4);
21
+ }
22
+ function wordOverlap(a, b) {
23
+ const wordsA = new Set(a.toLowerCase().split(/\s+/).filter(w => w.length >= 3));
24
+ const wordsB = new Set(b.toLowerCase().split(/\s+/).filter(w => w.length >= 3));
25
+ if (wordsA.size === 0 || wordsB.size === 0)
26
+ return 0;
27
+ let common = 0;
28
+ for (const w of wordsA) {
29
+ if (wordsB.has(w))
30
+ common++;
31
+ }
32
+ return common / Math.max(wordsA.size, wordsB.size);
33
+ }
34
+ // ─── Layer 1: Direct fact search ─────────────────────────────────────────────
35
+ async function searchFactsDirect(t, query, limit) {
36
+ await ensureFactsTable(t);
37
+ const db = getDb(t, 'timeline');
38
+ const results = [];
39
+ // Bug Fix #2: use sanitizeFtsQuery instead of naive replace
40
+ const sanitized = sanitizeFtsQuery(query);
41
+ // Bug Fix #9: derive score denominator from FTS token count, not raw query
42
+ const ftsTokens = sanitized
43
+ ? sanitized.split(' OR ').map(t => t.replace(/^"|"$/g, '').toLowerCase()).filter(Boolean)
44
+ : [];
45
+ if (sanitized) {
46
+ try {
47
+ const ftsRows = db.prepare(`
48
+ SELECT f.id, f.content, f.category, f.confidence, f.timestamp,
49
+ f.entities_json, f.is_latest, f.expires_at, f.source_conversation_id
50
+ FROM facts f
51
+ JOIN facts_fts fts ON f.id = fts.rowid
52
+ WHERE facts_fts MATCH ?
53
+ AND COALESCE(f.is_latest, 1) = 1
54
+ AND (f.expires_at IS NULL OR f.expires_at > datetime('now'))
55
+ ORDER BY rank
56
+ LIMIT ?
57
+ `).all(sanitized, limit * 2);
58
+ for (const row of ftsRows) {
59
+ const contentLower = row.content.toLowerCase();
60
+ // Bug Fix #9: denominator is ftsTokens.length, not raw query word count
61
+ const matchedCount = ftsTokens.filter(w => contentLower.includes(w)).length;
62
+ const wordMatchRatio = ftsTokens.length > 0 ? matchedCount / ftsTokens.length : 0;
63
+ const score = 0.5 + wordMatchRatio * 0.5;
64
+ results.push({
65
+ id: row.id,
66
+ content: row.content,
67
+ category: row.category || 'general',
68
+ confidence: row.confidence || 0.7,
69
+ timestamp: row.timestamp,
70
+ entities: JSON.parse(row.entities_json || '[]'),
71
+ score,
72
+ source: 'direct',
73
+ source_conversation_id: row.source_conversation_id,
74
+ });
75
+ }
76
+ }
77
+ catch (err) {
78
+ console.warn('[brain-core/fact-retrieval] FTS search failed', { err: String(err) });
79
+ }
80
+ }
81
+ // Semantic search (no-op until Day 4 vectors)
82
+ try {
83
+ const semanticResults = await semanticSearch(t, query, { limit: limit * 2, type: 'note' });
84
+ for (const sr of semanticResults) {
85
+ if (!sr.metadata.source_path.startsWith('fact://'))
86
+ continue;
87
+ const semanticScore = 1 - sr.distance;
88
+ let merged = false;
89
+ for (const existing of results) {
90
+ if (wordOverlap(existing.content, sr.content) > 0.8) {
91
+ existing.score = Math.max(existing.score, semanticScore);
92
+ merged = true;
93
+ break;
94
+ }
95
+ }
96
+ if (!merged) {
97
+ results.push({
98
+ id: 0,
99
+ content: sr.content,
100
+ category: 'general',
101
+ confidence: 0.7,
102
+ timestamp: sr.metadata.timestamp ?? new Date().toISOString(),
103
+ entities: [],
104
+ score: semanticScore,
105
+ source: 'direct',
106
+ });
107
+ }
108
+ }
109
+ }
110
+ catch { /* semantic search is best-effort */ }
111
+ // Deduplicate by content overlap
112
+ const deduped = [];
113
+ for (const fact of results) {
114
+ let isDuplicate = false;
115
+ for (const existing of deduped) {
116
+ if (wordOverlap(existing.content, fact.content) > 0.8) {
117
+ if (fact.score > existing.score)
118
+ Object.assign(existing, fact);
119
+ isDuplicate = true;
120
+ break;
121
+ }
122
+ }
123
+ if (!isDuplicate)
124
+ deduped.push(fact);
125
+ }
126
+ return deduped.sort((a, b) => b.score - a.score).slice(0, limit);
127
+ }
128
+ // ─── Layer 2: Entity expansion ────────────────────────────────────────────────
129
+ const HOP_DISTANCE_PENALTY = { 0: 1.0, 1: 0.7, 2: 0.5, 3: 0.3 };
130
+ function traverseEntityGraph(entityDb, seedIds, maxHops, maxEntities) {
131
+ const visited = new Map();
132
+ for (const id of seedIds)
133
+ visited.set(id, 0);
134
+ let frontier = [...seedIds];
135
+ for (let hop = 0; hop < maxHops && frontier.length > 0; hop++) {
136
+ const next = [];
137
+ for (const eid of frontier) {
138
+ try {
139
+ const connected = entityDb.prepare(`
140
+ SELECT CASE WHEN source_id = ? THEN target_id ELSE source_id END as cid
141
+ FROM entity_relations
142
+ WHERE source_id = ? OR target_id = ?
143
+ ORDER BY strength DESC LIMIT 10
144
+ `).all(eid, eid, eid);
145
+ for (const c of connected) {
146
+ if (!visited.has(c.cid) && visited.size < maxEntities) {
147
+ visited.set(c.cid, hop + 1);
148
+ next.push(c.cid);
149
+ }
150
+ }
151
+ }
152
+ catch { /* skip */ }
153
+ }
154
+ frontier = next;
155
+ }
156
+ return Array.from(visited.entries()).map(([id, hopDistance]) => ({ id, hopDistance }));
157
+ }
158
+ async function expandByEntities(t, query, existingFacts, limit) {
159
+ const expanded = [];
160
+ try {
161
+ const entityDb = getDb(t, 'entityGraph');
162
+ const queryLower = query.toLowerCase();
163
+ const allEntities = entityDb.prepare('SELECT id, name FROM entities ORDER BY mention_count DESC LIMIT 200').all();
164
+ const matched = allEntities.filter(e => e.name.length >= 3 && queryLower.includes(e.name.toLowerCase()));
165
+ if (matched.length === 0)
166
+ return [];
167
+ const seedIds = matched.slice(0, 5).map(e => e.id);
168
+ const reached = traverseEntityGraph(entityDb, seedIds, 1, 10);
169
+ const nameMap = new Map(allEntities.map(e => [e.id, e.name]));
170
+ const existingContent = existingFacts.map(f => f.content);
171
+ for (const r of reached) {
172
+ const name = nameMap.get(r.id);
173
+ if (!name)
174
+ continue;
175
+ const entityFacts = await getFactsForEntity(t, name, { latestOnly: true, limit: 10 });
176
+ const penalty = HOP_DISTANCE_PENALTY[r.hopDistance] ?? 0.3;
177
+ const source = r.hopDistance === 0 ? 'entity_expansion' : 'graph_expansion';
178
+ for (const ef of entityFacts) {
179
+ if (existingContent.some(c => wordOverlap(c, ef.content) > 0.8))
180
+ continue;
181
+ if (expanded.some(ex => wordOverlap(ex.content, ef.content) > 0.8))
182
+ continue;
183
+ if (r.hopDistance > 0 && wordOverlap(queryLower, ef.content.toLowerCase()) < 0.1)
184
+ continue;
185
+ expanded.push({
186
+ id: ef.id,
187
+ content: ef.content,
188
+ category: ef.category || 'general',
189
+ confidence: ef.confidence,
190
+ timestamp: ef.timestamp,
191
+ entities: ef.entities,
192
+ score: (r.hopDistance === 0 ? 1.0 : ef.confidence || 0.7) * penalty,
193
+ source,
194
+ source_conversation_id: ef.sourceConversationId,
195
+ });
196
+ }
197
+ }
198
+ }
199
+ catch (err) {
200
+ console.warn('[brain-core/fact-retrieval] entity expansion failed', { err: String(err) });
201
+ }
202
+ return expanded.sort((a, b) => b.score - a.score).slice(0, limit);
203
+ }
204
+ async function findSupportingContext(t, facts, maxFacts, maxPerFact) {
205
+ const supporting = [];
206
+ try {
207
+ const db = getDb(t, 'timeline');
208
+ for (const fact of facts.slice(0, maxFacts)) {
209
+ let parentPath = null;
210
+ try {
211
+ const row = db.prepare('SELECT source_conversation_id FROM facts WHERE id = ?').get(fact.id);
212
+ if (row)
213
+ parentPath = row.source_conversation_id;
214
+ }
215
+ catch { /* best-effort */ }
216
+ if (!parentPath)
217
+ continue;
218
+ try {
219
+ const segs = db.prepare(`
220
+ SELECT source_path, summary, timestamp FROM timeline_events
221
+ WHERE source_path LIKE ? OR source_path = ?
222
+ ORDER BY timestamp ASC LIMIT ?
223
+ `).all(`${parentPath}%`, parentPath, maxPerFact);
224
+ for (const seg of segs) {
225
+ if (!seg.summary || seg.summary.trim().length < 10)
226
+ continue;
227
+ supporting.push({
228
+ content: seg.summary,
229
+ source_path: seg.source_path,
230
+ timestamp: seg.timestamp,
231
+ related_fact_id: fact.id,
232
+ score: fact.score,
233
+ });
234
+ }
235
+ }
236
+ catch { /* best-effort */ }
237
+ }
238
+ }
239
+ catch (err) {
240
+ console.warn('[brain-core/fact-retrieval] supporting context failed', { err: String(err) });
241
+ }
242
+ return supporting;
243
+ }
244
+ // ─── Layer 4: Context assembly ────────────────────────────────────────────────
245
+ function assembleContext(facts, supporting, tokenBudget) {
246
+ const lines = [];
247
+ let tokens = 0;
248
+ let pruned = 0;
249
+ lines.push('## Relevant Facts\n');
250
+ tokens += estimateTokens('## Relevant Facts\n');
251
+ for (const fact of facts) {
252
+ const line = `- [${fact.category}] ${fact.content} (confidence: ${Math.round(fact.confidence * 100)}%)\n`;
253
+ const t = estimateTokens(line);
254
+ if (tokens + t > tokenBudget * 0.7) {
255
+ pruned++;
256
+ continue;
257
+ }
258
+ lines.push(line);
259
+ tokens += t;
260
+ }
261
+ if (supporting.length > 0) {
262
+ lines.push('\n## Supporting Context\n');
263
+ tokens += estimateTokens('\n## Supporting Context\n');
264
+ for (const chunk of supporting) {
265
+ const line = `${chunk.content.slice(0, 300)}\n`;
266
+ const t = estimateTokens(line);
267
+ if (tokens + t > tokenBudget) {
268
+ pruned++;
269
+ continue;
270
+ }
271
+ lines.push(line);
272
+ tokens += t;
273
+ }
274
+ }
275
+ return { assembled: lines.join(''), tokenEstimate: tokens, pruned };
276
+ }
277
+ // ─── Main entry point ─────────────────────────────────────────────────────────
278
+ export async function factFirstSearch(t, query, options = {}) {
279
+ const { limit = 15, tokenBudget = 4000, includeSupporting = true, maxSupportingPerFact = 2, } = options;
280
+ // Layer 1
281
+ const directFacts = await searchFactsDirect(t, query, limit * 2);
282
+ // Layer 2
283
+ const expandedFacts = await expandByEntities(t, query, directFacts, limit);
284
+ const allFacts = [...expandedFacts, ...directFacts]
285
+ .sort((a, b) => b.score - a.score);
286
+ // Layer 2.5: scene expansion + bridge discovery
287
+ let sceneExpandedCount = 0;
288
+ let bridgeCount = 0;
289
+ const seenIds = new Set(allFacts.map(f => f.id));
290
+ try {
291
+ const db = getDb(t, 'timeline');
292
+ for (const topFact of allFacts.slice(0, 5)) {
293
+ let convId = topFact.source_conversation_id;
294
+ if (!convId && topFact.id > 0) {
295
+ try {
296
+ const row = db.prepare('SELECT source_conversation_id FROM facts WHERE id = ?')
297
+ .get(topFact.id);
298
+ if (row)
299
+ convId = row.source_conversation_id;
300
+ }
301
+ catch { /* best-effort */ }
302
+ }
303
+ if (!convId)
304
+ continue;
305
+ try {
306
+ const nearby = db.prepare(`
307
+ SELECT id, content, category, confidence, timestamp, entities_json, source_conversation_id
308
+ FROM facts
309
+ WHERE source_conversation_id = ? AND id != ?
310
+ AND COALESCE(is_latest, 1) = 1
311
+ AND (expires_at IS NULL OR expires_at > datetime('now'))
312
+ ORDER BY ABS(id - ?) ASC LIMIT 3
313
+ `).all(convId, topFact.id, topFact.id);
314
+ for (const n of nearby) {
315
+ if (!seenIds.has(n.id)) {
316
+ seenIds.add(n.id);
317
+ sceneExpandedCount++;
318
+ allFacts.push({
319
+ id: n.id, content: n.content, category: n.category || 'general',
320
+ confidence: n.confidence || 0.7, timestamp: n.timestamp,
321
+ entities: JSON.parse(n.entities_json || '[]'),
322
+ score: topFact.score * 0.6, source: 'scene_expansion',
323
+ source_conversation_id: n.source_conversation_id,
324
+ });
325
+ }
326
+ }
327
+ }
328
+ catch { /* best-effort */ }
329
+ }
330
+ // Bridge: find facts that share entities with multiple top results
331
+ const topFacts = allFacts.slice(0, 3);
332
+ if (topFacts.length >= 2) {
333
+ const entitySets = topFacts.map(f => new Set(f.entities.map(e => e.toLowerCase())));
334
+ try {
335
+ const bridgeCandidates = db.prepare(`
336
+ SELECT id, content, category, confidence, timestamp, entities_json
337
+ FROM facts
338
+ WHERE COALESCE(is_latest, 1) = 1
339
+ AND (expires_at IS NULL OR expires_at > datetime('now'))
340
+ LIMIT 100
341
+ `).all();
342
+ for (const candidate of bridgeCandidates) {
343
+ if (seenIds.has(candidate.id))
344
+ continue;
345
+ const candidateEntities = new Set(JSON.parse(candidate.entities_json || '[]').map(e => e.toLowerCase()));
346
+ let setsConnected = 0;
347
+ for (const es of entitySets) {
348
+ if ([...candidateEntities].some(e => es.has(e)))
349
+ setsConnected++;
350
+ }
351
+ if (setsConnected >= 2) {
352
+ seenIds.add(candidate.id);
353
+ bridgeCount++;
354
+ allFacts.push({
355
+ id: candidate.id, content: candidate.content,
356
+ category: candidate.category || 'general',
357
+ confidence: candidate.confidence || 0.7, timestamp: candidate.timestamp,
358
+ entities: JSON.parse(candidate.entities_json || '[]'),
359
+ score: 0.4, source: 'bridge',
360
+ });
361
+ }
362
+ }
363
+ }
364
+ catch { /* best-effort */ }
365
+ }
366
+ }
367
+ catch (err) {
368
+ console.warn('[brain-core/fact-retrieval] scene/bridge expansion failed', { err: String(err) });
369
+ }
370
+ // Layer 3: supporting context
371
+ const supporting = includeSupporting
372
+ ? await findSupportingContext(t, allFacts.slice(0, limit), Math.min(limit, 10), maxSupportingPerFact)
373
+ : [];
374
+ // Layer 4: assemble context
375
+ const topFacts = allFacts.sort((a, b) => b.score - a.score).slice(0, limit);
376
+ const { assembled, tokenEstimate, pruned } = assembleContext(topFacts, supporting, tokenBudget);
377
+ const directCount = topFacts.filter(f => f.source === 'direct').length;
378
+ const expandedCount = topFacts.filter(f => f.source === 'entity_expansion').length;
379
+ const graphCount = topFacts.filter(f => f.source === 'graph_expansion').length;
380
+ return {
381
+ facts: topFacts,
382
+ supporting_context: supporting.map(s => ({
383
+ content: s.content,
384
+ source_path: s.source_path,
385
+ timestamp: s.timestamp,
386
+ related_fact_id: s.related_fact_id,
387
+ })),
388
+ assembled_context: assembled,
389
+ token_estimate: tokenEstimate,
390
+ stats: {
391
+ direct_facts: directCount,
392
+ expanded_facts: expandedCount,
393
+ graph_expanded_facts: graphCount,
394
+ scene_expanded_facts: sceneExpandedCount,
395
+ bridge_facts: bridgeCount,
396
+ supporting_chunks: supporting.length,
397
+ pruned_items: pruned,
398
+ },
399
+ };
400
+ }
401
+ //# sourceMappingURL=fact-retrieval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fact-retrieval.js","sourceRoot":"","sources":["../src/fact-retrieval.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { TenantContext, FactCategory, Fact } from '@kybernesis/brain-contracts';
2
+ export declare const VALID_CATEGORIES: Set<"biographical" | "preference" | "event" | "relationship" | "temporal" | "opinion" | "plan" | "general">;
3
+ export declare function ensureFactsTable(t: TenantContext): Promise<void>;
4
+ export declare function storeFact(t: TenantContext, fact: Omit<Fact, 'id' | 'createdAt' | 'isLatest' | 'accessCount' | 'isRetracted'>): Promise<number>;
5
+ export declare function retractFact(t: TenantContext, factId: number, retractedBy?: string): Promise<void>;
6
+ export declare function reinforceFact(t: TenantContext, factId: number): Promise<void>;
7
+ export declare function getFactById(t: TenantContext, factId: number): Promise<Fact | null>;
8
+ export declare function getFactsForEntity(t: TenantContext, entity: string, options?: {
9
+ latestOnly?: boolean;
10
+ limit?: number;
11
+ category?: FactCategory;
12
+ }): Promise<Fact[]>;
13
+ export declare function markFactSuperseded(t: TenantContext, oldFactId: number, newFactId: number): Promise<void>;
14
+ export declare function factsCount(t: TenantContext): Promise<number>;
15
+ export declare function listFacts(t: TenantContext, options?: {
16
+ limit?: number;
17
+ offset?: number;
18
+ latestOnly?: boolean;
19
+ category?: FactCategory;
20
+ }): Promise<Fact[]>;
21
+ export declare function listTemporalFacts(t: TenantContext, options?: {
22
+ includeExpired?: boolean;
23
+ limit?: number;
24
+ }): Promise<Fact[]>;
25
+ export declare function searchFactsFts(t: TenantContext, query: string, limit?: number): Promise<Fact[]>;
26
+ //# sourceMappingURL=fact-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fact-store.d.ts","sourceRoot":"","sources":["../src/fact-store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAC;AAGrF,eAAO,MAAM,gBAAgB,6GAG3B,CAAC;AAgGH,wBAAsB,gBAAgB,CAAC,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtE;AAED,wBAAsB,SAAS,CAC7B,CAAC,EAAE,aAAa,EAChB,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,WAAW,GAAG,UAAU,GAAG,aAAa,GAAG,aAAa,CAAC,GAChF,OAAO,CAAC,MAAM,CAAC,CA2BjB;AAED,wBAAsB,WAAW,CAC/B,CAAC,EAAE,aAAa,EAChB,MAAM,EAAE,MAAM,EACd,WAAW,GAAE,MAA0B,GACtC,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAsB,aAAa,CAAC,CAAC,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAInF;AAED,wBAAsB,WAAW,CAAC,CAAC,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAKxF;AAED,wBAAsB,iBAAiB,CACrC,CAAC,EAAE,aAAa,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,YAAY,CAAA;CAAO,GAC9E,OAAO,CAAC,IAAI,EAAE,CAAC,CAqBjB;AAED,wBAAsB,kBAAkB,CACtC,CAAC,EAAE,aAAa,EAChB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAsB,UAAU,CAAC,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAKlE;AAED,wBAAsB,SAAS,CAC7B,CAAC,EAAE,aAAa,EAChB,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,YAAY,CAAA;CAAO,GAC/F,OAAO,CAAC,IAAI,EAAE,CAAC,CAcjB;AAED,wBAAsB,iBAAiB,CACrC,CAAC,EAAE,aAAa,EAChB,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GACzD,OAAO,CAAC,IAAI,EAAE,CAAC,CAYjB;AAED,wBAAsB,cAAc,CAClC,CAAC,EAAE,aAAa,EAChB,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,MAAW,GACjB,OAAO,CAAC,IAAI,EAAE,CAAC,CAgBjB"}
@@ -0,0 +1,181 @@
1
+ import { getDb } from '@kybernesis/brain-storage-sqlite';
2
+ import { sanitizeFtsQuery } from './fts-sanitizer.js';
3
+ export const VALID_CATEGORIES = new Set([
4
+ 'biographical', 'preference', 'event', 'relationship',
5
+ 'temporal', 'opinion', 'plan', 'general',
6
+ ]);
7
+ function rowToFact(row) {
8
+ return {
9
+ id: row.id,
10
+ content: row.content,
11
+ sourcePath: row.source_path,
12
+ sourceConversationId: row.source_conversation_id,
13
+ entities: JSON.parse(row.entities_json || '[]'),
14
+ timestamp: row.timestamp,
15
+ confidence: row.confidence,
16
+ category: row.category,
17
+ createdAt: row.created_at,
18
+ isLatest: (row.is_latest ?? 1) === 1,
19
+ supersededBy: row.superseded_by ?? undefined,
20
+ expiresAt: row.expires_at ?? undefined,
21
+ updatedAt: row.updated_at ?? undefined,
22
+ accessCount: row.access_count ?? 0,
23
+ sourceType: row.source_type ?? 'chat',
24
+ isRetracted: (row.is_retracted ?? 0) === 1,
25
+ retractedBy: row.retracted_by ?? undefined,
26
+ lastReinforcedAt: row.last_reinforced_at ?? undefined,
27
+ projectId: row.project_id ?? undefined,
28
+ tags: JSON.parse(row.tags_json || '[]'),
29
+ classification: row.classification ?? undefined,
30
+ connectionId: row.connection_id ?? undefined,
31
+ sourceDid: row.source_did ?? undefined,
32
+ };
33
+ }
34
+ // ─── Schema initialization ────────────────────────────────────────────────────
35
+ // Facts table lives in timeline.db (same file as timeline_events).
36
+ // Schema is created by timeline.ts ensureSchema. This module only ensures
37
+ // the FTS index and triggers exist.
38
+ const initialized = new Set();
39
+ function ensureFactsFts(t) {
40
+ if (initialized.has(t.slug))
41
+ return;
42
+ const db = getDb(t, 'timeline');
43
+ // Bug Fix 1 (CANONICAL.md §6): The non-contentless facts_fts table must use
44
+ // explicit DELETE FROM ... WHERE rowid = ? in triggers, NOT the ('delete', ...)
45
+ // command form which is only valid for contentless FTS5 tables. Using the wrong
46
+ // form threw SqliteError on every UPDATE/DELETE, silently swallowed by empty
47
+ // catch{} in decay.ts, meaning KAD has NEVER expired temporal facts in production.
48
+ db.exec(`
49
+ CREATE VIRTUAL TABLE IF NOT EXISTS facts_fts USING fts5(content, entities);
50
+
51
+ CREATE TRIGGER IF NOT EXISTS facts_fts_ai AFTER INSERT ON facts BEGIN
52
+ INSERT INTO facts_fts(rowid, content, entities) VALUES (new.id, new.content, new.entities_json);
53
+ END;
54
+
55
+ CREATE TRIGGER IF NOT EXISTS facts_fts_ad AFTER DELETE ON facts BEGIN
56
+ DELETE FROM facts_fts WHERE rowid = old.id;
57
+ END;
58
+
59
+ CREATE TRIGGER IF NOT EXISTS facts_fts_au AFTER UPDATE ON facts BEGIN
60
+ DELETE FROM facts_fts WHERE rowid = old.id;
61
+ INSERT INTO facts_fts(rowid, content, entities) VALUES (new.id, new.content, new.entities_json);
62
+ END;
63
+ `);
64
+ initialized.add(t.slug);
65
+ }
66
+ // ─── Operations ───────────────────────────────────────────────────────────────
67
+ export async function ensureFactsTable(t) {
68
+ ensureFactsFts(t);
69
+ }
70
+ export async function storeFact(t, fact) {
71
+ ensureFactsFts(t);
72
+ const db = getDb(t, 'timeline');
73
+ const result = db.prepare(`
74
+ INSERT OR REPLACE INTO facts
75
+ (content, source_path, source_conversation_id, entities_json, timestamp, confidence, category,
76
+ expires_at, source_type, project_id, tags_json, classification, connection_id, source_did)
77
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
78
+ `).run(fact.content, fact.sourcePath, fact.sourceConversationId, JSON.stringify(fact.entities), fact.timestamp, fact.confidence, fact.category, fact.expiresAt ?? null, fact.sourceType ?? 'chat', fact.projectId ?? null, JSON.stringify(fact.tags ?? []), fact.classification ?? null, fact.connectionId ?? null, fact.sourceDid ?? null);
79
+ return result.lastInsertRowid;
80
+ }
81
+ export async function retractFact(t, factId, retractedBy = 'user-correction') {
82
+ ensureFactsFts(t);
83
+ const db = getDb(t, 'timeline');
84
+ db.prepare(`
85
+ UPDATE facts SET is_retracted = 1, retracted_by = ?, is_latest = 0, updated_at = datetime('now')
86
+ WHERE id = ?
87
+ `).run(retractedBy, factId);
88
+ }
89
+ export async function reinforceFact(t, factId) {
90
+ ensureFactsFts(t);
91
+ const db = getDb(t, 'timeline');
92
+ db.prepare(`UPDATE facts SET last_reinforced_at = datetime('now'), updated_at = datetime('now') WHERE id = ?`).run(factId);
93
+ }
94
+ export async function getFactById(t, factId) {
95
+ ensureFactsFts(t);
96
+ const db = getDb(t, 'timeline');
97
+ const row = db.prepare('SELECT * FROM facts WHERE id = ?').get(factId);
98
+ return row ? rowToFact(row) : null;
99
+ }
100
+ export async function getFactsForEntity(t, entity, options = {}) {
101
+ const { latestOnly = true, limit = 20, category } = options;
102
+ ensureFactsFts(t);
103
+ const db = getDb(t, 'timeline');
104
+ const escapedEntity = entity.toLowerCase().replace(/[%_\\]/g, ch => `\\${ch}`);
105
+ const entityPattern = `%${escapedEntity}%`;
106
+ let sql = `
107
+ SELECT * FROM facts
108
+ WHERE LOWER(entities_json) LIKE ? ESCAPE '\\'
109
+ AND COALESCE(is_retracted, 0) = 0
110
+ `;
111
+ const params = [entityPattern];
112
+ if (latestOnly) {
113
+ sql += ` AND COALESCE(is_latest, 1) = 1`;
114
+ }
115
+ if (category) {
116
+ sql += ` AND category = ?`;
117
+ params.push(category);
118
+ }
119
+ sql += ` ORDER BY timestamp DESC LIMIT ?`;
120
+ params.push(limit);
121
+ return db.prepare(sql).all(...params).map(rowToFact);
122
+ }
123
+ export async function markFactSuperseded(t, oldFactId, newFactId) {
124
+ ensureFactsFts(t);
125
+ const db = getDb(t, 'timeline');
126
+ db.prepare('UPDATE facts SET is_latest = 0, superseded_by = ? WHERE id = ?').run(newFactId, oldFactId);
127
+ }
128
+ export async function factsCount(t) {
129
+ ensureFactsFts(t);
130
+ const db = getDb(t, 'timeline');
131
+ const { count } = db.prepare('SELECT COUNT(*) as count FROM facts WHERE COALESCE(is_retracted, 0) = 0').get();
132
+ return count;
133
+ }
134
+ export async function listFacts(t, options = {}) {
135
+ const { limit = 50, offset = 0, latestOnly = true, category } = options;
136
+ ensureFactsFts(t);
137
+ const db = getDb(t, 'timeline');
138
+ let sql = 'SELECT * FROM facts WHERE COALESCE(is_retracted, 0) = 0';
139
+ const params = [];
140
+ if (latestOnly) {
141
+ sql += ' AND COALESCE(is_latest, 1) = 1';
142
+ }
143
+ if (category) {
144
+ sql += ' AND category = ?';
145
+ params.push(category);
146
+ }
147
+ sql += ' ORDER BY timestamp DESC LIMIT ? OFFSET ?';
148
+ params.push(limit, offset);
149
+ return db.prepare(sql).all(...params).map(rowToFact);
150
+ }
151
+ export async function listTemporalFacts(t, options = {}) {
152
+ const { includeExpired = false, limit = 50 } = options;
153
+ ensureFactsFts(t);
154
+ const db = getDb(t, 'timeline');
155
+ let sql = `SELECT * FROM facts WHERE expires_at IS NOT NULL AND COALESCE(is_retracted, 0) = 0`;
156
+ const params = [];
157
+ if (!includeExpired) {
158
+ sql += ` AND (expires_at IS NULL OR expires_at > datetime('now'))`;
159
+ }
160
+ sql += ' ORDER BY expires_at ASC LIMIT ?';
161
+ params.push(limit);
162
+ return db.prepare(sql).all(...params).map(rowToFact);
163
+ }
164
+ export async function searchFactsFts(t, query, limit = 20) {
165
+ ensureFactsFts(t);
166
+ const db = getDb(t, 'timeline');
167
+ const sanitized = sanitizeFtsQuery(query);
168
+ if (!sanitized)
169
+ return [];
170
+ try {
171
+ const rowids = db.prepare(`SELECT rowid FROM facts_fts WHERE facts_fts MATCH ? LIMIT ?`).all(sanitized, limit);
172
+ if (rowids.length === 0)
173
+ return [];
174
+ const placeholders = rowids.map(() => '?').join(',');
175
+ return db.prepare(`SELECT * FROM facts WHERE id IN (${placeholders})`).all(...rowids.map(r => r.rowid)).map(rowToFact);
176
+ }
177
+ catch {
178
+ return [];
179
+ }
180
+ }
181
+ //# sourceMappingURL=fact-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fact-store.js","sourceRoot":"","sources":["../src/fact-store.ts"],"names":[],"mappings":"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"}