agent-working-memory 0.6.0 → 0.6.1

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 (122) hide show
  1. package/README.md +15 -9
  2. package/dist/adapters/claude-code.d.ts +4 -0
  3. package/dist/adapters/claude-code.d.ts.map +1 -0
  4. package/dist/adapters/claude-code.js +218 -0
  5. package/dist/adapters/claude-code.js.map +1 -0
  6. package/dist/adapters/codex.d.ts +4 -0
  7. package/dist/adapters/codex.d.ts.map +1 -0
  8. package/dist/adapters/codex.js +226 -0
  9. package/dist/adapters/codex.js.map +1 -0
  10. package/dist/adapters/common.d.ts +34 -0
  11. package/dist/adapters/common.d.ts.map +1 -0
  12. package/dist/adapters/common.js +145 -0
  13. package/dist/adapters/common.js.map +1 -0
  14. package/dist/adapters/cursor.d.ts +4 -0
  15. package/dist/adapters/cursor.d.ts.map +1 -0
  16. package/dist/adapters/cursor.js +138 -0
  17. package/dist/adapters/cursor.js.map +1 -0
  18. package/dist/adapters/http.d.ts +4 -0
  19. package/dist/adapters/http.d.ts.map +1 -0
  20. package/dist/adapters/http.js +88 -0
  21. package/dist/adapters/http.js.map +1 -0
  22. package/dist/adapters/index.d.ts +7 -0
  23. package/dist/adapters/index.d.ts.map +1 -0
  24. package/dist/adapters/index.js +21 -0
  25. package/dist/adapters/index.js.map +1 -0
  26. package/dist/adapters/types.d.ts +65 -0
  27. package/dist/adapters/types.d.ts.map +1 -0
  28. package/dist/adapters/types.js +4 -0
  29. package/dist/adapters/types.js.map +1 -0
  30. package/dist/cli.js +104 -230
  31. package/dist/cli.js.map +1 -1
  32. package/dist/coordination/events.d.ts +59 -0
  33. package/dist/coordination/events.d.ts.map +1 -0
  34. package/dist/coordination/events.js +28 -0
  35. package/dist/coordination/events.js.map +1 -0
  36. package/dist/coordination/index.d.ts +10 -1
  37. package/dist/coordination/index.d.ts.map +1 -1
  38. package/dist/coordination/index.js +87 -3
  39. package/dist/coordination/index.js.map +1 -1
  40. package/dist/coordination/peer-decisions.d.ts +40 -0
  41. package/dist/coordination/peer-decisions.d.ts.map +1 -0
  42. package/dist/coordination/peer-decisions.js +82 -0
  43. package/dist/coordination/peer-decisions.js.map +1 -0
  44. package/dist/coordination/plugin-loader.d.ts +18 -0
  45. package/dist/coordination/plugin-loader.d.ts.map +1 -0
  46. package/dist/coordination/plugin-loader.js +55 -0
  47. package/dist/coordination/plugin-loader.js.map +1 -0
  48. package/dist/coordination/plugin.d.ts +40 -0
  49. package/dist/coordination/plugin.d.ts.map +1 -0
  50. package/dist/coordination/plugin.js +22 -0
  51. package/dist/coordination/plugin.js.map +1 -0
  52. package/dist/coordination/routes.d.ts +2 -1
  53. package/dist/coordination/routes.d.ts.map +1 -1
  54. package/dist/coordination/routes.js +899 -76
  55. package/dist/coordination/routes.js.map +1 -1
  56. package/dist/coordination/schema.d.ts.map +1 -1
  57. package/dist/coordination/schema.js +72 -14
  58. package/dist/coordination/schema.js.map +1 -1
  59. package/dist/coordination/schemas.d.ts +84 -3
  60. package/dist/coordination/schemas.d.ts.map +1 -1
  61. package/dist/coordination/schemas.js +71 -1
  62. package/dist/coordination/schemas.js.map +1 -1
  63. package/dist/coordination/stale.d.ts.map +1 -1
  64. package/dist/coordination/stale.js +2 -1
  65. package/dist/coordination/stale.js.map +1 -1
  66. package/dist/coordination/types.d.ts +252 -0
  67. package/dist/coordination/types.d.ts.map +1 -0
  68. package/dist/coordination/types.js +8 -0
  69. package/dist/coordination/types.js.map +1 -0
  70. package/dist/coordination/write-mutex.d.ts +26 -0
  71. package/dist/coordination/write-mutex.d.ts.map +1 -0
  72. package/dist/coordination/write-mutex.js +63 -0
  73. package/dist/coordination/write-mutex.js.map +1 -0
  74. package/dist/core/embeddings.d.ts +2 -0
  75. package/dist/core/embeddings.d.ts.map +1 -1
  76. package/dist/core/embeddings.js +4 -0
  77. package/dist/core/embeddings.js.map +1 -1
  78. package/dist/engine/activation.d.ts.map +1 -1
  79. package/dist/engine/activation.js +16 -3
  80. package/dist/engine/activation.js.map +1 -1
  81. package/dist/engine/consolidation.d.ts.map +1 -1
  82. package/dist/engine/consolidation.js +15 -6
  83. package/dist/engine/consolidation.js.map +1 -1
  84. package/dist/engine/retraction.d.ts +3 -1
  85. package/dist/engine/retraction.d.ts.map +1 -1
  86. package/dist/engine/retraction.js +19 -6
  87. package/dist/engine/retraction.js.map +1 -1
  88. package/dist/index.js +6 -18
  89. package/dist/index.js.map +1 -1
  90. package/dist/mcp.js +52 -3
  91. package/dist/mcp.js.map +1 -1
  92. package/dist/storage/sqlite.d.ts +6 -1
  93. package/dist/storage/sqlite.d.ts.map +1 -1
  94. package/dist/storage/sqlite.js +39 -3
  95. package/dist/storage/sqlite.js.map +1 -1
  96. package/package.json +1 -1
  97. package/src/adapters/claude-code.ts +234 -0
  98. package/src/adapters/codex.ts +262 -0
  99. package/src/adapters/common.ts +172 -0
  100. package/src/adapters/cursor.ts +150 -0
  101. package/src/adapters/http.ts +100 -0
  102. package/src/adapters/index.ts +31 -0
  103. package/src/adapters/types.ts +75 -0
  104. package/src/cli.ts +107 -238
  105. package/src/coordination/events.ts +90 -0
  106. package/src/coordination/index.ts +102 -3
  107. package/src/coordination/peer-decisions.ts +105 -0
  108. package/src/coordination/plugin-loader.ts +60 -0
  109. package/src/coordination/plugin.ts +44 -0
  110. package/src/coordination/routes.ts +1176 -105
  111. package/src/coordination/schema.ts +67 -14
  112. package/src/coordination/schemas.ts +85 -1
  113. package/src/coordination/stale.ts +3 -2
  114. package/src/coordination/types.ts +311 -0
  115. package/src/coordination/write-mutex.ts +69 -0
  116. package/src/core/embeddings.ts +5 -0
  117. package/src/engine/activation.ts +13 -3
  118. package/src/engine/consolidation.ts +15 -6
  119. package/src/engine/retraction.ts +22 -6
  120. package/src/index.ts +6 -15
  121. package/src/mcp.ts +73 -9
  122. package/src/storage/sqlite.ts +39 -3
