agent-working-memory 0.7.9 → 0.7.10

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.
package/src/index.ts CHANGED
@@ -177,7 +177,7 @@ async function main() {
177
177
 
178
178
  // Start server
179
179
  await app.listen({ port: PORT, host: '0.0.0.0' });
180
- console.log(`AgentWorkingMemory v0.7.9 listening on port ${PORT}`);
180
+ console.log(`AgentWorkingMemory v0.7.10 listening on port ${PORT}`);
181
181
 
182
182
  // Graceful shutdown
183
183
  const shutdown = async () => {
package/src/mcp.ts CHANGED
@@ -78,7 +78,7 @@ const INCOGNITO = process.env.AWM_INCOGNITO === '1' || process.env.AWM_INCOGNITO
78
78
 
79
79
  if (INCOGNITO) {
80
80
  console.error('AWM: incognito mode — all memory tools disabled, nothing will be recorded');
81
- const server = new McpServer({ name: 'agent-working-memory', version: '0.7.9' });
81
+ const server = new McpServer({ name: 'agent-working-memory', version: '0.7.10' });
82
82
  const transport = new StdioServerTransport();
83
83
  server.connect(transport).catch(err => {
84
84
  console.error('MCP server failed:', err);
@@ -115,7 +115,7 @@ let coordDb: import('better-sqlite3').Database | null = null;
115
115
 
116
116
  const server = new McpServer({
117
117
  name: 'agent-working-memory',
118
- version: '0.7.9',
118
+ version: '0.7.10',
119
119
  });
120
120
 
121
121
  server.registerResource(
@@ -28,10 +28,33 @@ const DEFAULT_SALIENCE_FEATURES: SalienceFeatures = {
28
28
  surprise: 0, decisionMade: false, causalDepth: 0, resolutionEffort: 0, eventType: 'observation',
29
29
  };
30
30
 
31
+ /**
32
+ * In-memory slim entry — the minimum data the activation pipeline's pre-filter
33
+ * pass reads. Lives in EngramStore.slimCache to skip the SQL fetch + Buffer→
34
+ * Float32Array conversion on every recall. ~22 bytes overhead per entry plus
35
+ * the embedding (~1.5KB), so ~15MB at 10K engrams.
36
+ */
37
+ type SlimCacheEntry = {
38
+ id: string;
39
+ agentId: string;
40
+ concept: string;
41
+ embedding: number[] | null;
42
+ stage: EngramStage;
43
+ retracted: boolean;
44
+ };
45
+
31
46
  export class EngramStore {
32
47
  private db: Database.Database;
33
48
  private walTimer: ReturnType<typeof setInterval> | null = null;
34
49
 
50
+ // Slim cache for the activation pipeline pre-filter. Populated lazily on
51
+ // first call to getEngramsByAgentSlim(). Mutations to engrams keep this in
52
+ // sync via private cache* helpers. Disable via AWM_DISABLE_SLIM_CACHE=1
53
+ // (for A/B testing or if a regression appears).
54
+ private slimCache: Map<string, SlimCacheEntry> = new Map();
55
+ private slimCachePopulated: boolean = false;
56
+ private slimCacheEnabled: boolean = process.env.AWM_DISABLE_SLIM_CACHE !== '1';
57
+
35
58
  constructor(dbPath: string = 'memory.db') {
36
59
  this.db = new Database(dbPath);
37
60
  this.db.pragma('journal_mode = WAL');
@@ -43,6 +66,62 @@ export class EngramStore {
43
66
  this.startWalCheckpointTimer();
44
67
  }
45
68
 
69
+ // --- Slim cache management ---
70
+
71
+ /** Lazy-populate the slim cache from the engrams table. Called on first slim fetch. */
72
+ private ensureSlimCachePopulated(): void {
73
+ if (this.slimCachePopulated || !this.slimCacheEnabled) return;
74
+ const rows = this.db.prepare(
75
+ 'SELECT id, agent_id, concept, embedding, stage, retracted FROM engrams'
76
+ ).all() as any[];
77
+ for (const r of rows) {
78
+ this.slimCache.set(r.id as string, {
79
+ id: r.id as string,
80
+ agentId: r.agent_id as string,
81
+ concept: r.concept as string,
82
+ embedding: r.embedding ? Array.from(bufferToFloat32Array(r.embedding)) : null,
83
+ stage: r.stage as EngramStage,
84
+ retracted: !!r.retracted,
85
+ });
86
+ }
87
+ this.slimCachePopulated = true;
88
+ }
89
+
90
+ /** Add a new engram to the slim cache. Called from createEngram. */
91
+ private cacheAdd(entry: SlimCacheEntry): void {
92
+ if (!this.slimCacheEnabled) return;
93
+ this.slimCache.set(entry.id, entry);
94
+ }
95
+
96
+ private cacheUpdateStage(id: string, stage: EngramStage): void {
97
+ if (!this.slimCacheEnabled) return;
98
+ const e = this.slimCache.get(id);
99
+ if (e) e.stage = stage;
100
+ }
101
+
102
+ private cacheUpdateEmbedding(id: string, embedding: number[]): void {
103
+ if (!this.slimCacheEnabled) return;
104
+ const e = this.slimCache.get(id);
105
+ if (e) e.embedding = embedding;
106
+ }
107
+
108
+ private cacheRetract(id: string): void {
109
+ if (!this.slimCacheEnabled) return;
110
+ const e = this.slimCache.get(id);
111
+ if (e) e.retracted = true;
112
+ }
113
+
114
+ private cacheRemove(id: string): void {
115
+ if (!this.slimCacheEnabled) return;
116
+ this.slimCache.delete(id);
117
+ }
118
+
119
+ /** Reset cache (used by tests + after timeWarp/bulk operations). */
120
+ resetSlimCache(): void {
121
+ this.slimCache.clear();
122
+ this.slimCachePopulated = false;
123
+ }
124
+
46
125
  /** Expose the raw database handle for the coordination module. */
47
126
  getDb(): Database.Database {
48
127
  return this.db;
@@ -306,6 +385,18 @@ export class EngramStore {
306
385
  input.memoryType ?? 'unclassified',
307
386
  );
308
387
 
388
+ // Add to slim cache (skip if not yet populated — first slim fetch will load it)
389
+ if (this.slimCachePopulated) {
390
+ this.cacheAdd({
391
+ id,
392
+ agentId: input.agentId,
393
+ concept: input.concept,
394
+ embedding: input.embedding ?? null,
395
+ stage: 'active',
396
+ retracted: false,
397
+ });
398
+ }
399
+
309
400
  return this.getEngram(id)!;
310
401
  }
311
402
 
@@ -344,6 +435,18 @@ export class EngramStore {
344
435
  stage?: EngramStage,
345
436
  includeRetracted: boolean = false
346
437
  ): Array<{ id: string; concept: string; embedding: number[] | null }> {
438
+ if (this.slimCacheEnabled) {
439
+ this.ensureSlimCachePopulated();
440
+ const result: Array<{ id: string; concept: string; embedding: number[] | null }> = [];
441
+ for (const entry of this.slimCache.values()) {
442
+ if (entry.agentId !== agentId) continue;
443
+ if (stage && entry.stage !== stage) continue;
444
+ if (!includeRetracted && entry.retracted) continue;
445
+ result.push({ id: entry.id, concept: entry.concept, embedding: entry.embedding });
446
+ }
447
+ return result;
448
+ }
449
+ // Cache disabled — fall back to direct SQL
347
450
  let query = 'SELECT id, concept, embedding FROM engrams WHERE agent_id = ?';
348
451
  const params: any[] = [agentId];
349
452
 
@@ -371,6 +474,19 @@ export class EngramStore {
371
474
  if (agentIds.length === 0) return [];
372
475
  if (agentIds.length === 1) return this.getEngramsByAgentSlim(agentIds[0], stage, includeRetracted);
373
476
 
477
+ if (this.slimCacheEnabled) {
478
+ this.ensureSlimCachePopulated();
479
+ const agentSet = new Set(agentIds);
480
+ const result: Array<{ id: string; concept: string; embedding: number[] | null }> = [];
481
+ for (const entry of this.slimCache.values()) {
482
+ if (!agentSet.has(entry.agentId)) continue;
483
+ if (stage && entry.stage !== stage) continue;
484
+ if (!includeRetracted && entry.retracted) continue;
485
+ result.push({ id: entry.id, concept: entry.concept, embedding: entry.embedding });
486
+ }
487
+ return result;
488
+ }
489
+
374
490
  const placeholders = agentIds.map(() => '?').join(',');
375
491
  let query = `SELECT id, concept, embedding FROM engrams WHERE agent_id IN (${placeholders})`;
376
492
  const params: any[] = [...agentIds];
@@ -513,6 +629,7 @@ export class EngramStore {
513
629
 
514
630
  updateStage(id: string, stage: EngramStage): void {
515
631
  this.db.prepare('UPDATE engrams SET stage = ? WHERE id = ?').run(stage, id);
632
+ this.cacheUpdateStage(id, stage);
516
633
  }
517
634
 
518
635
  updateConfidence(id: string, confidence: number): void {
@@ -528,16 +645,19 @@ export class EngramStore {
528
645
  } else {
529
646
  this.db.prepare('UPDATE engrams SET embedding = ? WHERE id = ?').run(blob, id);
530
647
  }
648
+ this.cacheUpdateEmbedding(id, embedding);
531
649
  }
532
650
 
533
651
  retractEngram(id: string, retractedBy: string | null): void {
534
652
  this.db.prepare(`
535
653
  UPDATE engrams SET retracted = 1, retracted_by = ?, retracted_at = ? WHERE id = ?
536
654
  `).run(retractedBy, new Date().toISOString(), id);
655
+ this.cacheRetract(id);
537
656
  }
538
657
 
539
658
  deleteEngram(id: string): void {
540
659
  this.db.prepare('DELETE FROM engrams WHERE id = ?').run(id);
660
+ this.cacheRemove(id);
541
661
  }
542
662
 
543
663
  /**