@199-bio/engram 0.7.4 → 0.8.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.
@@ -1 +1 @@
1
- {"version":3,"file":"consolidator.d.ts","sourceRoot":"","sources":["../../src/consolidation/consolidator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,cAAc,EAAU,MAAM,EAAW,MAAM,wBAAwB,CAAC;AAEjF,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AA0FtD,UAAU,kBAAkB;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2BAA2B,CAAC,EAAE,MAAM,CAAC;CACtC;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,EAAE,CAAiB;IAC3B,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,MAAM,CAA6B;gBAGzC,EAAE,EAAE,cAAc,EAClB,KAAK,CAAC,EAAE,cAAc,EACtB,MAAM,CAAC,EAAE,YAAY;IAYvB,YAAY,IAAI,OAAO;IAIvB;;;OAGG;IACG,WAAW,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC;QAC3D,cAAc,EAAE,MAAM,CAAC;QACvB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,iBAAiB,EAAE,MAAM,CAAC;KAC3B,CAAC;IAwEF;;OAEG;YACW,gBAAgB;IAoE9B;;OAEG;IACG,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAuHjE;;OAEG;IACH,SAAS,IAAI;QACX,UAAU,EAAE,OAAO,CAAC;QACpB,sBAAsB,EAAE,MAAM,CAAC;QAC/B,sBAAsB,EAAE,MAAM,CAAC;QAC/B,YAAY,EAAE,MAAM,CAAC;QACrB,wBAAwB,EAAE,MAAM,CAAC;KAClC;IAeD;;;OAGG;IACG,mBAAmB,CAAC,OAAO,GAAE;QACjC,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;KACf,GAAG,OAAO,CAAC;QACf,iBAAiB,EAAE,MAAM,CAAC;QAC1B,eAAe,EAAE,MAAM,CAAC;QACxB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;IAwFF;;OAEG;YACW,2BAA2B;IAqDzC;;;OAGG;IACG,aAAa,IAAI,OAAO,CAAC;QAC7B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,eAAe,EAAE,MAAM,CAAC;QACxB,cAAc,EAAE,MAAM,CAAC;QACvB,mBAAmB,EAAE,MAAM,CAAC;KAC7B,CAAC;CAkBH"}
1
+ {"version":3,"file":"consolidator.d.ts","sourceRoot":"","sources":["../../src/consolidation/consolidator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,cAAc,EAAU,MAAM,EAAW,MAAM,wBAAwB,CAAC;AAEjF,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AA0FtD,UAAU,kBAAkB;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2BAA2B,CAAC,EAAE,MAAM,CAAC;CACtC;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,EAAE,CAAiB;IAC3B,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,MAAM,CAA6B;gBAGzC,EAAE,EAAE,cAAc,EAClB,KAAK,CAAC,EAAE,cAAc,EACtB,MAAM,CAAC,EAAE,YAAY;IAYvB,YAAY,IAAI,OAAO;IAIvB;;;OAGG;IACG,WAAW,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC;QAC3D,cAAc,EAAE,MAAM,CAAC;QACvB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,iBAAiB,EAAE,MAAM,CAAC;KAC3B,CAAC;IAwEF;;OAEG;YACW,gBAAgB;IAoE9B;;OAEG;IACG,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAuHjE;;OAEG;IACH,SAAS,IAAI;QACX,UAAU,EAAE,OAAO,CAAC;QACpB,sBAAsB,EAAE,MAAM,CAAC;QAC/B,sBAAsB,EAAE,MAAM,CAAC;QAC/B,YAAY,EAAE,MAAM,CAAC;QACrB,wBAAwB,EAAE,MAAM,CAAC;KAClC;IAeD;;;OAGG;IACG,mBAAmB,CAAC,OAAO,GAAE;QACjC,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;KACf,GAAG,OAAO,CAAC;QACf,iBAAiB,EAAE,MAAM,CAAC;QAC1B,eAAe,EAAE,MAAM,CAAC;QACxB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;IAwFF;;OAEG;YACW,2BAA2B;IAqDzC;;;OAGG;IACG,aAAa,IAAI,OAAO,CAAC;QAC7B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,eAAe,EAAE,MAAM,CAAC;QACxB,cAAc,EAAE,MAAM,CAAC;QACvB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,kBAAkB,EAAE,MAAM,CAAC;QAC3B,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;CA4BH"}
package/dist/index.js CHANGED
@@ -58,7 +58,7 @@ async function initialize() {
58
58
  // ============ MCP Server ============
59
59
  const server = new Server({
60
60
  name: "engram",
61
- version: "0.7.2",
61
+ version: "0.8.0",
62
62
  }, {
63
63
  capabilities: {
64
64
  tools: {},
@@ -257,6 +257,37 @@ const TOOLS = [
257
257
  openWorldHint: true, // Calls Anthropic API for consolidation
258
258
  },
259
259
  },
260
+ {
261
+ name: "memory_feedback",
262
+ description: "Signal which memories from a recall were actually useful. Call AFTER using memories to answer user's question. This enables the memory system to learn which memories help together (Hebbian learning). Optional but improves memory quality over time.",
263
+ inputSchema: {
264
+ type: "object",
265
+ properties: {
266
+ recall_id: {
267
+ type: "string",
268
+ description: "The recall_id from the recall response",
269
+ },
270
+ useful_memory_ids: {
271
+ type: "array",
272
+ items: { type: "string" },
273
+ description: "IDs of memories that actually helped answer the question. Empty array if none were useful.",
274
+ },
275
+ need_more: {
276
+ type: "boolean",
277
+ description: "Set true if the memories were insufficient and you need a deeper search with more results",
278
+ default: false,
279
+ },
280
+ },
281
+ required: ["recall_id", "useful_memory_ids"],
282
+ },
283
+ annotations: {
284
+ title: "Memory Feedback",
285
+ readOnlyHint: false,
286
+ destructiveHint: false,
287
+ idempotentHint: true,
288
+ openWorldHint: false,
289
+ },
290
+ },
260
291
  ];
261
292
  // List available tools
262
293
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
@@ -314,11 +345,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
314
345
  }
315
346
  case "recall": {
316
347
  const { query, limit = 5, include_graph = true } = args;
317
- const results = await search.search(query, {
348
+ const response = await search.search(query, {
318
349
  limit,
319
350
  includeGraph: include_graph,
320
351
  });
321
- const formatted = results.map((r) => ({
352
+ const formatted = response.results.map((r) => ({
322
353
  id: r.memory.id,
323
354
  content: r.memory.content,
324
355
  source: r.memory.source,
@@ -330,14 +361,24 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
330
361
  .map(([k]) => k)
331
362
  .join(", "),
332
363
  }));
364
+ // Format connected memories (Hebbian associations)
365
+ const connectedFormatted = response.connected_memories.map((c) => ({
366
+ id: c.memory.id,
367
+ content: c.memory.content,
368
+ connected_to: c.connected_to,
369
+ connection_strength: c.strength.toFixed(2),
370
+ }));
333
371
  return {
334
372
  content: [
335
373
  {
336
374
  type: "text",
337
375
  text: JSON.stringify({
376
+ recall_id: response.recall_id, // For memory_feedback
338
377
  query,
339
378
  results: formatted,
340
379
  count: formatted.length,
380
+ connected_memories: connectedFormatted,
381
+ hint: formatted.length > 0 ? "Call memory_feedback with useful_memory_ids after answering" : undefined,
341
382
  }, null, 2),
342
383
  },
343
384
  ],
@@ -497,6 +538,81 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
497
538
  ],
498
539
  };
499
540
  }
541
+ case "memory_feedback": {
542
+ const { recall_id, useful_memory_ids, need_more = false } = args;
543
+ // Update the retrieval log with feedback
544
+ const updated = db.updateRetrievalFeedback(recall_id, useful_memory_ids, need_more);
545
+ if (!updated) {
546
+ return {
547
+ content: [
548
+ {
549
+ type: "text",
550
+ text: JSON.stringify({
551
+ success: false,
552
+ error: `Recall ID not found: ${recall_id}`,
553
+ }),
554
+ },
555
+ ],
556
+ };
557
+ }
558
+ // If need_more is set, do an expanded search
559
+ if (need_more) {
560
+ const expandedResponse = await search.expandSearch(recall_id);
561
+ const formatted = expandedResponse.results.map((r) => ({
562
+ id: r.memory.id,
563
+ content: r.memory.content,
564
+ source: r.memory.source,
565
+ timestamp: r.memory.timestamp.toISOString(),
566
+ relevance_score: r.score.toFixed(4),
567
+ matched_via: Object.entries(r.sources)
568
+ .filter(([, v]) => v !== undefined)
569
+ .map(([k]) => k)
570
+ .join(", "),
571
+ }));
572
+ return {
573
+ content: [
574
+ {
575
+ type: "text",
576
+ text: JSON.stringify({
577
+ success: true,
578
+ feedback_recorded: true,
579
+ useful_count: useful_memory_ids.length,
580
+ expanded_search: true,
581
+ additional_results: formatted,
582
+ additional_count: formatted.length,
583
+ }, null, 2),
584
+ },
585
+ ],
586
+ };
587
+ }
588
+ // Process deferred learning if we have enough feedback
589
+ const unprocessed = db.getUnprocessedRetrievalLogs(50);
590
+ let learningApplied = 0;
591
+ for (const log of unprocessed) {
592
+ if (log.useful_ids && log.useful_ids.length >= 2) {
593
+ // Strengthen connections between useful memories
594
+ db.recordCoUseful(log.useful_ids);
595
+ learningApplied++;
596
+ }
597
+ }
598
+ if (unprocessed.length > 0) {
599
+ db.markRetrievalLogsProcessed(unprocessed.map(l => l.id));
600
+ }
601
+ return {
602
+ content: [
603
+ {
604
+ type: "text",
605
+ text: JSON.stringify({
606
+ success: true,
607
+ feedback_recorded: true,
608
+ useful_count: useful_memory_ids.length,
609
+ learning_applied: learningApplied > 0,
610
+ connections_strengthened: learningApplied,
611
+ }, null, 2),
612
+ },
613
+ ],
614
+ };
615
+ }
500
616
  default:
501
617
  throw new Error(`Unknown tool: ${name}`);
502
618
  }
@@ -1 +1 @@
1
- {"version":3,"file":"hybrid.d.ts","sourceRoot":"","sources":["../../src/retrieval/hybrid.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAA0B,MAAM,cAAc,CAAC;AAEzF,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE;QACP,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAwDD,qBAAa,YAAY;IAErB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,SAAS;gBAFT,EAAE,EAAE,cAAc,EAClB,KAAK,EAAE,cAAc,EACrB,SAAS,EAAE,gBAAgB,GAAG,eAAe;IAGvD;;;;;OAKG;IACG,MAAM,CACV,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,YAAY,CAAC,EAAE,OAAO,CAAC;KACnB,GACL,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAyIhC;;OAEG;YACW,UAAU;IASxB;;OAEG;YACW,cAAc;IAS5B;;;OAGG;IACH,OAAO,CAAC,aAAa;IAQrB;;;OAGG;YACW,WAAW;IA8BzB;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOhD;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAYhD;;OAEG;IACG,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGvD"}
1
+ {"version":3,"file":"hybrid.d.ts","sourceRoot":"","sources":["../../src/retrieval/hybrid.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAA0B,MAAM,cAAc,CAAC;AAEzF,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE;QACP,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,KAAK,CAAC;QACxB,MAAM,EAAE,MAAM,CAAC;QACf,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;CACJ;AAwDD,qBAAa,YAAY;IAIrB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,SAAS;IALnB,OAAO,CAAC,SAAS,CAAS;gBAGhB,EAAE,EAAE,cAAc,EAClB,KAAK,EAAE,cAAc,EACrB,SAAS,EAAE,gBAAgB,GAAG,eAAe;IAMvD;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAIxB;;;;;OAKG;IACG,MAAM,CACV,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,qBAAqB,CAAC,EAAE,MAAM,CAAC;QAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;KACnB,GACL,OAAO,CAAC,oBAAoB,CAAC;IAuNhC;;;OAGG;IACG,YAAY,CAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;QAAE,eAAe,CAAC,EAAE,MAAM,CAAA;KAAO,GACzC,OAAO,CAAC,oBAAoB,CAAC;IAyBhC;;OAEG;YACW,UAAU;IASxB;;OAEG;YACW,cAAc;IAS5B;;;OAGG;IACH,OAAO,CAAC,aAAa;IAQrB;;;OAGG;YACW,WAAW;IA8BzB;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOhD;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAYhD;;OAEG;IACG,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGvD"}
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/storage/database.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/storage/database.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@199-bio/engram",
3
- "version": "0.7.4",
3
+ "version": "0.8.0",
4
4
  "description": "Give Claude a perfect memory. Local-first MCP server with hybrid search.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -596,6 +596,8 @@ Respond with JSON only.`;
596
596
  memoriesCreated: number;
597
597
  digestsCreated: number;
598
598
  contradictionsFound: number;
599
+ connectionsDecayed: number;
600
+ logsCleanedUp: number;
599
601
  }> {
600
602
  console.error("[Consolidator] Starting sleep cycle...");
601
603
 
@@ -607,11 +609,21 @@ Respond with JSON only.`;
607
609
  const memoryResult = await this.consolidate();
608
610
  console.error(`[Consolidator] Memories: ${memoryResult.memoriesProcessed} → ${memoryResult.digestsCreated} digests`);
609
611
 
612
+ // Step 3: Decay unused Hebbian connections (memories that haven't fired together recently)
613
+ const connectionsDecayed = this.db.decayConnections(30, 0.9);
614
+ console.error(`[Consolidator] Connections decayed: ${connectionsDecayed}`);
615
+
616
+ // Step 4: Clean up old retrieval logs
617
+ const logsCleanedUp = this.db.cleanupRetrievalLogs(7);
618
+ console.error(`[Consolidator] Retrieval logs cleaned: ${logsCleanedUp}`);
619
+
610
620
  return {
611
621
  episodesProcessed: episodeResult.episodesProcessed,
612
622
  memoriesCreated: episodeResult.memoriesCreated,
613
623
  digestsCreated: memoryResult.digestsCreated,
614
624
  contradictionsFound: memoryResult.contradictionsFound,
625
+ connectionsDecayed,
626
+ logsCleanedUp,
615
627
  };
616
628
  }
617
629
  }
package/src/index.ts CHANGED
@@ -76,7 +76,7 @@ async function initialize(): Promise<void> {
76
76
  const server = new Server(
77
77
  {
78
78
  name: "engram",
79
- version: "0.7.2",
79
+ version: "0.8.0",
80
80
  },
81
81
  {
82
82
  capabilities: {
@@ -280,6 +280,37 @@ const TOOLS = [
280
280
  openWorldHint: true, // Calls Anthropic API for consolidation
281
281
  },
282
282
  },
283
+ {
284
+ name: "memory_feedback",
285
+ description: "Signal which memories from a recall were actually useful. Call AFTER using memories to answer user's question. This enables the memory system to learn which memories help together (Hebbian learning). Optional but improves memory quality over time.",
286
+ inputSchema: {
287
+ type: "object" as const,
288
+ properties: {
289
+ recall_id: {
290
+ type: "string",
291
+ description: "The recall_id from the recall response",
292
+ },
293
+ useful_memory_ids: {
294
+ type: "array",
295
+ items: { type: "string" },
296
+ description: "IDs of memories that actually helped answer the question. Empty array if none were useful.",
297
+ },
298
+ need_more: {
299
+ type: "boolean",
300
+ description: "Set true if the memories were insufficient and you need a deeper search with more results",
301
+ default: false,
302
+ },
303
+ },
304
+ required: ["recall_id", "useful_memory_ids"],
305
+ },
306
+ annotations: {
307
+ title: "Memory Feedback",
308
+ readOnlyHint: false,
309
+ destructiveHint: false,
310
+ idempotentHint: true,
311
+ openWorldHint: false,
312
+ },
313
+ },
283
314
  ];
284
315
 
285
316
  // List available tools
@@ -366,12 +397,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
366
397
  include_graph?: boolean;
367
398
  };
368
399
 
369
- const results = await search.search(query, {
400
+ const response = await search.search(query, {
370
401
  limit,
371
402
  includeGraph: include_graph,
372
403
  });
373
404
 
374
- const formatted = results.map((r) => ({
405
+ const formatted = response.results.map((r) => ({
375
406
  id: r.memory.id,
376
407
  content: r.memory.content,
377
408
  source: r.memory.source,
@@ -384,14 +415,25 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
384
415
  .join(", "),
385
416
  }));
386
417
 
418
+ // Format connected memories (Hebbian associations)
419
+ const connectedFormatted = response.connected_memories.map((c) => ({
420
+ id: c.memory.id,
421
+ content: c.memory.content,
422
+ connected_to: c.connected_to,
423
+ connection_strength: c.strength.toFixed(2),
424
+ }));
425
+
387
426
  return {
388
427
  content: [
389
428
  {
390
429
  type: "text" as const,
391
430
  text: JSON.stringify({
431
+ recall_id: response.recall_id, // For memory_feedback
392
432
  query,
393
433
  results: formatted,
394
434
  count: formatted.length,
435
+ connected_memories: connectedFormatted,
436
+ hint: formatted.length > 0 ? "Call memory_feedback with useful_memory_ids after answering" : undefined,
395
437
  }, null, 2),
396
438
  },
397
439
  ],
@@ -575,6 +617,95 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
575
617
  };
576
618
  }
577
619
 
620
+ case "memory_feedback": {
621
+ const { recall_id, useful_memory_ids, need_more = false } = args as {
622
+ recall_id: string;
623
+ useful_memory_ids: string[];
624
+ need_more?: boolean;
625
+ };
626
+
627
+ // Update the retrieval log with feedback
628
+ const updated = db.updateRetrievalFeedback(recall_id, useful_memory_ids, need_more);
629
+
630
+ if (!updated) {
631
+ return {
632
+ content: [
633
+ {
634
+ type: "text" as const,
635
+ text: JSON.stringify({
636
+ success: false,
637
+ error: `Recall ID not found: ${recall_id}`,
638
+ }),
639
+ },
640
+ ],
641
+ };
642
+ }
643
+
644
+ // If need_more is set, do an expanded search
645
+ if (need_more) {
646
+ const expandedResponse = await search.expandSearch(recall_id);
647
+
648
+ const formatted = expandedResponse.results.map((r) => ({
649
+ id: r.memory.id,
650
+ content: r.memory.content,
651
+ source: r.memory.source,
652
+ timestamp: r.memory.timestamp.toISOString(),
653
+ relevance_score: r.score.toFixed(4),
654
+ matched_via: Object.entries(r.sources)
655
+ .filter(([, v]) => v !== undefined)
656
+ .map(([k]) => k)
657
+ .join(", "),
658
+ }));
659
+
660
+ return {
661
+ content: [
662
+ {
663
+ type: "text" as const,
664
+ text: JSON.stringify({
665
+ success: true,
666
+ feedback_recorded: true,
667
+ useful_count: useful_memory_ids.length,
668
+ expanded_search: true,
669
+ additional_results: formatted,
670
+ additional_count: formatted.length,
671
+ }, null, 2),
672
+ },
673
+ ],
674
+ };
675
+ }
676
+
677
+ // Process deferred learning if we have enough feedback
678
+ const unprocessed = db.getUnprocessedRetrievalLogs(50);
679
+ let learningApplied = 0;
680
+
681
+ for (const log of unprocessed) {
682
+ if (log.useful_ids && log.useful_ids.length >= 2) {
683
+ // Strengthen connections between useful memories
684
+ db.recordCoUseful(log.useful_ids);
685
+ learningApplied++;
686
+ }
687
+ }
688
+
689
+ if (unprocessed.length > 0) {
690
+ db.markRetrievalLogsProcessed(unprocessed.map(l => l.id));
691
+ }
692
+
693
+ return {
694
+ content: [
695
+ {
696
+ type: "text" as const,
697
+ text: JSON.stringify({
698
+ success: true,
699
+ feedback_recorded: true,
700
+ useful_count: useful_memory_ids.length,
701
+ learning_applied: learningApplied > 0,
702
+ connections_strengthened: learningApplied,
703
+ }, null, 2),
704
+ },
705
+ ],
706
+ };
707
+ }
708
+
578
709
  default:
579
710
  throw new Error(`Unknown tool: ${name}`);
580
711
  }
@@ -16,9 +16,20 @@ export interface HybridSearchResult {
16
16
  bm25?: number;
17
17
  semantic?: number;
18
18
  graph?: number;
19
+ connected?: number; // Rank from Hebbian connections
19
20
  };
20
21
  }
21
22
 
23
+ export interface HybridSearchResponse {
24
+ results: HybridSearchResult[];
25
+ recall_id: string; // For LLM feedback
26
+ connected_memories: Array<{
27
+ memory: Memory;
28
+ connected_to: string; // ID of the memory it's connected to
29
+ strength: number;
30
+ }>;
31
+ }
32
+
22
33
  /**
23
34
  * Calculate Ebbinghaus forgetting curve retention
24
35
  * R = e^(-t/S) where t=time since last access, S=stability
@@ -74,11 +85,23 @@ function adjustScore(memory: Memory, baseScore: number, now: Date): { adjusted:
74
85
  }
75
86
 
76
87
  export class HybridSearch {
88
+ private sessionId: string;
89
+
77
90
  constructor(
78
91
  private db: EngramDatabase,
79
92
  private graph: KnowledgeGraph,
80
93
  private retriever: ColBERTRetriever | SimpleRetriever
81
- ) {}
94
+ ) {
95
+ // Generate a session ID for this search instance
96
+ this.sessionId = `session_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
97
+ }
98
+
99
+ /**
100
+ * Generate a unique recall ID for tracking
101
+ */
102
+ private generateRecallId(): string {
103
+ return `recall_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
104
+ }
82
105
 
83
106
  /**
84
107
  * Search using all available methods and fuse results
@@ -91,21 +114,36 @@ export class HybridSearch {
91
114
  options: {
92
115
  limit?: number;
93
116
  includeGraph?: boolean;
117
+ includeConnections?: boolean; // Include Hebbian-connected memories
118
+ connectionBudget?: number; // How many results to allocate to connections (default: 30%)
119
+ minConnectionStrength?: number; // Minimum strength to include connections
94
120
  bm25Weight?: number;
95
121
  semanticWeight?: number;
96
122
  graphWeight?: number;
97
- useReranking?: boolean; // Use ColBERT to rerank BM25 results
123
+ connectionWeight?: number; // Weight for connected memories in RRF
124
+ useReranking?: boolean; // Use ColBERT to rerank BM25 results
98
125
  } = {}
99
- ): Promise<HybridSearchResult[]> {
126
+ ): Promise<HybridSearchResponse> {
100
127
  const {
101
128
  limit = 10,
102
129
  includeGraph = true,
130
+ includeConnections = true,
131
+ connectionBudget = 0.3, // 30% of results can be connected memories
132
+ minConnectionStrength = 0.3,
103
133
  bm25Weight = 1.0,
104
134
  semanticWeight = 1.0,
105
135
  graphWeight = 0.3,
106
- useReranking = true, // Default: reranking mode for better quality
136
+ connectionWeight = 0.5,
137
+ useReranking = true,
107
138
  } = options;
108
139
 
140
+ // Generate recall_id for tracking
141
+ const recallId = this.generateRecallId();
142
+
143
+ // Calculate budgets: 70% direct results, 30% connected
144
+ const directBudget = Math.ceil(limit * (1 - connectionBudget));
145
+ const connectedBudget = limit - directBudget;
146
+
109
147
  // Fetch more candidates than needed for fusion
110
148
  const candidateLimit = Math.max(limit * 3, 30);
111
149
 
@@ -139,11 +177,15 @@ export class HybridSearch {
139
177
  graphMemories.forEach(m => allCandidateIds.add(m.id));
140
178
 
141
179
  if (allCandidateIds.size === 0) {
142
- return [];
180
+ return {
181
+ results: [],
182
+ recall_id: recallId,
183
+ connected_memories: [],
184
+ };
143
185
  }
144
186
 
145
187
  // Create rankings for RRF
146
- const rankings: Map<string, { bm25?: number; semantic?: number; graph?: number }> = new Map();
188
+ const rankings: Map<string, { bm25?: number; semantic?: number; graph?: number; connected?: number }> = new Map();
147
189
 
148
190
  // BM25 ranking
149
191
  bm25Results.forEach((result, rank) => {
@@ -168,7 +210,7 @@ export class HybridSearch {
168
210
 
169
211
  // Calculate RRF scores
170
212
  const k = 60; // RRF constant
171
- const rrfScores: Array<{ id: string; score: number; sources: typeof rankings extends Map<string, infer V> ? V : never }> = [];
213
+ const rrfScores: Array<{ id: string; score: number; sources: { bm25?: number; semantic?: number; graph?: number; connected?: number } }> = [];
172
214
 
173
215
  for (const [id, ranks] of rankings) {
174
216
  let score = 0;
@@ -204,11 +246,7 @@ export class HybridSearch {
204
246
  score: adjusted,
205
247
  retention,
206
248
  originalScore: score,
207
- sources: {
208
- bm25: sources.bm25,
209
- semantic: sources.semantic,
210
- graph: sources.graph,
211
- },
249
+ sources,
212
250
  });
213
251
  }
214
252
  }
@@ -216,9 +254,44 @@ export class HybridSearch {
216
254
  // Re-sort by adjusted score (accounts for recency/stability)
217
255
  adjustedResults.sort((a, b) => b.score - a.score);
218
256
 
219
- // Take top results and update access counts
257
+ // Take top direct results
258
+ const directResults = adjustedResults.slice(0, directBudget);
259
+ const directIds = directResults.map(r => r.memory.id);
260
+
261
+ // Find Hebbian-connected memories (not already in direct results)
262
+ let connectedMemories: Array<{ memory: Memory; connected_to: string; strength: number }> = [];
263
+ if (includeConnections && directIds.length > 0) {
264
+ const connectedIds = this.db.getConnectedMemoryIds(directIds, minConnectionStrength, connectedBudget * 2);
265
+
266
+ for (const connId of connectedIds) {
267
+ if (directIds.includes(connId)) continue;
268
+
269
+ const memory = this.db.getMemory(connId);
270
+ if (!memory) continue;
271
+
272
+ // Find which direct result this is connected to
273
+ const connections = this.db.getMemoryConnections(connId, minConnectionStrength);
274
+ const connectedTo = connections.find(c =>
275
+ directIds.includes(c.memory_a === connId ? c.memory_b : c.memory_a)
276
+ );
277
+
278
+ if (connectedTo) {
279
+ connectedMemories.push({
280
+ memory,
281
+ connected_to: connectedTo.memory_a === connId ? connectedTo.memory_b : connectedTo.memory_a,
282
+ strength: connectedTo.strength,
283
+ });
284
+ }
285
+ }
286
+
287
+ // Sort by strength and limit
288
+ connectedMemories.sort((a, b) => b.strength - a.strength);
289
+ connectedMemories = connectedMemories.slice(0, connectedBudget);
290
+ }
291
+
292
+ // Build final results (direct + space for connected)
220
293
  const results: HybridSearchResult[] = [];
221
- for (const result of adjustedResults.slice(0, limit)) {
294
+ for (const result of directResults) {
222
295
  // Update access count (which also increases stability for future searches)
223
296
  this.db.touchMemory(result.memory.id);
224
297
 
@@ -230,7 +303,71 @@ export class HybridSearch {
230
303
  });
231
304
  }
232
305
 
233
- return results;
306
+ // Add connected memories to results with their own scores
307
+ for (const { memory, strength } of connectedMemories) {
308
+ const baseScore = strength * connectionWeight;
309
+ const { adjusted, retention } = adjustScore(memory, baseScore, now);
310
+
311
+ this.db.touchMemory(memory.id);
312
+
313
+ results.push({
314
+ memory,
315
+ score: adjusted,
316
+ retention,
317
+ sources: { connected: 1 },
318
+ });
319
+ }
320
+
321
+ // Track co-retrieval for Hebbian learning (all result IDs)
322
+ const allResultIds = results.map(r => r.memory.id);
323
+ if (allResultIds.length >= 2) {
324
+ this.db.recordCoRetrieval(allResultIds);
325
+ }
326
+
327
+ // Log this retrieval for deferred learning
328
+ try {
329
+ this.db.createRetrievalLog(this.sessionId, recallId, query, allResultIds);
330
+ } catch {
331
+ // Ignore duplicate recall_id errors (can happen with rapid queries)
332
+ }
333
+
334
+ return {
335
+ results,
336
+ recall_id: recallId,
337
+ connected_memories: connectedMemories,
338
+ };
339
+ }
340
+
341
+ /**
342
+ * Expanded search when LLM needs more memories
343
+ * Relaxes constraints and follows weaker connections
344
+ */
345
+ async expandSearch(
346
+ recallId: string,
347
+ options: { additionalLimit?: number } = {}
348
+ ): Promise<HybridSearchResponse> {
349
+ const { additionalLimit = 10 } = options;
350
+
351
+ // Get the original retrieval log
352
+ const log = this.db.getRetrievalLog(recallId);
353
+ if (!log) {
354
+ return { results: [], recall_id: recallId, connected_memories: [] };
355
+ }
356
+
357
+ // Search again with relaxed parameters
358
+ const response = await this.search(log.query, {
359
+ limit: additionalLimit + log.memory_ids.length,
360
+ includeConnections: true,
361
+ minConnectionStrength: 0.1, // Lower threshold
362
+ connectionBudget: 0.5, // More connected memories
363
+ });
364
+
365
+ // Filter out memories already returned
366
+ const existingIds = new Set(log.memory_ids);
367
+ response.results = response.results.filter(r => !existingIds.has(r.memory.id));
368
+ response.connected_memories = response.connected_memories.filter(c => !existingIds.has(c.memory.id));
369
+
370
+ return response;
234
371
  }
235
372
 
236
373
  /**
@@ -87,6 +87,38 @@ export interface Contradiction {
87
87
  resolved_at: Date | null;
88
88
  }
89
89
 
90
+ /**
91
+ * Hebbian connection between two memories
92
+ * "Neurons that fire together, wire together"
93
+ * Strength increases when memories are useful TOGETHER, not just co-retrieved
94
+ */
95
+ export interface MemoryConnection {
96
+ id: string;
97
+ memory_a: string;
98
+ memory_b: string;
99
+ strength: number; // 0-1, how strongly connected
100
+ co_retrievals: number; // Times retrieved together
101
+ co_useful: number; // Times BOTH were useful together (from LLM feedback)
102
+ last_fired: Date | null; // Last time this connection was activated
103
+ created_at: Date;
104
+ }
105
+
106
+ /**
107
+ * Log of retrieval sessions for deferred learning
108
+ * We don't update connections immediately - we batch process after N turns
109
+ */
110
+ export interface RetrievalLog {
111
+ id: string;
112
+ session_id: string;
113
+ recall_id: string; // Unique ID for this recall operation
114
+ query: string;
115
+ memory_ids: string[]; // All memories retrieved
116
+ useful_ids: string[] | null; // From LLM feedback (null = no feedback yet)
117
+ need_more: boolean; // LLM requested more memories
118
+ timestamp: Date;
119
+ processed: boolean; // Has this been used for learning?
120
+ }
121
+
90
122
  export class EngramDatabase {
91
123
  private db: Database.Database;
92
124
  private stmtCache: Map<string, Database.Statement> = new Map();
@@ -269,6 +301,45 @@ export class EngramDatabase {
269
301
  CREATE INDEX IF NOT EXISTS idx_contradictions_entity ON contradictions(entity_id);
270
302
  CREATE INDEX IF NOT EXISTS idx_contradictions_resolved ON contradictions(resolved);
271
303
  `);
304
+
305
+ // Memory connections table (Hebbian learning)
306
+ // "Neurons that fire together, wire together"
307
+ this.db.exec(`
308
+ CREATE TABLE IF NOT EXISTS memory_connections (
309
+ id TEXT PRIMARY KEY,
310
+ memory_a TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
311
+ memory_b TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
312
+ strength REAL DEFAULT 0.1,
313
+ co_retrievals INTEGER DEFAULT 0,
314
+ co_useful INTEGER DEFAULT 0,
315
+ last_fired DATETIME,
316
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
317
+ UNIQUE(memory_a, memory_b)
318
+ );
319
+
320
+ CREATE INDEX IF NOT EXISTS idx_connections_memory_a ON memory_connections(memory_a);
321
+ CREATE INDEX IF NOT EXISTS idx_connections_memory_b ON memory_connections(memory_b);
322
+ CREATE INDEX IF NOT EXISTS idx_connections_strength ON memory_connections(strength);
323
+ `);
324
+
325
+ // Retrieval logs for deferred learning
326
+ this.db.exec(`
327
+ CREATE TABLE IF NOT EXISTS retrieval_logs (
328
+ id TEXT PRIMARY KEY,
329
+ session_id TEXT NOT NULL,
330
+ recall_id TEXT NOT NULL UNIQUE,
331
+ query TEXT NOT NULL,
332
+ memory_ids TEXT NOT NULL,
333
+ useful_ids TEXT,
334
+ need_more INTEGER DEFAULT 0,
335
+ timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
336
+ processed INTEGER DEFAULT 0
337
+ );
338
+
339
+ CREATE INDEX IF NOT EXISTS idx_retrieval_logs_session ON retrieval_logs(session_id);
340
+ CREATE INDEX IF NOT EXISTS idx_retrieval_logs_recall ON retrieval_logs(recall_id);
341
+ CREATE INDEX IF NOT EXISTS idx_retrieval_logs_processed ON retrieval_logs(processed);
342
+ `);
272
343
  }
273
344
 
274
345
  /**
@@ -1043,6 +1114,220 @@ export class EngramDatabase {
1043
1114
  return result.changes > 0;
1044
1115
  }
1045
1116
 
1117
+ // ============ Memory Connections (Hebbian Learning) ============
1118
+
1119
+ /**
1120
+ * Get or create a connection between two memories
1121
+ * Always stores with smaller ID first for consistency
1122
+ */
1123
+ getOrCreateConnection(memoryA: string, memoryB: string): MemoryConnection {
1124
+ // Normalize order: smaller ID always first
1125
+ const [a, b] = memoryA < memoryB ? [memoryA, memoryB] : [memoryB, memoryA];
1126
+
1127
+ const existing = this.stmt(
1128
+ "SELECT * FROM memory_connections WHERE memory_a = ? AND memory_b = ?"
1129
+ ).get(a, b) as Record<string, unknown> | undefined;
1130
+
1131
+ if (existing) {
1132
+ return this.rowToConnection(existing);
1133
+ }
1134
+
1135
+ const id = randomUUID();
1136
+ this.db.prepare(`
1137
+ INSERT INTO memory_connections (id, memory_a, memory_b)
1138
+ VALUES (?, ?, ?)
1139
+ `).run(id, a, b);
1140
+
1141
+ return this.getConnection(id)!;
1142
+ }
1143
+
1144
+ getConnection(id: string): MemoryConnection | null {
1145
+ const row = this.stmt("SELECT * FROM memory_connections WHERE id = ?").get(id) as Record<string, unknown> | undefined;
1146
+ return row ? this.rowToConnection(row) : null;
1147
+ }
1148
+
1149
+ /**
1150
+ * Get all connections for a memory
1151
+ */
1152
+ getMemoryConnections(memoryId: string, minStrength: number = 0): MemoryConnection[] {
1153
+ const rows = this.stmt(`
1154
+ SELECT * FROM memory_connections
1155
+ WHERE (memory_a = ? OR memory_b = ?) AND strength >= ?
1156
+ ORDER BY strength DESC
1157
+ `).all(memoryId, memoryId, minStrength) as Record<string, unknown>[];
1158
+ return rows.map(r => this.rowToConnection(r));
1159
+ }
1160
+
1161
+ /**
1162
+ * Get connected memory IDs for a set of memories
1163
+ */
1164
+ getConnectedMemoryIds(memoryIds: string[], minStrength: number = 0.3, limit: number = 10): string[] {
1165
+ if (memoryIds.length === 0) return [];
1166
+
1167
+ const placeholders = memoryIds.map(() => "?").join(",");
1168
+ const rows = this.db.prepare(`
1169
+ SELECT DISTINCT
1170
+ CASE WHEN memory_a IN (${placeholders}) THEN memory_b ELSE memory_a END as connected_id,
1171
+ strength
1172
+ FROM memory_connections
1173
+ WHERE (memory_a IN (${placeholders}) OR memory_b IN (${placeholders}))
1174
+ AND strength >= ?
1175
+ ORDER BY strength DESC
1176
+ LIMIT ?
1177
+ `).all(...memoryIds, ...memoryIds, ...memoryIds, minStrength, limit) as Array<{ connected_id: string; strength: number }>;
1178
+
1179
+ // Filter out memories that are already in the input set
1180
+ const inputSet = new Set(memoryIds);
1181
+ return rows.map(r => r.connected_id).filter(id => !inputSet.has(id));
1182
+ }
1183
+
1184
+ /**
1185
+ * Record that memories were retrieved together (co-retrieval)
1186
+ */
1187
+ recordCoRetrieval(memoryIds: string[]): void {
1188
+ if (memoryIds.length < 2) return;
1189
+
1190
+ // Update all pairs
1191
+ for (let i = 0; i < memoryIds.length; i++) {
1192
+ for (let j = i + 1; j < memoryIds.length; j++) {
1193
+ const [a, b] = memoryIds[i] < memoryIds[j]
1194
+ ? [memoryIds[i], memoryIds[j]]
1195
+ : [memoryIds[j], memoryIds[i]];
1196
+
1197
+ this.db.prepare(`
1198
+ INSERT INTO memory_connections (id, memory_a, memory_b, co_retrievals, last_fired)
1199
+ VALUES (?, ?, ?, 1, CURRENT_TIMESTAMP)
1200
+ ON CONFLICT(memory_a, memory_b) DO UPDATE SET
1201
+ co_retrievals = co_retrievals + 1,
1202
+ last_fired = CURRENT_TIMESTAMP
1203
+ `).run(randomUUID(), a, b);
1204
+ }
1205
+ }
1206
+ }
1207
+
1208
+ /**
1209
+ * Record that memories were useful together (from LLM feedback)
1210
+ * This is what actually strengthens connections
1211
+ */
1212
+ recordCoUseful(memoryIds: string[]): void {
1213
+ if (memoryIds.length < 2) return;
1214
+
1215
+ for (let i = 0; i < memoryIds.length; i++) {
1216
+ for (let j = i + 1; j < memoryIds.length; j++) {
1217
+ const [a, b] = memoryIds[i] < memoryIds[j]
1218
+ ? [memoryIds[i], memoryIds[j]]
1219
+ : [memoryIds[j], memoryIds[i]];
1220
+
1221
+ // Get current stats to calculate new strength
1222
+ const conn = this.getOrCreateConnection(a, b);
1223
+ const newCoUseful = conn.co_useful + 1;
1224
+ const newCoRetrievals = Math.max(conn.co_retrievals, newCoUseful);
1225
+
1226
+ // Calculate strength with diminishing returns
1227
+ const usefulRatio = newCoUseful / Math.max(1, newCoRetrievals);
1228
+ const diminished = Math.sqrt(usefulRatio);
1229
+ const confidence = Math.min(1, newCoUseful / 3); // Require 3+ co-useful for strong connection
1230
+ const newStrength = diminished * confidence;
1231
+
1232
+ this.db.prepare(`
1233
+ UPDATE memory_connections
1234
+ SET co_useful = ?, strength = ?, last_fired = CURRENT_TIMESTAMP
1235
+ WHERE memory_a = ? AND memory_b = ?
1236
+ `).run(newCoUseful, newStrength, a, b);
1237
+ }
1238
+ }
1239
+ }
1240
+
1241
+ /**
1242
+ * Decay unused connections (called during consolidation)
1243
+ */
1244
+ decayConnections(daysThreshold: number = 30, decayFactor: number = 0.9): number {
1245
+ const result = this.db.prepare(`
1246
+ UPDATE memory_connections
1247
+ SET strength = strength * ?
1248
+ WHERE last_fired < datetime('now', '-' || ? || ' days')
1249
+ AND strength > 0.05
1250
+ `).run(decayFactor, daysThreshold);
1251
+
1252
+ // Clean up very weak connections
1253
+ this.db.prepare(`
1254
+ DELETE FROM memory_connections WHERE strength < 0.05
1255
+ `).run();
1256
+
1257
+ return result.changes;
1258
+ }
1259
+
1260
+ // ============ Retrieval Logs (Deferred Learning) ============
1261
+
1262
+ /**
1263
+ * Log a retrieval for later learning
1264
+ */
1265
+ createRetrievalLog(
1266
+ sessionId: string,
1267
+ recallId: string,
1268
+ query: string,
1269
+ memoryIds: string[]
1270
+ ): RetrievalLog {
1271
+ const id = randomUUID();
1272
+ this.db.prepare(`
1273
+ INSERT INTO retrieval_logs (id, session_id, recall_id, query, memory_ids)
1274
+ VALUES (?, ?, ?, ?, ?)
1275
+ `).run(id, sessionId, recallId, query, JSON.stringify(memoryIds));
1276
+ return this.getRetrievalLog(recallId)!;
1277
+ }
1278
+
1279
+ getRetrievalLog(recallId: string): RetrievalLog | null {
1280
+ const row = this.stmt("SELECT * FROM retrieval_logs WHERE recall_id = ?").get(recallId) as Record<string, unknown> | undefined;
1281
+ return row ? this.rowToRetrievalLog(row) : null;
1282
+ }
1283
+
1284
+ /**
1285
+ * Update retrieval log with LLM feedback
1286
+ */
1287
+ updateRetrievalFeedback(recallId: string, usefulIds: string[], needMore: boolean): boolean {
1288
+ const result = this.db.prepare(`
1289
+ UPDATE retrieval_logs
1290
+ SET useful_ids = ?, need_more = ?
1291
+ WHERE recall_id = ?
1292
+ `).run(JSON.stringify(usefulIds), needMore ? 1 : 0, recallId);
1293
+ return result.changes > 0;
1294
+ }
1295
+
1296
+ /**
1297
+ * Get unprocessed retrieval logs for learning
1298
+ */
1299
+ getUnprocessedRetrievalLogs(limit: number = 100): RetrievalLog[] {
1300
+ const rows = this.stmt(`
1301
+ SELECT * FROM retrieval_logs
1302
+ WHERE processed = 0 AND useful_ids IS NOT NULL
1303
+ ORDER BY timestamp ASC
1304
+ LIMIT ?
1305
+ `).all(limit) as Record<string, unknown>[];
1306
+ return rows.map(r => this.rowToRetrievalLog(r));
1307
+ }
1308
+
1309
+ /**
1310
+ * Mark retrieval logs as processed
1311
+ */
1312
+ markRetrievalLogsProcessed(ids: string[]): void {
1313
+ if (ids.length === 0) return;
1314
+ const placeholders = ids.map(() => "?").join(",");
1315
+ this.db.prepare(`
1316
+ UPDATE retrieval_logs SET processed = 1 WHERE id IN (${placeholders})
1317
+ `).run(...ids);
1318
+ }
1319
+
1320
+ /**
1321
+ * Clean up old retrieval logs
1322
+ */
1323
+ cleanupRetrievalLogs(daysOld: number = 7): number {
1324
+ const result = this.db.prepare(`
1325
+ DELETE FROM retrieval_logs
1326
+ WHERE processed = 1 AND timestamp < datetime('now', '-' || ? || ' days')
1327
+ `).run(daysOld);
1328
+ return result.changes;
1329
+ }
1330
+
1046
1331
  // ============ Statistics ============
1047
1332
 
1048
1333
  getStats(): {
@@ -1174,4 +1459,31 @@ export class EngramDatabase {
1174
1459
  resolved_at: row.resolved_at ? new Date(row.resolved_at as string) : null,
1175
1460
  };
1176
1461
  }
1462
+
1463
+ private rowToConnection(row: Record<string, unknown>): MemoryConnection {
1464
+ return {
1465
+ id: row.id as string,
1466
+ memory_a: row.memory_a as string,
1467
+ memory_b: row.memory_b as string,
1468
+ strength: row.strength as number,
1469
+ co_retrievals: row.co_retrievals as number,
1470
+ co_useful: row.co_useful as number,
1471
+ last_fired: row.last_fired ? new Date(row.last_fired as string) : null,
1472
+ created_at: new Date(row.created_at as string),
1473
+ };
1474
+ }
1475
+
1476
+ private rowToRetrievalLog(row: Record<string, unknown>): RetrievalLog {
1477
+ return {
1478
+ id: row.id as string,
1479
+ session_id: row.session_id as string,
1480
+ recall_id: row.recall_id as string,
1481
+ query: row.query as string,
1482
+ memory_ids: JSON.parse(row.memory_ids as string),
1483
+ useful_ids: row.useful_ids ? JSON.parse(row.useful_ids as string) : null,
1484
+ need_more: Boolean(row.need_more),
1485
+ timestamp: new Date(row.timestamp as string),
1486
+ processed: Boolean(row.processed),
1487
+ };
1488
+ }
1177
1489
  }
@@ -669,15 +669,15 @@ export class ChatHandler {
669
669
  const query = input.query as string;
670
670
  const limit = (input.limit as number) || 10;
671
671
 
672
- const results = await this.search.search(query, { limit });
672
+ const response = await this.search.search(query, { limit });
673
673
  return {
674
- results: results.map((r) => ({
674
+ results: response.results.map((r) => ({
675
675
  id: r.memory.id,
676
676
  content: r.memory.content.substring(0, 300) + (r.memory.content.length > 300 ? "..." : ""),
677
677
  timestamp: r.memory.timestamp.toISOString(),
678
678
  score: r.score.toFixed(3),
679
679
  })),
680
- count: results.length,
680
+ count: response.results.length,
681
681
  };
682
682
  }
683
683
 
package/src/web/server.ts CHANGED
@@ -241,9 +241,9 @@ export class EngramWebServer {
241
241
  const offset = parseInt(url.searchParams.get("offset") || "0");
242
242
 
243
243
  if (query) {
244
- const results = await this.search.search(query, { limit });
244
+ const response = await this.search.search(query, { limit });
245
245
  res.end(JSON.stringify({
246
- memories: results.map(r => ({
246
+ memories: response.results.map(r => ({
247
247
  ...r.memory,
248
248
  score: r.score,
249
249
  sources: r.sources,