@@ -203,10 +203,16 @@ export class ActivationEngine {
203
203
  // Phase 0: Query expansion — add related terms to improve BM25 recall
204
204
  let searchContext = queryContext;
205
205
  if (useExpansion) {
206
+ let timer: ReturnType<typeof setTimeout> | undefined;
206
207
  try {
207
- searchContext = await expandQuery(query.context);
208
+ searchContext = await Promise.race([
209
+ expandQuery(query.context),
210
+ new Promise<string>((_, reject) => { timer = setTimeout(() => reject(new Error('expansion timeout')), 5000); }),
211
+ ]);
208
212
  } catch {
209
- // Expansion unavailable — use original query
213
+ // Expansion unavailable or timed out — use original query
214
+ } finally {
215
+ if (timer) clearTimeout(timer);
210
216
  }
211
217
  }
212
218
 
@@ -522,7 +528,11 @@ export class ActivationEngine {
522
528
  const passages = rerankPool.map(r =>
523
529
  `${r.engram.concept}: ${r.engram.content}`
524
530
  );
525
- const rerankResults = await rerank(query.context, passages);
531
+ let rerankTimer: ReturnType<typeof setTimeout> | undefined;
532
+ const rerankResults = await Promise.race([
533
+ rerank(query.context, passages),
534
+ new Promise<never>((_, reject) => { rerankTimer = setTimeout(() => reject(new Error('reranker timeout')), 10000); }),
535
+ ]).finally(() => { if (rerankTimer) clearTimeout(rerankTimer); });
526
536
 
527
537
  // Adaptive reranker blend (Codex recommendation):
528
538
  // When BM25/text signals are strong, trust them more; when weak, lean on reranker.
@@ -144,13 +144,22 @@ export class ConsolidationEngine {
144
144
  const needsEmbedding = allActive.filter(e => !e.embedding || e.embedding.length === 0);
145
145
  if (needsEmbedding.length > 0) {
146
146
  try {
147
- const { embed } = await import('../core/embeddings.js');
148
- for (const e of needsEmbedding) {
147
+ const { embedBatch, getModelId } = await import('../core/embeddings.js');
148
+ const BATCH_SIZE = 32;
149
+ const modelId = getModelId?.() ?? 'unknown';
150
+ for (let b = 0; b < needsEmbedding.length; b += BATCH_SIZE) {
151
+ const batch = needsEmbedding.slice(b, b + BATCH_SIZE);
149
152
  try {
150
- const vec = await embed(`${e.concept} ${e.content}`);
151
- this.store.updateEmbedding(e.id, vec);
152
- e.embedding = vec;
153
- } catch { /* non-fatal */ }
153
+ const texts = batch.map(e => `${e.concept} ${e.content}`);
154
+ const vecs = await embedBatch(texts);
155
+ for (let j = 0; j < batch.length; j++) {
156
+ this.store.updateEmbedding(batch[j].id, vecs[j], modelId);
157
+ batch[j].embedding = vecs[j];
158
+ }
159
+ } catch { /* batch failed, skip — non-fatal */ }
160
+ }
161
+ if (needsEmbedding.length > 0) {
162
+ console.log(`[consolidation] Backfilled ${needsEmbedding.filter(e => e.embedding?.length).length}/${needsEmbedding.length} embeddings (model: ${modelId})`);
154
163
  }
155
164
  } catch { /* embeddings module unavailable */ }
156
165
  }
@@ -60,40 +60,56 @@ export class RetractionEngine {
60
60
  this.store.retractEngram(target.id, correction.id);
61
61
  }
62
62
 
63
- // Reduce confidence of closely associated engrams (contamination spread)
64
- const associatesAffected = this.propagateConfidenceReduction(target.id, 0.1, 1);
63
+ // Reduce confidence of associated engrams (contamination spread)
64
+ // Depth 2 with 50% decay per hop, capped at 20 total affected nodes
65
+ const associatesAffected = this.propagateConfidenceReduction(target.id, 0.1, 2);
65
66
 
66
67
  return { retractedId: target.id, correctionId, associatesAffected };
67
68
  }
68
69
 
69
70
  /**
70
71
  * Reduce confidence of engrams associated with a retracted engram.
71
- * Shallow propagation (depth 1) to avoid over-penalizing.
72
+ * Propagates up to maxDepth hops with decaying penalty (50% per hop).
73
+ * Capped at MAX_AFFECTED to prevent cascading through the graph.
72
74
  */
75
+ private static readonly MAX_AFFECTED = 20;
76
+
73
77
  private propagateConfidenceReduction(
74
78
  engramId: string,
75
79
  penalty: number,
76
80
  maxDepth: number,
77
- currentDepth: number = 0
81
+ currentDepth: number = 0,
82
+ visited: Set<string> = new Set(),
78
83
  ): number {
79
84
  if (currentDepth >= maxDepth) return 0;
85
+ if (visited.size >= RetractionEngine.MAX_AFFECTED) return 0;
86
+ visited.add(engramId);
80
87
 
81
88
  let affected = 0;
82
89
  const associations = this.store.getAssociationsFor(engramId);
83
90
  for (const assoc of associations) {
84
91
  if (assoc.type === 'invalidation') continue; // Don't penalize corrections
92
+ if (visited.size >= RetractionEngine.MAX_AFFECTED) break;
85
93
 
86
94
  const neighborId = assoc.fromEngramId === engramId
87
95
  ? assoc.toEngramId
88
96
  : assoc.fromEngramId;
97
+ if (visited.has(neighborId)) continue;
98
+
89
99
  const neighbor = this.store.getEngram(neighborId);
90
100
  if (!neighbor || neighbor.retracted) continue;
91
101
 
92
- // Scale penalty by association weight
93
- const scaledPenalty = penalty * assoc.weight;
102
+ // Scale penalty by association weight and decay per hop (50% per depth level)
103
+ const depthDecay = Math.pow(0.5, currentDepth);
104
+ const scaledPenalty = penalty * assoc.weight * depthDecay;
94
105
  const newConfidence = Math.max(0.1, neighbor.confidence - scaledPenalty);
95
106
  this.store.updateConfidence(neighborId, newConfidence);
96
107
  affected++;
108
+
109
+ // Recurse to next depth
110
+ affected += this.propagateConfidenceReduction(
111
+ neighborId, penalty, maxDepth, currentDepth + 1, visited
112
+ );
97
113
  }
98
114
  return affected;
99
115
  }
package/src/index.ts CHANGED
@@ -101,7 +101,8 @@ async function main() {
101
101
  const consolidationScheduler = new ConsolidationScheduler(store, consolidationEngine);
102
102
 
103
103
  // API — disable Fastify's default request logging (too noisy for hive polling)
104
- const app = Fastify({ logger: false });
104
+ // bodyLimit: 512KB to prevent Content-Length mismatch errors with large task payloads
105
+ const app = Fastify({ logger: false, bodyLimit: 512_000 });
105
106
 
106
107
  // Bearer token auth — only enforced when AWM_API_KEY is explicitly set and non-empty
107
108
  if (API_KEY && API_KEY !== 'NONE' && API_KEY.length > 1) {
@@ -122,19 +123,9 @@ async function main() {
122
123
  });
123
124
 
124
125
  // Coordination module (opt-in via AWM_COORDINATION=true)
125
- let heartbeatPruneTimer: ReturnType<typeof setInterval> | null = null;
126
- const { isCoordinationEnabled, initCoordination } = await import('./coordination/index.js');
126
+ const { isCoordinationEnabled, initCoordination, stopCoordinationCleanup } = await import('./coordination/index.js');
127
127
  if (isCoordinationEnabled()) {
128
- initCoordination(app, store.getDb());
129
- // Prune stale heartbeat events every 30s (keeps assignment/command events permanently)
130
- // Purge dead agents older than 24h every 30s to prevent table bloat
131
- const { pruneOldHeartbeats, purgeDeadAgents } = await import('./coordination/stale.js');
132
- heartbeatPruneTimer = setInterval(() => {
133
- const pruned = pruneOldHeartbeats(store.getDb());
134
- if (pruned > 0) console.log(`[coordination] pruned ${pruned} old heartbeat event(s)`);
135
- const purged = purgeDeadAgents(store.getDb());
136
- if (purged > 0) console.log(`[coordination] purged ${purged} dead agent(s) older than 24h`);
137
- }, 30_000);
128
+ initCoordination(app, store.getDb(), store);
138
129
  } else {
139
130
  console.log(' Coordination module disabled (set AWM_COORDINATION=true to enable)');
140
131
  }
@@ -189,9 +180,9 @@ async function main() {
189
180
  console.log(`AgentWorkingMemory v0.6.0 listening on port ${PORT}`);
190
181
 
191
182
  // Graceful shutdown
192
- const shutdown = () => {
183
+ const shutdown = async () => {
193
184
  clearInterval(backupTimer);
194
- if (heartbeatPruneTimer) clearInterval(heartbeatPruneTimer);
185
+ await stopCoordinationCleanup();
195
186
  consolidationScheduler.stop();
196
187
  stagingBuffer.stop();
197
188
  try { store.walCheckpoint(); } catch { /* non-fatal */ }
package/src/mcp.ts CHANGED
@@ -26,7 +26,7 @@
26
26
 
27
27
  import { readFileSync } from 'node:fs';
28
28
  import { resolve } from 'node:path';
29
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
29
+ import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
30
30
 
31
31
  // Load .env file if present (no external dependency)
32
32
  try {
@@ -42,6 +42,12 @@ try {
42
42
  if (!process.env[key]) process.env[key] = val;
43
43
  }
44
44
  } catch { /* No .env file */ }
45
+
46
+ // MCP uses stdout for JSON-RPC. Redirect console.log to stderr so engine
47
+ // startup messages (ConsolidationScheduler, model loading, etc.) don't
48
+ // corrupt the transport. This MUST happen before any engine imports.
49
+ console.log = console.error;
50
+
45
51
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
46
52
  import { z } from 'zod';
47
53
 
@@ -62,6 +68,7 @@ import { DEFAULT_AGENT_CONFIG } from './types/agent.js';
62
68
  import { embed } from './core/embeddings.js';
63
69
  import { startSidecar } from './hooks/sidecar.js';
64
70
  import { initLogger, log, getLogPath } from './core/logger.js';
71
+ import { queryPeerDecisions, formatPeerDecisions } from './coordination/peer-decisions.js';
65
72
 
66
73
  // --- Incognito Mode ---
67
74
  // When AWM_INCOGNITO=1, register zero tools. Claude won't see memory tools at all.
@@ -106,12 +113,64 @@ consolidationScheduler.start();
106
113
  // Coordination DB handle — set when AWM_COORDINATION=true, used by memory_write for decision propagation
107
114
  let coordDb: import('better-sqlite3').Database | null = null;
108
115
 
109
- const server = new McpServer({
110
- name: 'agent-working-memory',
111
- version: '0.6.0',
112
- });
113
-
114
- // --- Auto-classification for memory types ---
116
+ const server = new McpServer({
117
+ name: 'agent-working-memory',
118
+ version: '0.6.0',
119
+ });
120
+
121
+ server.registerResource(
122
+ 'awm-overview',
123
+ 'awm://server/overview',
124
+ {
125
+ title: 'AWM Overview',
126
+ description: 'AgentWorkingMemory MCP server metadata and discovery notes',
127
+ mimeType: 'text/markdown',
128
+ },
129
+ async () => ({
130
+ contents: [{
131
+ uri: 'awm://server/overview',
132
+ text: [
133
+ '# Agent Working Memory',
134
+ '',
135
+ `Agent: ${AGENT_ID}`,
136
+ `DB: ${DB_PATH}`,
137
+ `Coordination: ${process.env.AWM_COORDINATION === 'true' || process.env.AWM_COORDINATION === '1' ? 'enabled' : 'disabled'}`,
138
+ '',
139
+ 'This MCP server primarily exposes tools such as `memory_restore`, `memory_recall`, `memory_write`, and task/checkpoint operations.',
140
+ 'The resources below exist so generic MCP clients can discover the server through `resources/list` and `resources/templates/list`.',
141
+ ].join('\n'),
142
+ mimeType: 'text/markdown',
143
+ }],
144
+ })
145
+ );
146
+
147
+ server.registerResource(
148
+ 'awm-memory-template',
149
+ new ResourceTemplate('awm://memory/{id}', { list: undefined }),
150
+ {
151
+ title: 'AWM Memory By ID',
152
+ description: 'Metadata resource template for a memory identifier',
153
+ mimeType: 'text/markdown',
154
+ },
155
+ async (_uri, variables) => ({
156
+ contents: [{
157
+ uri: `awm://memory/${variables.id ?? ''}`,
158
+ text: [
159
+ '# AWM Memory Reference',
160
+ '',
161
+ `Requested memory id: ${variables.id ?? ''}`,
162
+ '',
163
+ 'Use the AWM memory tools for actual retrieval and mutation:',
164
+ '- `memory_recall` for cognitive retrieval',
165
+ '- `memory_restore` for session state',
166
+ '- `memory_feedback`, `memory_retract`, `memory_supersede` for memory maintenance',
167
+ ].join('\n'),
168
+ mimeType: 'text/markdown',
169
+ }],
170
+ })
171
+ );
172
+
173
+ // --- Auto-classification for memory types ---
115
174
 
116
175
  function classifyMemoryType(content: string): 'episodic' | 'semantic' | 'procedural' | 'unclassified' {
117
176
  const lower = content.toLowerCase();
@@ -349,11 +408,16 @@ Returns the most relevant memories ranked by text relevance, temporal recency, a
349
408
 
350
409
  log(AGENT_ID, 'recall', `"${queryText.slice(0, 80)}" → ${results.length} results`);
351
410
 
411
+ // Peer decisions: append recent decisions by other agents relevant to this query
412
+ const peerSuffix = coordDb
413
+ ? formatPeerDecisions(queryPeerDecisions(coordDb, AGENT_ID, queryText))
414
+ : '';
415
+
352
416
  if (results.length === 0) {
353
417
  return {
354
418
  content: [{
355
419
  type: 'text' as const,
356
- text: 'No relevant memories found.',
420
+ text: 'No relevant memories found.' + peerSuffix,
357
421
  }],
358
422
  };
359
423
  }
@@ -365,7 +429,7 @@ Returns the most relevant memories ranked by text relevance, temporal recency, a
365
429
  return {
366
430
  content: [{
367
431
  type: 'text' as const,
368
- text: lines.join('\n'),
432
+ text: lines.join('\n') + peerSuffix,
369
433
  }],
370
434
  };
371
435
  }
@@ -30,6 +30,7 @@ const DEFAULT_SALIENCE_FEATURES: SalienceFeatures = {
30
30
 
31
31
  export class EngramStore {
32
32
  private db: Database.Database;
33
+ private walTimer: ReturnType<typeof setInterval> | null = null;
33
34
 
34
35
  constructor(dbPath: string = 'memory.db') {
35
36
  this.db = new Database(dbPath);
@@ -37,7 +38,9 @@ export class EngramStore {
37
38
  this.db.pragma('foreign_keys = ON');
38
39
  this.db.pragma('busy_timeout = 5000');
39
40
  this.db.pragma('synchronous = NORMAL');
41
+ this.db.pragma('wal_autocheckpoint = 1000');
40
42
  this.init();
43
+ this.startWalCheckpointTimer();
41
44
  }
42
45
 
43
46
  /** Expose the raw database handle for the coordination module. */
@@ -63,7 +66,27 @@ export class EngramStore {
63
66
 
64
67
  /** Flush WAL to main database file. */
65
68
  walCheckpoint(): void {
66
- this.db.pragma('wal_checkpoint(TRUNCATE)');
69
+ try {
70
+ this.db.pragma('wal_checkpoint(TRUNCATE)');
71
+ } catch {
72
+ // Checkpoint can fail if another connection holds the DB; non-fatal
73
+ }
74
+ }
75
+
76
+ /** Start periodic WAL checkpoint every 5 minutes to prevent unbounded WAL growth. */
77
+ private startWalCheckpointTimer(): void {
78
+ this.walTimer = setInterval(() => {
79
+ this.walCheckpoint();
80
+ }, 5 * 60 * 1000);
81
+ this.walTimer.unref();
82
+ }
83
+
84
+ /** Stop the WAL checkpoint timer (call before close). */
85
+ stopWalCheckpointTimer(): void {
86
+ if (this.walTimer) {
87
+ clearInterval(this.walTimer);
88
+ this.walTimer = null;
89
+ }
67
90
  }
68
91
 
69
92
  private init(): void {
@@ -212,6 +235,13 @@ export class EngramStore {
212
235
  `);
213
236
  }
214
237
 
238
+ // Migration: add embedding_model for version tracking (prevents drift on model change)
239
+ try {
240
+ this.db.prepare('SELECT embedding_model FROM engrams LIMIT 0').get();
241
+ } catch {
242
+ this.db.exec(`ALTER TABLE engrams ADD COLUMN embedding_model TEXT`);
243
+ }
244
+
215
245
  // Migration: add conscious_state table for checkpointing
216
246
  this.db.exec(`
217
247
  CREATE TABLE IF NOT EXISTS conscious_state (
@@ -327,9 +357,13 @@ export class EngramStore {
327
357
  );
328
358
  }
329
359
 
330
- updateEmbedding(id: string, embedding: number[]): void {
360
+ updateEmbedding(id: string, embedding: number[], modelId?: string): void {
331
361
  const blob = Buffer.from(new Float32Array(embedding).buffer);
332
- this.db.prepare('UPDATE engrams SET embedding = ? WHERE id = ?').run(blob, id);
362
+ if (modelId) {
363
+ this.db.prepare('UPDATE engrams SET embedding = ?, embedding_model = ? WHERE id = ?').run(blob, modelId, id);
364
+ } else {
365
+ this.db.prepare('UPDATE engrams SET embedding = ? WHERE id = ?').run(blob, id);
366
+ }
333
367
  }
334
368
 
335
369
  retractEngram(id: string, retractedBy: string | null): void {
@@ -1012,6 +1046,8 @@ export class EngramStore {
1012
1046
  }
1013
1047
 
1014
1048
  close(): void {
1049
+ this.stopWalCheckpointTimer();
1050
+ this.walCheckpoint();
1015
1051
  this.db.close();
1016
1052
  }
1017
1053
  }