@jungjaehoon/mama-core 1.1.2 → 1.1.3

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 (67) hide show
  1. package/dist/config-loader.d.ts +61 -0
  2. package/dist/config-loader.js +187 -0
  3. package/dist/db-adapter/base-adapter.d.ts +76 -0
  4. package/dist/db-adapter/base-adapter.js +11 -0
  5. package/dist/db-adapter/index.d.ts +24 -0
  6. package/dist/db-adapter/index.js +29 -0
  7. package/dist/db-adapter/sqlite-adapter.d.ts +73 -0
  8. package/dist/db-adapter/sqlite-adapter.js +330 -0
  9. package/dist/db-adapter/statement.d.ts +119 -0
  10. package/dist/db-adapter/statement.js +113 -0
  11. package/dist/db-manager.d.ts +252 -0
  12. package/dist/db-manager.js +622 -0
  13. package/dist/debug-logger.d.ts +32 -0
  14. package/dist/debug-logger.js +83 -0
  15. package/dist/decision-formatter.d.ts +172 -0
  16. package/dist/decision-formatter.js +894 -0
  17. package/dist/decision-tracker.d.ts +218 -0
  18. package/dist/decision-tracker.js +531 -0
  19. package/dist/embedding-cache.d.ts +83 -0
  20. package/dist/embedding-cache.js +182 -0
  21. package/dist/embedding-client.d.ts +43 -0
  22. package/dist/embedding-client.js +132 -0
  23. package/dist/embedding-server/index.d.ts +65 -0
  24. package/dist/embedding-server/index.js +397 -0
  25. package/dist/embedding-server/mobile/auth.d.ts +53 -0
  26. package/dist/embedding-server/mobile/auth.js +140 -0
  27. package/dist/embedding-server/mobile/daemon.d.ts +128 -0
  28. package/dist/embedding-server/mobile/daemon.js +303 -0
  29. package/dist/embedding-server/mobile/output-parser.d.ts +115 -0
  30. package/dist/embedding-server/mobile/output-parser.js +241 -0
  31. package/dist/embedding-server/mobile/session-api.d.ts +57 -0
  32. package/dist/embedding-server/mobile/session-api.js +261 -0
  33. package/dist/embedding-server/mobile/session-manager.d.ts +135 -0
  34. package/dist/embedding-server/mobile/session-manager.js +333 -0
  35. package/dist/embedding-server/mobile/websocket-handler.d.ts +127 -0
  36. package/dist/embedding-server/mobile/websocket-handler.js +435 -0
  37. package/dist/embeddings.d.ts +75 -0
  38. package/dist/embeddings.js +262 -0
  39. package/dist/errors.d.ts +131 -0
  40. package/dist/errors.js +225 -0
  41. package/dist/index.d.ts +32 -0
  42. package/dist/index.js +193 -0
  43. package/dist/mama-api.d.ts +954 -0
  44. package/dist/mama-api.js +2210 -0
  45. package/dist/memory-inject.d.ts +24 -0
  46. package/dist/memory-inject.js +116 -0
  47. package/dist/memory-store.d.ts +103 -0
  48. package/dist/memory-store.js +129 -0
  49. package/dist/notification-manager.d.ts +7 -0
  50. package/dist/notification-manager.js +12 -0
  51. package/dist/ollama-client.d.ts +51 -0
  52. package/dist/ollama-client.js +308 -0
  53. package/dist/outcome-tracker.d.ts +165 -0
  54. package/dist/outcome-tracker.js +315 -0
  55. package/dist/progress-indicator.d.ts +48 -0
  56. package/dist/progress-indicator.js +82 -0
  57. package/dist/query-intent.d.ts +27 -0
  58. package/dist/query-intent.js +144 -0
  59. package/dist/relevance-scorer.d.ts +124 -0
  60. package/dist/relevance-scorer.js +243 -0
  61. package/dist/test-utils.d.ts +66 -0
  62. package/dist/test-utils.js +166 -0
  63. package/dist/tier-validator.d.ts +55 -0
  64. package/dist/tier-validator.js +216 -0
  65. package/dist/time-formatter.d.ts +25 -0
  66. package/dist/time-formatter.js +93 -0
  67. package/package.json +3 -2
@@ -0,0 +1,531 @@
1
+ "use strict";
2
+ /**
3
+ * MAMA (Memory-Augmented MCP Architecture) - Decision Tracker
4
+ *
5
+ * Learn and store decisions with graph relationships
6
+ * Tasks: 3.1-3.9 (Learn decision, ID generation, supersedes edges, refinement, embeddings)
7
+ * AC #1: Decision stored with outcome=NULL, confidence from LLM
8
+ * AC #2: Supersedes relationship creation
9
+ * AC #5: Multi-parent refinement with confidence calculation
10
+ *
11
+ * Updated for PostgreSQL compatibility via db-manager
12
+ *
13
+ * @module decision-tracker
14
+ * @version 2.0
15
+ * @date 2025-11-17
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.VALID_EDGE_TYPES = void 0;
19
+ exports.generateDecisionId = generateDecisionId;
20
+ exports.getPreviousDecision = getPreviousDecision;
21
+ exports.createEdge = createEdge;
22
+ exports.createSupersedesEdge = createSupersedesEdge;
23
+ exports.markSuperseded = markSuperseded;
24
+ exports.calculateCombinedConfidence = calculateCombinedConfidence;
25
+ exports.detectRefinement = detectRefinement;
26
+ exports.parseReasoningForRelationships = parseReasoningForRelationships;
27
+ exports.createEdgesFromReasoning = createEdgesFromReasoning;
28
+ exports.getSupersededChainDepth = getSupersededChainDepth;
29
+ exports.learnDecision = learnDecision;
30
+ exports.updateConfidence = updateConfidence;
31
+ const debug_logger_js_1 = require("./debug-logger.js");
32
+ const memory_store_js_1 = require("./memory-store.js");
33
+ // ════════════════════════════════════════════════════════════════════════════
34
+ // Story 2.1: Extended Edge Types
35
+ // ════════════════════════════════════════════════════════════════════════════
36
+ // Valid relationship types for decision_edges
37
+ // Original: supersedes, refines, contradicts
38
+ // v1.3 Extension: builds_on, debates, synthesizes
39
+ exports.VALID_EDGE_TYPES = [
40
+ 'supersedes', // Original: New decision replaces old one
41
+ 'refines', // Original: Decision refines another
42
+ 'contradicts', // Original: Decision contradicts another
43
+ 'builds_on', // v1.3: Extends existing decision with new insights
44
+ 'debates', // v1.3: Presents counter-argument with evidence
45
+ 'synthesizes', // v1.3: Merges multiple decisions into unified approach
46
+ ];
47
+ /**
48
+ * Generate decision ID
49
+ *
50
+ * Task 3.2: Generate decision ID: `decision_${topic}_${timestamp}`
51
+ *
52
+ * @param topic - Decision topic
53
+ * @returns Decision ID
54
+ */
55
+ function generateDecisionId(topic) {
56
+ // Sanitize topic: remove spaces, lowercase, max 50 chars
57
+ const sanitized = topic
58
+ .toLowerCase()
59
+ .replace(/\s+/g, '_')
60
+ .replace(/[^a-z0-9_]/g, '')
61
+ .substring(0, 50);
62
+ const timestamp = Date.now();
63
+ const random = Math.random().toString(36).substring(2, 6);
64
+ return `decision_${sanitized}_${timestamp}_${random}`;
65
+ }
66
+ /**
67
+ * Check for previous decision on same topic
68
+ *
69
+ * Task 3.3: Query decisions table WHERE topic=? AND superseded_by IS NULL
70
+ * AC #2: Find previous decision to create supersedes relationship
71
+ *
72
+ * @param topic - Decision topic
73
+ * @returns Previous decision or null
74
+ */
75
+ async function getPreviousDecision(topic) {
76
+ const adapter = (0, memory_store_js_1.getAdapter)();
77
+ try {
78
+ const stmt = adapter.prepare(`
79
+ SELECT * FROM decisions
80
+ WHERE topic = ? AND superseded_by IS NULL
81
+ ORDER BY created_at DESC
82
+ LIMIT 1
83
+ `);
84
+ const previous = stmt.get(topic);
85
+ return previous || null;
86
+ }
87
+ catch (error) {
88
+ const message = error instanceof Error ? error.message : String(error);
89
+ throw new Error(`Failed to query previous decision: ${message}`);
90
+ }
91
+ }
92
+ /**
93
+ * Create a decision edge with specified relationship type
94
+ *
95
+ * Story 2.1: Generic edge creation supporting all relationship types
96
+ *
97
+ * @param fromId - Source decision ID
98
+ * @param toId - Target decision ID
99
+ * @param relationship - Edge type (supersedes, builds_on, debates, synthesizes, etc.)
100
+ * @param reason - Reason for the relationship
101
+ * @returns Success status
102
+ */
103
+ async function createEdge(fromId, toId, relationship, reason) {
104
+ const adapter = (0, memory_store_js_1.getAdapter)();
105
+ // Story 2.1: Runtime validation of edge types
106
+ if (!exports.VALID_EDGE_TYPES.includes(relationship)) {
107
+ throw new Error(`Invalid edge type: "${relationship}". Valid types: ${exports.VALID_EDGE_TYPES.join(', ')}`);
108
+ }
109
+ try {
110
+ // Note: SQLite CHECK constraint only allows supersedes/refines/contradicts
111
+ // New types (builds_on, debates, synthesizes) bypass CHECK via runtime validation
112
+ // The INSERT will fail for new types due to CHECK constraint
113
+ // WORKAROUND: Use PRAGMA ignore_check_constraints or recreate table
114
+ // For now, we'll catch the error and handle gracefully
115
+ // Story 2.1: LLM auto-detected edges are approved by default (approved_by_user=1)
116
+ // This allows them to appear in search results via querySemanticEdges
117
+ const stmt = adapter.prepare(`
118
+ INSERT OR REPLACE INTO decision_edges (from_id, to_id, relationship, reason, created_at, created_by, approved_by_user)
119
+ VALUES (?, ?, ?, ?, ?, 'llm', 1)
120
+ `);
121
+ stmt.run(fromId, toId, relationship, reason, Date.now());
122
+ return true;
123
+ }
124
+ catch (error) {
125
+ const message = error instanceof Error ? error.message : String(error);
126
+ // Handle CHECK constraint failure for new edge types
127
+ if (message.includes('CHECK constraint failed')) {
128
+ (0, debug_logger_js_1.info)(`[decision-tracker] Edge type "${relationship}" not yet supported in schema, skipping edge creation`);
129
+ return false;
130
+ }
131
+ throw new Error(`Failed to create ${relationship} edge: ${message}`);
132
+ }
133
+ }
134
+ /**
135
+ * Create supersedes edge
136
+ *
137
+ * Task 3.5: Create supersedes edge (INSERT INTO decision_edges)
138
+ * AC #2: Supersedes relationship creation
139
+ *
140
+ * @param fromId - New decision ID
141
+ * @param toId - Previous decision ID
142
+ * @param reason - Reason for superseding
143
+ */
144
+ async function createSupersedesEdge(fromId, toId, reason) {
145
+ return createEdge(fromId, toId, 'supersedes', reason);
146
+ }
147
+ /**
148
+ * Update previous decision's superseded_by field
149
+ *
150
+ * Task 3.5: Update previous decision's superseded_by field
151
+ * AC #2: Previous decision's superseded_by field updated
152
+ *
153
+ * @param previousId - Previous decision ID
154
+ * @param newId - New decision ID
155
+ */
156
+ async function markSuperseded(previousId, newId) {
157
+ const adapter = (0, memory_store_js_1.getAdapter)();
158
+ try {
159
+ const stmt = adapter.prepare(`
160
+ UPDATE decisions
161
+ SET superseded_by = ?, updated_at = ?
162
+ WHERE id = ?
163
+ `);
164
+ stmt.run(newId, Date.now(), previousId);
165
+ }
166
+ catch (error) {
167
+ const message = error instanceof Error ? error.message : String(error);
168
+ throw new Error(`Failed to mark decision as superseded: ${message}`);
169
+ }
170
+ }
171
+ /**
172
+ * Calculate combined confidence (Bayesian update)
173
+ *
174
+ * Task 3.6: Calculate combined confidence for multi-parent refinement
175
+ * AC #5: Confidence score calculated based on history
176
+ *
177
+ * @param prior - Prior confidence
178
+ * @param parents - Parent decisions
179
+ * @returns Updated confidence (0.0-1.0)
180
+ */
181
+ function calculateCombinedConfidence(prior, parents) {
182
+ if (!parents || parents.length === 0) {
183
+ return prior;
184
+ }
185
+ // Bayesian update: Average parent confidences + prior
186
+ const parentConfidences = parents.map((p) => p.confidence ?? 0.5);
187
+ const avgParentConfidence = parentConfidences.reduce((a, b) => a + b, 0) / parentConfidences.length;
188
+ // Weighted average: 60% prior, 40% parent history
189
+ const combined = prior * 0.6 + avgParentConfidence * 0.4;
190
+ // Clamp to [0.0, 1.0]
191
+ return Math.max(0, Math.min(1, combined));
192
+ }
193
+ /**
194
+ * Detect multi-parent refinement
195
+ *
196
+ * Task 3.6: Detect if new decision refines multiple previous decisions
197
+ * AC #5: Multi-parent refinement
198
+ *
199
+ * @param _detection - Decision detection result
200
+ * @param _sessionContext - Session context
201
+ * @returns Array of parent decision IDs or null
202
+ */
203
+ function detectRefinement(_detection, _sessionContext) {
204
+ // Refinement detection not implemented; currently returns null for single-parent only.
205
+ // Multi-parent refinement detection would analyze session context for references
206
+ // to multiple decisions (e.g., "combine", "merge", user mentioning multiple topics).
207
+ return null;
208
+ }
209
+ // ════════════════════════════════════════════════════════════════════════════
210
+ // Story 2.2: Reasoning Field Parsing
211
+ // ════════════════════════════════════════════════════════════════════════════
212
+ /**
213
+ * Parse reasoning field for relationship references
214
+ *
215
+ * Story 2.2: Detect patterns like:
216
+ * - builds_on: <decision_id>
217
+ * - debates: <decision_id>
218
+ * - synthesizes: [id1, id2]
219
+ *
220
+ * @param reasoning - Decision reasoning text
221
+ * @returns Detected relationships
222
+ */
223
+ function parseReasoningForRelationships(reasoning) {
224
+ if (!reasoning || typeof reasoning !== 'string') {
225
+ return [];
226
+ }
227
+ const relationships = [];
228
+ // Pattern 1: builds_on: <id> (allows optional markdown **bold**)
229
+ const buildsOnMatch = reasoning.match(/\*{0,2}builds_on\*{0,2}:\*{0,2}\s*(decision_[a-z0-9_]+)/gi);
230
+ if (buildsOnMatch) {
231
+ buildsOnMatch.forEach((match) => {
232
+ const id = match.replace(/\*{0,2}builds_on\*{0,2}:\*{0,2}\s*/i, '').trim();
233
+ if (id) {
234
+ relationships.push({ type: 'builds_on', targetIds: [id] });
235
+ }
236
+ });
237
+ }
238
+ // Pattern 2: debates: <id> (allows optional markdown **bold**)
239
+ const debatesMatch = reasoning.match(/\*{0,2}debates\*{0,2}:\*{0,2}\s*(decision_[a-z0-9_]+)/gi);
240
+ if (debatesMatch) {
241
+ debatesMatch.forEach((match) => {
242
+ const id = match.replace(/\*{0,2}debates\*{0,2}:\*{0,2}\s*/i, '').trim();
243
+ if (id) {
244
+ relationships.push({ type: 'debates', targetIds: [id] });
245
+ }
246
+ });
247
+ }
248
+ // Pattern 3: synthesizes: [id1, id2] (allows optional markdown **bold**)
249
+ const synthesizesMatch = reasoning.match(/\*{0,2}synthesizes\*{0,2}:\*{0,2}\s*\[?\s*(decision_[a-z0-9_]+(?:\s*,\s*decision_[a-z0-9_]+)*)\s*\]?/gi);
250
+ if (synthesizesMatch) {
251
+ synthesizesMatch.forEach((match) => {
252
+ const idsStr = match
253
+ .replace(/\*{0,2}synthesizes\*{0,2}:\*{0,2}\s*\[?\s*/i, '')
254
+ .replace(/\s*\]?\s*$/, '');
255
+ const ids = idsStr.split(/\s*,\s*/).filter((id) => id.startsWith('decision_'));
256
+ if (ids.length > 0) {
257
+ relationships.push({ type: 'synthesizes', targetIds: ids });
258
+ }
259
+ });
260
+ }
261
+ return relationships;
262
+ }
263
+ /**
264
+ * Create edges from parsed reasoning relationships
265
+ *
266
+ * Story 2.2: Auto-create edges when reasoning references other decisions
267
+ *
268
+ * @param fromId - Source decision ID
269
+ * @param reasoning - Decision reasoning text
270
+ * @returns Edge creation stats
271
+ */
272
+ async function createEdgesFromReasoning(fromId, reasoning) {
273
+ const relationships = parseReasoningForRelationships(reasoning);
274
+ let created = 0;
275
+ let failed = 0;
276
+ for (const rel of relationships) {
277
+ for (const targetId of rel.targetIds) {
278
+ try {
279
+ // Verify target decision exists
280
+ const adapter = (0, memory_store_js_1.getAdapter)();
281
+ const stmt = adapter.prepare('SELECT id FROM decisions WHERE id = ?');
282
+ const target = stmt.get(targetId);
283
+ if (!target) {
284
+ (0, debug_logger_js_1.info)(`[decision-tracker] Referenced decision not found: ${targetId}, skipping edge`);
285
+ failed++;
286
+ continue;
287
+ }
288
+ // Create the edge
289
+ const reason = `Auto-detected from reasoning: ${rel.type} reference`;
290
+ const success = await createEdge(fromId, targetId, rel.type, reason);
291
+ if (success) {
292
+ created++;
293
+ (0, debug_logger_js_1.info)(`[decision-tracker] Created ${rel.type} edge: ${fromId} -> ${targetId}`);
294
+ }
295
+ else {
296
+ failed++;
297
+ }
298
+ }
299
+ catch (error) {
300
+ const message = error instanceof Error ? error.message : String(error);
301
+ (0, debug_logger_js_1.info)(`[decision-tracker] Failed to create edge to ${targetId}: ${message}`);
302
+ failed++;
303
+ }
304
+ }
305
+ }
306
+ return { created, failed };
307
+ }
308
+ /**
309
+ * Get supersedes chain depth for a topic
310
+ *
311
+ * Story 2.2: Calculate how many times a topic has been superseded
312
+ *
313
+ * @param topic - Decision topic
314
+ * @returns Chain depth and decision IDs
315
+ */
316
+ async function getSupersededChainDepth(topic) {
317
+ const adapter = (0, memory_store_js_1.getAdapter)();
318
+ const chain = [];
319
+ try {
320
+ // Start from the latest decision (superseded_by IS NULL)
321
+ let stmt = adapter.prepare(`
322
+ SELECT id, supersedes FROM decisions
323
+ WHERE topic = ? AND superseded_by IS NULL
324
+ ORDER BY created_at DESC
325
+ LIMIT 1
326
+ `);
327
+ let current = stmt.get(topic);
328
+ if (!current) {
329
+ return { depth: 0, chain: [] };
330
+ }
331
+ chain.push(current.id);
332
+ // Walk back through supersedes chain with cycle detection
333
+ const visited = new Set([current.id]);
334
+ const MAX_CHAIN_DEPTH = 1000; // Safety limit
335
+ while (current && current.supersedes && chain.length < MAX_CHAIN_DEPTH) {
336
+ // Cycle detection: stop if we've seen this ID before
337
+ if (visited.has(current.supersedes)) {
338
+ break;
339
+ }
340
+ stmt = adapter.prepare('SELECT id, supersedes FROM decisions WHERE id = ?');
341
+ current = stmt.get(current.supersedes);
342
+ if (current) {
343
+ visited.add(current.id);
344
+ chain.push(current.id);
345
+ }
346
+ }
347
+ return {
348
+ depth: chain.length - 1, // depth = number of supersedes edges
349
+ chain: chain.reverse(), // oldest to newest
350
+ };
351
+ }
352
+ catch (error) {
353
+ const message = error instanceof Error ? error.message : String(error);
354
+ throw new Error(`Failed to get supersedes chain: ${message}`);
355
+ }
356
+ }
357
+ // ════════════════════════════════════════════════════════════════════════════
358
+ // NOTE: Auto-link functions REMOVED in v1.2.0
359
+ //
360
+ // Removed functions:
361
+ // - createRefinesEdge
362
+ // - detectConflicts
363
+ // - createContradictsEdge
364
+ // - findRelatedDecisions
365
+ // - isConflicting
366
+ //
367
+ // Reason: LLM can infer decision evolution from time-ordered search results.
368
+ // Auto-links created 366 noise edges (100% cross-topic).
369
+ // Only supersedes (same topic) is reliable.
370
+ //
371
+ // See: CHANGELOG.md v1.2.0 - 2025-11-25
372
+ // ════════════════════════════════════════════════════════════════════════════
373
+ /**
374
+ * Learn Decision Function (Main API)
375
+ *
376
+ * Task 3.1: Create Learn Decision Function
377
+ * Task 3.2: Generate decision ID
378
+ * Task 3.3: Check for previous decision on same topic
379
+ * Task 3.4: Insert new decision with outcome=NULL, confidence from LLM
380
+ * Task 3.5: If previous exists: Create supersedes edge, Update previous superseded_by
381
+ * Task 3.6: If multi-parent refinement: Store refined_from, Calculate combined confidence
382
+ * Task 3.7: Generate enhanced embedding
383
+ * Task 3.8: Store in vss_memories (link via rowid)
384
+ *
385
+ * AC #1: Decision stored with outcome=NULL, confidence from LLM
386
+ * AC #2: Supersedes relationship creation
387
+ * AC #5: Multi-parent refinement with confidence calculation
388
+ *
389
+ * @param detection - Decision detection result
390
+ * @param toolExecution - Tool execution data
391
+ * @param sessionContext - Session context
392
+ * @returns decisionId and notification
393
+ */
394
+ async function learnDecision(detection, toolExecution, sessionContext) {
395
+ try {
396
+ // Ensure database is initialized
397
+ await (0, memory_store_js_1.initDB)();
398
+ // ════════════════════════════════════════════════════════
399
+ // Task 3.2: Generate Decision ID
400
+ // ════════════════════════════════════════════════════════
401
+ const decisionId = generateDecisionId(detection.topic);
402
+ // ════════════════════════════════════════════════════════
403
+ // Task 3.3: Check for Previous Decision on Same Topic
404
+ // ════════════════════════════════════════════════════════
405
+ const previous = await getPreviousDecision(detection.topic);
406
+ // ════════════════════════════════════════════════════════
407
+ // Task 3.6: Detect Multi-Parent Refinement
408
+ // ════════════════════════════════════════════════════════
409
+ const refinedFrom = detectRefinement(detection, sessionContext);
410
+ let finalConfidence = detection.confidence;
411
+ if (refinedFrom && refinedFrom.length > 0) {
412
+ // AC #5: Multi-parent refinement
413
+ // Get parent decisions
414
+ const adapter = (0, memory_store_js_1.getAdapter)();
415
+ const stmt = adapter.prepare('SELECT * FROM decisions WHERE id = ?');
416
+ const parents = refinedFrom.map((parentId) => stmt.get(parentId));
417
+ const validParents = parents.filter((p) => p !== undefined && p !== null);
418
+ // Calculate combined confidence
419
+ finalConfidence = calculateCombinedConfidence(detection.confidence, validParents);
420
+ }
421
+ // ════════════════════════════════════════════════════════
422
+ // Task 3.4: Insert New Decision
423
+ // ════════════════════════════════════════════════════════
424
+ // ════════════════════════════════════════════════════════
425
+ // Story 014.7.6: Set needs_validation for assistant insights
426
+ // ════════════════════════════════════════════════════════
427
+ const isAssistantInsight = detection.type === 'assistant_insight';
428
+ const needsValidation = isAssistantInsight ? 1 : 0;
429
+ // AC #1: Decision stored with outcome=NULL, confidence from LLM
430
+ const decision = {
431
+ id: decisionId,
432
+ topic: detection.topic,
433
+ decision: detection.decision,
434
+ reasoning: detection.reasoning,
435
+ outcome: null, // AC #1: outcome=NULL (not yet tracked)
436
+ failure_reason: null,
437
+ limitation: null,
438
+ user_involvement: 'requested', // Inferred from tool execution
439
+ session_id: sessionContext.session_id,
440
+ supersedes: previous ? previous.id : null,
441
+ superseded_by: null,
442
+ refined_from: refinedFrom, // AC #5: Multi-parent refinement
443
+ confidence: finalConfidence, // AC #1, AC #5: Confidence from LLM
444
+ needs_validation: needsValidation, // Story 014.7.6: AC #1 - Validation for assistant insights
445
+ validation_attempts: 0, // Story 014.7.6: Track skip count
446
+ usage_count: 0, // Story 014.7.6: Track usage for periodic review
447
+ created_at: toolExecution.timestamp || Date.now(),
448
+ updated_at: Date.now(),
449
+ // Story 014.7.10: Add trust_context for Claude-Friendly Context Formatting
450
+ trust_context: detection.trust_context ? JSON.stringify(detection.trust_context) : null,
451
+ // Story 2.1: 5-layer narrative fields
452
+ evidence: detection.evidence
453
+ ? Array.isArray(detection.evidence)
454
+ ? JSON.stringify(detection.evidence)
455
+ : detection.evidence
456
+ : null,
457
+ alternatives: detection.alternatives
458
+ ? Array.isArray(detection.alternatives)
459
+ ? JSON.stringify(detection.alternatives)
460
+ : detection.alternatives
461
+ : null,
462
+ risks: detection.risks || null,
463
+ };
464
+ // Task 3.7, 3.8: Generate enhanced embedding and store in vss_memories
465
+ // (Handled by insertDecisionWithEmbedding function)
466
+ await (0, memory_store_js_1.insertDecisionWithEmbedding)(decision);
467
+ // ════════════════════════════════════════════════════════
468
+ // Task 3.5: Create Supersedes Relationship (if previous exists)
469
+ // ════════════════════════════════════════════════════════
470
+ if (previous) {
471
+ // AC #2: Supersedes relationship creation
472
+ const reason = `User changed from "${previous.decision}" to "${detection.decision}"`;
473
+ // Create edge: new decision → previous decision
474
+ await createSupersedesEdge(decisionId, previous.id, reason);
475
+ // Update previous decision's superseded_by field
476
+ await markSuperseded(previous.id, decisionId);
477
+ }
478
+ // ════════════════════════════════════════════════════════
479
+ // NOTE: Auto-link generation (refines, contradicts) REMOVED
480
+ //
481
+ // Reason: LLM can infer decision evolution from time-ordered
482
+ // search results. Auto-links created 366 noise edges (100%
483
+ // cross-topic). Only supersedes (same topic) is reliable.
484
+ //
485
+ // See: 2025-11-25 discussion on decision tracking algorithm
486
+ // ════════════════════════════════════════════════════════
487
+ // ════════════════════════════════════════════════════════
488
+ // Story 014.7.6: Generate notification if needs validation
489
+ // ════════════════════════════════════════════════════════
490
+ let notification = null;
491
+ if (needsValidation) {
492
+ const { notifyInsight } = await import('./notification-manager.js');
493
+ // notifyInsight is a stub that returns null
494
+ notification = notifyInsight();
495
+ }
496
+ // ════════════════════════════════════════════════════════
497
+ // Task 3.9: Return decision ID (+ notification for Story 014.7.6)
498
+ // ════════════════════════════════════════════════════════
499
+ return {
500
+ decisionId,
501
+ notification, // null if no validation needed, notification object otherwise
502
+ };
503
+ }
504
+ catch (error) {
505
+ // CLAUDE.md Rule #1: No silent failures
506
+ const message = error instanceof Error ? error.message : String(error);
507
+ throw new Error(`Failed to learn decision: ${message}`);
508
+ }
509
+ }
510
+ /**
511
+ * Update confidence score
512
+ *
513
+ * Task 6: Confidence evolution (used in outcome tracking)
514
+ * AC #5: Confidence score calculated based on history
515
+ *
516
+ * @param prior - Prior confidence
517
+ * @param evidence - Evidence items
518
+ * @returns Updated confidence (0.0-1.0)
519
+ */
520
+ function updateConfidence(prior, evidence) {
521
+ if (!evidence || evidence.length === 0) {
522
+ return prior;
523
+ }
524
+ // Calculate total impact
525
+ const totalImpact = evidence.reduce((acc, e) => acc + e.impact, 0);
526
+ // Update confidence
527
+ const updated = prior + totalImpact;
528
+ // Clamp to [0.0, 1.0]
529
+ return Math.max(0, Math.min(1, updated));
530
+ }
531
+ //# sourceMappingURL=decision-tracker.js.map
@@ -0,0 +1,83 @@
1
+ /**
2
+ * MAMA (Memory-Augmented MCP Architecture) - Embedding Cache
3
+ *
4
+ * LRU cache for embedding vectors to avoid re-computation
5
+ *
6
+ * Task 2: Implement Embedding Cache
7
+ * AC #3: Cache embeddings to avoid re-computation
8
+ * Target: > 80% cache hit ratio
9
+ *
10
+ * @module embedding-cache
11
+ */
12
+ export declare const MAX_CACHE_SIZE = 1000;
13
+ interface CacheStats {
14
+ hits: number;
15
+ misses: number;
16
+ evictions: number;
17
+ totalSize: number;
18
+ }
19
+ export interface CacheStatsReport extends CacheStats {
20
+ hitRatio: number;
21
+ size: number;
22
+ maxSize: number;
23
+ }
24
+ /**
25
+ * LRU Cache for embeddings
26
+ *
27
+ * Key: SHA-256 hash of decision text
28
+ * Value: {embedding: Float32Array, timestamp: number, hits: number}
29
+ *
30
+ * Eviction: Least Recently Used when size > MAX_CACHE_SIZE
31
+ */
32
+ export declare class EmbeddingCache {
33
+ private cache;
34
+ private stats;
35
+ constructor();
36
+ /**
37
+ * Generate cache key from text
38
+ *
39
+ * Task 2.3: Key = decision text hash (SHA-256)
40
+ */
41
+ generateKey(text: string): string;
42
+ /**
43
+ * Get embedding from cache
44
+ *
45
+ * Task 2.2: Cache hit updates LRU position
46
+ */
47
+ get(text: string): Float32Array | null;
48
+ /**
49
+ * Store embedding in cache
50
+ *
51
+ * Task 2.2: Add to cache with LRU tracking
52
+ * Task 2.5: Implement cache eviction (LRU)
53
+ */
54
+ set(text: string, embedding: Float32Array): void;
55
+ /**
56
+ * Evict least recently used entries
57
+ *
58
+ * Task 2.5: Implement cache eviction (LRU)
59
+ * Strategy: Remove entries until size <= MAX_CACHE_SIZE
60
+ *
61
+ * Eviction order:
62
+ * 1. Oldest lastAccessed (LRU)
63
+ * 2. If tied, lowest hits
64
+ */
65
+ evictLRU(): void;
66
+ /**
67
+ * Get cache hit ratio
68
+ *
69
+ * Task 2.4: Cache hit ratio target: > 80%
70
+ */
71
+ getHitRatio(): number;
72
+ /**
73
+ * Get cache statistics
74
+ */
75
+ getStats(): CacheStatsReport;
76
+ /**
77
+ * Clear cache
78
+ */
79
+ clear(): void;
80
+ }
81
+ export declare const embeddingCache: EmbeddingCache;
82
+ export {};
83
+ //# sourceMappingURL=embedding-cache.d.ts.map