agent-working-memory 0.7.17 → 0.8.5

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 (186) hide show
  1. package/README.md +156 -15
  2. package/dist/adapters/common.js +202 -202
  3. package/dist/adapters/http.js +22 -22
  4. package/dist/api/routes.d.ts +1 -1
  5. package/dist/api/routes.d.ts.map +1 -1
  6. package/dist/api/routes.js +258 -79
  7. package/dist/api/routes.js.map +1 -1
  8. package/dist/cli/migrate.d.ts +42 -0
  9. package/dist/cli/migrate.d.ts.map +1 -0
  10. package/dist/cli/migrate.js +268 -0
  11. package/dist/cli/migrate.js.map +1 -0
  12. package/dist/cli.js +147 -103
  13. package/dist/cli.js.map +1 -1
  14. package/dist/coordination/circuit-breaker.d.ts +24 -0
  15. package/dist/coordination/circuit-breaker.d.ts.map +1 -0
  16. package/dist/coordination/circuit-breaker.js +70 -0
  17. package/dist/coordination/circuit-breaker.js.map +1 -0
  18. package/dist/coordination/failure-modes.d.ts +21 -0
  19. package/dist/coordination/failure-modes.d.ts.map +1 -0
  20. package/dist/coordination/failure-modes.js +49 -0
  21. package/dist/coordination/failure-modes.js.map +1 -0
  22. package/dist/coordination/peer-decisions.js +7 -7
  23. package/dist/coordination/routes.d.ts.map +1 -1
  24. package/dist/coordination/routes.js +44 -1
  25. package/dist/coordination/routes.js.map +1 -1
  26. package/dist/coordination/schema.d.ts.map +1 -1
  27. package/dist/coordination/schema.js +18 -0
  28. package/dist/coordination/schema.js.map +1 -1
  29. package/dist/coordination/schemas.d.ts +9 -9
  30. package/dist/coordination/stale.d.ts +9 -1
  31. package/dist/coordination/stale.d.ts.map +1 -1
  32. package/dist/coordination/stale.js +53 -9
  33. package/dist/coordination/stale.js.map +1 -1
  34. package/dist/core/embeddings.d.ts +16 -10
  35. package/dist/core/embeddings.d.ts.map +1 -1
  36. package/dist/core/embeddings.js +54 -38
  37. package/dist/core/embeddings.js.map +1 -1
  38. package/dist/core/ml-worker-entry.d.ts +17 -0
  39. package/dist/core/ml-worker-entry.d.ts.map +1 -0
  40. package/dist/core/ml-worker-entry.js +193 -0
  41. package/dist/core/ml-worker-entry.js.map +1 -0
  42. package/dist/core/ml-worker.d.ts +59 -0
  43. package/dist/core/ml-worker.d.ts.map +1 -0
  44. package/dist/core/ml-worker.js +253 -0
  45. package/dist/core/ml-worker.js.map +1 -0
  46. package/dist/core/query-expander.d.ts +13 -15
  47. package/dist/core/query-expander.d.ts.map +1 -1
  48. package/dist/core/query-expander.js +48 -54
  49. package/dist/core/query-expander.js.map +1 -1
  50. package/dist/core/reranker.d.ts +9 -19
  51. package/dist/core/reranker.d.ts.map +1 -1
  52. package/dist/core/reranker.js +33 -35
  53. package/dist/core/reranker.js.map +1 -1
  54. package/dist/core/salience.d.ts +32 -5
  55. package/dist/core/salience.d.ts.map +1 -1
  56. package/dist/core/salience.js +201 -28
  57. package/dist/core/salience.js.map +1 -1
  58. package/dist/core/write-pipeline.d.ts +10 -2
  59. package/dist/core/write-pipeline.d.ts.map +1 -1
  60. package/dist/core/write-pipeline.js +218 -49
  61. package/dist/core/write-pipeline.js.map +1 -1
  62. package/dist/engine/activation.d.ts +2 -2
  63. package/dist/engine/activation.d.ts.map +1 -1
  64. package/dist/engine/activation.js +238 -115
  65. package/dist/engine/activation.js.map +1 -1
  66. package/dist/engine/confidence.d.ts +62 -0
  67. package/dist/engine/confidence.d.ts.map +1 -0
  68. package/dist/engine/confidence.js +100 -0
  69. package/dist/engine/confidence.js.map +1 -0
  70. package/dist/engine/connections.d.ts +47 -8
  71. package/dist/engine/connections.d.ts.map +1 -1
  72. package/dist/engine/connections.js +83 -22
  73. package/dist/engine/connections.js.map +1 -1
  74. package/dist/engine/consolidation-scheduler.d.ts +46 -10
  75. package/dist/engine/consolidation-scheduler.d.ts.map +1 -1
  76. package/dist/engine/consolidation-scheduler.js +170 -50
  77. package/dist/engine/consolidation-scheduler.js.map +1 -1
  78. package/dist/engine/consolidation.d.ts +16 -2
  79. package/dist/engine/consolidation.d.ts.map +1 -1
  80. package/dist/engine/consolidation.js +143 -44
  81. package/dist/engine/consolidation.js.map +1 -1
  82. package/dist/engine/eval.d.ts +2 -2
  83. package/dist/engine/eval.d.ts.map +1 -1
  84. package/dist/engine/eval.js +9 -9
  85. package/dist/engine/eval.js.map +1 -1
  86. package/dist/engine/eviction.d.ts +4 -4
  87. package/dist/engine/eviction.d.ts.map +1 -1
  88. package/dist/engine/eviction.js +15 -15
  89. package/dist/engine/eviction.js.map +1 -1
  90. package/dist/engine/retraction.d.ts +93 -5
  91. package/dist/engine/retraction.d.ts.map +1 -1
  92. package/dist/engine/retraction.js +238 -19
  93. package/dist/engine/retraction.js.map +1 -1
  94. package/dist/engine/staging.d.ts +1 -1
  95. package/dist/engine/staging.d.ts.map +1 -1
  96. package/dist/engine/staging.js +3 -3
  97. package/dist/engine/staging.js.map +1 -1
  98. package/dist/hooks/sidecar.d.ts +1 -1
  99. package/dist/hooks/sidecar.d.ts.map +1 -1
  100. package/dist/hooks/sidecar.js +4 -4
  101. package/dist/hooks/sidecar.js.map +1 -1
  102. package/dist/index.js +48 -31
  103. package/dist/index.js.map +1 -1
  104. package/dist/mcp.js +167 -141
  105. package/dist/mcp.js.map +1 -1
  106. package/dist/storage/factory.d.ts +55 -0
  107. package/dist/storage/factory.d.ts.map +1 -0
  108. package/dist/storage/factory.js +135 -0
  109. package/dist/storage/factory.js.map +1 -0
  110. package/dist/storage/pglite-schema.d.ts +20 -0
  111. package/dist/storage/pglite-schema.d.ts.map +1 -0
  112. package/dist/storage/pglite-schema.js +165 -0
  113. package/dist/storage/pglite-schema.js.map +1 -0
  114. package/dist/storage/pglite.d.ts +187 -0
  115. package/dist/storage/pglite.d.ts.map +1 -0
  116. package/dist/storage/pglite.js +1114 -0
  117. package/dist/storage/pglite.js.map +1 -0
  118. package/dist/storage/sqlite.d.ts +117 -0
  119. package/dist/storage/sqlite.d.ts.map +1 -1
  120. package/dist/storage/sqlite.js +357 -8
  121. package/dist/storage/sqlite.js.map +1 -1
  122. package/dist/storage/store.d.ts +63 -0
  123. package/dist/storage/store.d.ts.map +1 -0
  124. package/dist/storage/store.js +27 -0
  125. package/dist/storage/store.js.map +1 -0
  126. package/dist/types/engram.d.ts +92 -2
  127. package/dist/types/engram.d.ts.map +1 -1
  128. package/dist/types/engram.js.map +1 -1
  129. package/package.json +3 -1
  130. package/src/adapters/claude-code.ts +218 -218
  131. package/src/adapters/codex.ts +252 -252
  132. package/src/adapters/common.ts +411 -411
  133. package/src/adapters/cursor.ts +135 -135
  134. package/src/adapters/http.ts +93 -93
  135. package/src/adapters/index.ts +31 -31
  136. package/src/adapters/types.ts +75 -75
  137. package/src/api/index.ts +3 -3
  138. package/src/api/routes.ts +970 -714
  139. package/src/cli/migrate.ts +307 -0
  140. package/src/cli.ts +755 -719
  141. package/src/coordination/circuit-breaker.ts +83 -0
  142. package/src/coordination/events.ts +90 -90
  143. package/src/coordination/failure-modes.ts +50 -0
  144. package/src/coordination/peer-decisions.ts +105 -105
  145. package/src/coordination/plugin-loader.ts +60 -60
  146. package/src/coordination/plugin.ts +44 -44
  147. package/src/coordination/routes.ts +56 -1
  148. package/src/coordination/schema.ts +13 -0
  149. package/src/coordination/stale.ts +80 -11
  150. package/src/coordination/types.ts +311 -311
  151. package/src/coordination/write-mutex.ts +69 -69
  152. package/src/core/auto-tagger.ts +168 -168
  153. package/src/core/decay.ts +63 -63
  154. package/src/core/embeddings.ts +110 -93
  155. package/src/core/index.ts +5 -5
  156. package/src/core/logger.ts +36 -36
  157. package/src/core/ml-worker-entry.ts +194 -0
  158. package/src/core/ml-worker.ts +281 -0
  159. package/src/core/query-expander.ts +122 -128
  160. package/src/core/reranker.ts +119 -125
  161. package/src/core/salience.ts +200 -33
  162. package/src/core/write-pipeline.ts +521 -343
  163. package/src/engine/activation.ts +235 -109
  164. package/src/engine/confidence.ts +120 -0
  165. package/src/engine/connections.ts +162 -103
  166. package/src/engine/consolidation-scheduler.ts +242 -125
  167. package/src/engine/consolidation.ts +138 -45
  168. package/src/engine/eval.ts +102 -102
  169. package/src/engine/eviction.ts +101 -101
  170. package/src/engine/index.ts +8 -8
  171. package/src/engine/retraction.ts +366 -116
  172. package/src/engine/staging.ts +74 -74
  173. package/src/hooks/sidecar.ts +5 -5
  174. package/src/index.ts +226 -212
  175. package/src/mcp.ts +1145 -1121
  176. package/src/storage/factory.ts +147 -0
  177. package/src/storage/index.ts +3 -3
  178. package/src/storage/pglite-schema.ts +166 -0
  179. package/src/storage/pglite.ts +1363 -0
  180. package/src/storage/sqlite.ts +402 -7
  181. package/src/storage/store.ts +80 -0
  182. package/src/types/agent.ts +67 -67
  183. package/src/types/checkpoint.ts +46 -46
  184. package/src/types/engram.ts +101 -3
  185. package/src/types/eval.ts +100 -100
  186. package/src/types/index.ts +6 -6
package/dist/mcp.js CHANGED
@@ -50,7 +50,7 @@ catch { /* No .env file */ }
50
50
  console.log = console.error;
51
51
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
52
52
  import { z } from 'zod';
53
- import { EngramStore } from './storage/sqlite.js';
53
+ import { openStore, getConfiguredBackend } from './storage/factory.js';
54
54
  import { ActivationEngine } from './engine/activation.js';
55
55
  import { ConnectionEngine } from './engine/connections.js';
56
56
  import { StagingBuffer } from './engine/staging.js';
@@ -72,7 +72,7 @@ import { queryPeerDecisions, formatPeerDecisions } from './coordination/peer-dec
72
72
  const INCOGNITO = process.env.AWM_INCOGNITO === '1' || process.env.AWM_INCOGNITO === 'true';
73
73
  if (INCOGNITO) {
74
74
  console.error('AWM: incognito mode — all memory tools disabled, nothing will be recorded');
75
- const server = new McpServer({ name: 'agent-working-memory', version: '0.7.17' });
75
+ const server = new McpServer({ name: 'agent-working-memory', version: '0.8.5' });
76
76
  const transport = new StdioServerTransport();
77
77
  server.connect(transport).catch(err => {
78
78
  console.error('MCP server failed:', err);
@@ -82,20 +82,25 @@ if (INCOGNITO) {
82
82
  }
83
83
  else {
84
84
  // --- Setup ---
85
- const DB_PATH = process.env.AWM_DB_PATH ?? 'memory.db';
85
+ const BACKEND = getConfiguredBackend();
86
+ const DB_PATH = process.env.AWM_DB_PATH ?? (BACKEND === 'pglite' ? 'memory-pglite' : 'memory.db');
86
87
  const AGENT_ID = process.env.AWM_AGENT_ID ?? process.env.WORKER_NAME ?? 'claude-code';
87
88
  const HOOK_PORT = parseInt(process.env.AWM_HOOK_PORT ?? '8401', 10);
88
89
  const HOOK_SECRET = process.env.AWM_HOOK_SECRET ?? null;
89
90
  initLogger(DB_PATH);
90
- log(AGENT_ID, 'startup', `MCP server starting (db: ${DB_PATH}, hooks: ${HOOK_PORT})`);
91
- const store = new EngramStore(DB_PATH);
91
+ log(AGENT_ID, 'startup', `MCP server starting (backend: ${BACKEND}, db: ${DB_PATH}, hooks: ${HOOK_PORT})`);
92
+ // AWM 0.8.x: openStore() returns either SQLite (sync) or PGlite (async) store.
93
+ // Engines accept either via IEngramStore (MaybePromise<T> contract).
94
+ const { store: storeAny } = await openStore();
95
+ // Engines accept the async contract; SQLite-only call sites must guard on BACKEND.
96
+ const store = storeAny;
92
97
  const activationEngine = new ActivationEngine(store);
93
98
  const connectionEngine = new ConnectionEngine(store, activationEngine);
94
99
  const stagingBuffer = new StagingBuffer(store, activationEngine);
95
100
  const evictionEngine = new EvictionEngine(store);
96
101
  const retractionEngine = new RetractionEngine(store);
97
102
  const evalEngine = new EvalEngine(store);
98
- const consolidationEngine = new ConsolidationEngine(store);
103
+ const consolidationEngine = new ConsolidationEngine(store, connectionEngine);
99
104
  const consolidationScheduler = new ConsolidationScheduler(store, consolidationEngine);
100
105
  stagingBuffer.start(DEFAULT_AGENT_CONFIG.stagingTtlMs);
101
106
  consolidationScheduler.start();
@@ -103,7 +108,7 @@ else {
103
108
  let coordDb = null;
104
109
  const server = new McpServer({
105
110
  name: 'agent-working-memory',
106
- version: '0.7.17',
111
+ version: '0.8.5',
107
112
  });
108
113
  server.registerResource('awm-overview', 'awm://server/overview', {
109
114
  title: 'AWM Overview',
@@ -163,15 +168,15 @@ else {
163
168
  return 'unclassified';
164
169
  }
165
170
  // --- Tools ---
166
- server.tool('memory_write', `Store a memory. The salience filter decides whether it's worth keeping (active), needs more evidence (staging), or should be discarded.
167
-
168
- CALL THIS PROACTIVELY — do not wait to be asked. Write memories when you:
169
- - Discover something about the codebase, bugs, or architecture
170
- - Make a decision and want to remember why
171
- - Encounter and resolve an error
172
- - Learn a user preference or project pattern
173
- - Complete a significant piece of work
174
-
171
+ server.tool('memory_write', `Store a memory. The salience filter decides whether it's worth keeping (active), needs more evidence (staging), or should be discarded.
172
+
173
+ CALL THIS PROACTIVELY — do not wait to be asked. Write memories when you:
174
+ - Discover something about the codebase, bugs, or architecture
175
+ - Make a decision and want to remember why
176
+ - Encounter and resolve an error
177
+ - Learn a user preference or project pattern
178
+ - Complete a significant piece of work
179
+
175
180
  The concept should be a short label (3-8 words). The content should be the full detail.`, {
176
181
  concept: z.string().describe('Short label for this memory (3-8 words)'),
177
182
  content: z.string().describe('Full detail of what was learned'),
@@ -223,7 +228,7 @@ The concept should be a short label (3-8 words). The content should be the full
223
228
  if (params.intent)
224
229
  metaTags.push(`intent=${params.intent}`);
225
230
  const memoryType = params.memory_type ?? classifyMemoryType(params.content);
226
- const result = performWrite({ store, connectionEngine }, {
231
+ const result = await performWrite({ store, connectionEngine }, {
227
232
  agentId: AGENT_ID,
228
233
  concept: params.concept,
229
234
  content: params.content,
@@ -239,7 +244,7 @@ The concept should be a short label (3-8 words). The content should be the full
239
244
  });
240
245
  // Auto-checkpoint — covers create/reinforce/supersede uniformly
241
246
  try {
242
- store.updateAutoCheckpointWrite(AGENT_ID, result.engram.id);
247
+ await store.updateAutoCheckpointWrite(AGENT_ID, result.engram.id);
243
248
  }
244
249
  catch { /* non-fatal */ }
245
250
  if (result.action === 'reinforce') {
@@ -275,15 +280,15 @@ The concept should be a short label (3-8 words). The content should be the full
275
280
  }],
276
281
  };
277
282
  });
278
- server.tool('memory_recall', `Recall memories relevant to a query. Uses cognitive activation — not keyword search.
279
-
280
- ALWAYS call this when:
281
- - Starting work on a project or topic (recall what you know)
282
- - Debugging (recall similar errors and solutions)
283
- - Making decisions (recall past decisions and outcomes)
284
- - The user mentions a topic you might have stored memories about
285
-
286
- Accepts either "query" or "context" parameter — both work identically.
283
+ server.tool('memory_recall', `Recall memories relevant to a query. Uses cognitive activation — not keyword search.
284
+
285
+ ALWAYS call this when:
286
+ - Starting work on a project or topic (recall what you know)
287
+ - Debugging (recall similar errors and solutions)
288
+ - Making decisions (recall past decisions and outcomes)
289
+ - The user mentions a topic you might have stored memories about
290
+
291
+ Accepts either "query" or "context" parameter — both work identically.
287
292
  Returns the most relevant memories ranked by text relevance, temporal recency, and associative strength.`, {
288
293
  query: z.string().optional().describe('What to search for — describe the situation, question, or topic'),
289
294
  context: z.string().optional().describe('Alias for query (either works)'),
@@ -294,6 +299,8 @@ Returns the most relevant memories ranked by text relevance, temporal recency, a
294
299
  use_expansion: z.boolean().optional().default(true).describe('Expand query with synonyms for better recall (default true)'),
295
300
  memory_type: z.enum(['episodic', 'semantic', 'procedural']).optional().describe('Filter by memory type (omit to search all types)'),
296
301
  workspace: z.string().optional().describe('Search across all agents in this workspace (hive mode). Omit for agent-scoped recall only.'),
302
+ require_confidence: z.number().optional().describe('Opt-in: abstain (return []) when recall confidence is below this threshold. Typical values: 0.10 (strict), 0.25 (balanced), 0.40 (aggressive). Confidence is the shape of the result-score distribution; low confidence indicates a noisy or best-of-bad-bunch recall.'),
303
+ granularity: z.enum(['full', 'compact', 'auto']).optional().describe('Output granularity (Paper 3: cognitive teaming). "full" (default): no change. "compact": every result carries a short summary field. "auto": confidence-adaptive — top result gets a longer summary when there is a clear winner, otherwise everything is compact for scanning.'),
297
304
  }, async (params) => {
298
305
  const queryText = params.query ?? params.context;
299
306
  if (!queryText) {
@@ -316,11 +323,13 @@ Returns the most relevant memories ranked by text relevance, temporal recency, a
316
323
  useExpansion: params.use_expansion,
317
324
  memoryType: params.memory_type,
318
325
  workspace,
326
+ requireConfidence: params.require_confidence,
327
+ granularity: params.granularity,
319
328
  });
320
329
  // Auto-checkpoint: track recall
321
330
  try {
322
331
  const ids = results.map(r => r.engram.id);
323
- store.updateAutoCheckpointRecall(AGENT_ID, queryText, ids);
332
+ await store.updateAutoCheckpointRecall(AGENT_ID, queryText, ids);
324
333
  }
325
334
  catch { /* non-fatal */ }
326
335
  log(AGENT_ID, 'recall', `"${queryText.slice(0, 80)}" → ${results.length} results`);
@@ -337,7 +346,11 @@ Returns the most relevant memories ranked by text relevance, temporal recency, a
337
346
  };
338
347
  }
339
348
  const lines = results.map((r, i) => {
340
- return `${i + 1}. **${r.engram.concept}** (${r.score.toFixed(3)}): ${r.engram.content}`;
349
+ // Confidence-adaptive output (Paper 3: cognitive teaming). When the caller
350
+ // requested 'compact' or 'auto' granularity, surface the engine-computed
351
+ // summary instead of the full content — same engram, less to read.
352
+ const body = r.summary ?? r.engram.content;
353
+ return `${i + 1}. **${r.engram.concept}** (${r.score.toFixed(3)}): ${body}`;
341
354
  });
342
355
  return {
343
356
  content: [{
@@ -346,23 +359,23 @@ Returns the most relevant memories ranked by text relevance, temporal recency, a
346
359
  }],
347
360
  };
348
361
  });
349
- server.tool('memory_feedback', `Report whether a recalled memory was actually useful. This updates the memory's confidence score — useful memories become stronger, useless ones weaken.
350
-
362
+ server.tool('memory_feedback', `Report whether a recalled memory was actually useful. This updates the memory's confidence score — useful memories become stronger, useless ones weaken.
363
+
351
364
  Always call this after using a recalled memory so the system learns what's valuable.`, {
352
365
  engram_id: z.string().describe('ID of the memory (from memory_recall results)'),
353
366
  useful: z.boolean().describe('Was this memory actually helpful?'),
354
367
  context: z.string().optional().describe('Brief note on why it was/wasn\'t useful'),
355
368
  }, async (params) => {
356
- store.logRetrievalFeedback(null, params.engram_id, params.useful, params.context ?? '');
357
- const engram = store.getEngram(params.engram_id);
369
+ await store.logRetrievalFeedback(null, params.engram_id, params.useful, params.context ?? '');
370
+ const engram = await store.getEngram(params.engram_id);
358
371
  if (engram) {
359
372
  const delta = params.useful
360
373
  ? DEFAULT_AGENT_CONFIG.feedbackPositiveBoost
361
374
  : -DEFAULT_AGENT_CONFIG.feedbackNegativePenalty;
362
- store.updateConfidence(engram.id, engram.confidence + delta);
375
+ await store.updateConfidence(engram.id, engram.confidence + delta);
363
376
  }
364
377
  // Validation-gated Hebbian: resolve pending co-activation pairs for this engram
365
- const hebbianUpdated = activationEngine.resolveHebbianFeedback(params.engram_id, params.useful);
378
+ const hebbianUpdated = await activationEngine.resolveHebbianFeedback(params.engram_id, params.useful);
366
379
  return {
367
380
  content: [{
368
381
  type: 'text',
@@ -370,14 +383,14 @@ Always call this after using a recalled memory so the system learns what's valua
370
383
  }],
371
384
  };
372
385
  });
373
- server.tool('memory_retract', `Retract a memory that turned out to be wrong. Creates a correction and reduces confidence of related memories.
374
-
386
+ server.tool('memory_retract', `Retract a memory that turned out to be wrong. Creates a correction and reduces confidence of related memories.
387
+
375
388
  Use this when you discover a memory contains incorrect information.`, {
376
389
  engram_id: z.string().describe('ID of the wrong memory'),
377
390
  reason: z.string().describe('Why is this memory wrong?'),
378
391
  correction: z.string().optional().describe('What is the correct information? (creates a new memory)'),
379
392
  }, async (params) => {
380
- const result = retractionEngine.retract({
393
+ const result = await retractionEngine.retract({
381
394
  agentId: AGENT_ID,
382
395
  targetEngramId: params.engram_id,
383
396
  reason: params.reason,
@@ -395,31 +408,31 @@ Use this when you discover a memory contains incorrect information.`, {
395
408
  }],
396
409
  };
397
410
  });
398
- server.tool('memory_supersede', `Replace an outdated memory with a newer one. Unlike retraction (which marks memories as wrong), supersession marks the old memory as outdated but historically correct.
399
-
400
- Use this when:
401
- - A status or count has changed (e.g., "5 reviews done" → "7 reviews done")
402
- - Architecture or infrastructure evolved (e.g., "two-repo model" → "three-repo model")
403
- - A schedule or plan was updated
404
-
411
+ server.tool('memory_supersede', `Replace an outdated memory with a newer one. Unlike retraction (which marks memories as wrong), supersession marks the old memory as outdated but historically correct.
412
+
413
+ Use this when:
414
+ - A status or count has changed (e.g., "5 reviews done" → "7 reviews done")
415
+ - Architecture or infrastructure evolved (e.g., "two-repo model" → "three-repo model")
416
+ - A schedule or plan was updated
417
+
405
418
  The old memory stays in the database (searchable for history) but is heavily down-ranked in recall so the current version dominates.`, {
406
419
  old_engram_id: z.string().describe('ID of the outdated memory'),
407
420
  new_engram_id: z.string().describe('ID of the replacement memory'),
408
421
  reason: z.string().optional().describe('Why the old memory is outdated'),
409
422
  }, async (params) => {
410
- const oldEngram = store.getEngram(params.old_engram_id);
423
+ const oldEngram = await store.getEngram(params.old_engram_id);
411
424
  if (!oldEngram) {
412
425
  return { content: [{ type: 'text', text: `Old memory not found: ${params.old_engram_id}` }] };
413
426
  }
414
- const newEngram = store.getEngram(params.new_engram_id);
427
+ const newEngram = await store.getEngram(params.new_engram_id);
415
428
  if (!newEngram) {
416
429
  return { content: [{ type: 'text', text: `New memory not found: ${params.new_engram_id}` }] };
417
430
  }
418
- store.supersedeEngram(params.old_engram_id, params.new_engram_id);
431
+ await store.supersedeEngram(params.old_engram_id, params.new_engram_id);
419
432
  // Create supersession association (new → old)
420
- store.upsertAssociation(params.new_engram_id, params.old_engram_id, 0.8, 'causal', 0.9);
433
+ await store.upsertAssociation(params.new_engram_id, params.old_engram_id, 0.8, 'causal', 0.9);
421
434
  // Reduce old memory's confidence (not to zero — it's historical, not wrong)
422
- store.updateConfidence(params.old_engram_id, Math.max(0.2, oldEngram.confidence * 0.4));
435
+ await store.updateConfidence(params.old_engram_id, Math.max(0.2, oldEngram.confidence * 0.4));
423
436
  log(AGENT_ID, 'supersede', `"${oldEngram.concept}" → "${newEngram.concept}"${params.reason ? ` (${params.reason})` : ''}`);
424
437
  return {
425
438
  content: [{
@@ -428,10 +441,10 @@ The old memory stays in the database (searchable for history) but is heavily dow
428
441
  }],
429
442
  };
430
443
  });
431
- server.tool('memory_stats', `Get memory health stats — how many memories, confidence levels, association count, and system performance.
444
+ server.tool('memory_stats', `Get memory health stats — how many memories, confidence levels, association count, and system performance.
432
445
  Also shows the activity log path so the user can tail it to see what's happening.`, {}, async () => {
433
- const metrics = evalEngine.computeMetrics(AGENT_ID);
434
- const checkpoint = store.getCheckpoint(AGENT_ID);
446
+ const metrics = await evalEngine.computeMetrics(AGENT_ID);
447
+ const checkpoint = await store.getCheckpoint(AGENT_ID);
435
448
  const lines = [
436
449
  `Agent: ${AGENT_ID}`,
437
450
  `Active memories: ${metrics.activeEngramCount}`,
@@ -459,13 +472,13 @@ Also shows the activity log path so the user can tail it to see what's happening
459
472
  };
460
473
  });
461
474
  // --- Checkpointing Tools ---
462
- server.tool('memory_checkpoint', `Save your current execution state so you can recover after context compaction.
463
-
464
- ALWAYS call this before:
465
- - Long operations (multi-file generation, large refactors, overnight work)
466
- - Anything that might fill the context window
467
- - Switching to a different task
468
-
475
+ server.tool('memory_checkpoint', `Save your current execution state so you can recover after context compaction.
476
+
477
+ ALWAYS call this before:
478
+ - Long operations (multi-file generation, large refactors, overnight work)
479
+ - Anything that might fill the context window
480
+ - Switching to a different task
481
+
469
482
  Also call periodically during long sessions to avoid losing state. The state is saved per-agent and overwrites any previous checkpoint.`, {
470
483
  current_task: z.string().describe('What you are currently working on'),
471
484
  decisions: z.array(z.string()).optional().default([])
@@ -490,7 +503,7 @@ Also call periodically during long sessions to avoid losing state. The state is
490
503
  notes: params.notes,
491
504
  episodeId: params.episode_id ?? null,
492
505
  };
493
- store.saveCheckpoint(AGENT_ID, state);
506
+ await store.saveCheckpoint(AGENT_ID, state);
494
507
  log(AGENT_ID, 'checkpoint', `"${params.current_task}" decisions=${params.decisions.length} files=${params.active_files.length}`);
495
508
  return {
496
509
  content: [{
@@ -499,16 +512,16 @@ Also call periodically during long sessions to avoid losing state. The state is
499
512
  }],
500
513
  };
501
514
  });
502
- server.tool('memory_restore', `Restore your previous execution state after context compaction or at session start.
503
-
504
- Returns:
505
- - Your saved execution state (task, decisions, next steps, files)
506
- - Recently recalled memories for context
507
- - Your last write for continuity
508
- - How long you were idle
509
-
515
+ server.tool('memory_restore', `Restore your previous execution state after context compaction or at session start.
516
+
517
+ Returns:
518
+ - Your saved execution state (task, decisions, next steps, files)
519
+ - Recently recalled memories for context
520
+ - Your last write for continuity
521
+ - How long you were idle
522
+
510
523
  Use this at the start of every session or after compaction to pick up where you left off.`, {}, async () => {
511
- const checkpoint = store.getCheckpoint(AGENT_ID);
524
+ const checkpoint = await store.getCheckpoint(AGENT_ID);
512
525
  const now = Date.now();
513
526
  const idleMs = checkpoint
514
527
  ? now - checkpoint.auto.lastActivityAt.getTime()
@@ -516,7 +529,7 @@ Use this at the start of every session or after compaction to pick up where you
516
529
  // Get last written engram
517
530
  let lastWrite = null;
518
531
  if (checkpoint?.auto.lastWriteId) {
519
- const engram = store.getEngram(checkpoint.auto.lastWriteId);
532
+ const engram = await store.getEngram(checkpoint.auto.lastWriteId);
520
533
  if (engram) {
521
534
  lastWrite = { id: engram.id, concept: engram.concept, content: engram.content };
522
535
  }
@@ -562,7 +575,7 @@ Use this at the start of every session or after compaction to pick up where you
562
575
  fullConsolidationTriggered = true;
563
576
  try {
564
577
  const result = await consolidationEngine.consolidate(AGENT_ID);
565
- store.markConsolidation(AGENT_ID, false);
578
+ await store.markConsolidation(AGENT_ID, false);
566
579
  log(AGENT_ID, 'consolidation', `full sleep cycle on restore (no graceful exit, idle ${Math.round(idleMs / 60_000)}min, last consolidation ${Math.round(sinceLastConsolidation / 60_000)}min ago) — ${result.edgesStrengthened} strengthened, ${result.memoriesForgotten} forgotten`);
567
580
  }
568
581
  catch { /* consolidation failure is non-fatal */ }
@@ -614,9 +627,9 @@ Use this at the start of every session or after compaction to pick up where you
614
627
  if (coordDb) {
615
628
  try {
616
629
  const myAgent = coordDb.prepare(`SELECT id FROM coord_agents WHERE name = ? AND status != 'dead' ORDER BY last_seen DESC LIMIT 1`).get(AGENT_ID);
617
- const peerDecisions = coordDb.prepare(`SELECT d.summary, a.name AS author_name, d.created_at
618
- FROM coord_decisions d JOIN coord_agents a ON d.author_id = a.id
619
- WHERE d.author_id != ? AND d.created_at > datetime('now', '-30 minutes')
630
+ const peerDecisions = coordDb.prepare(`SELECT d.summary, a.name AS author_name, d.created_at
631
+ FROM coord_decisions d JOIN coord_agents a ON d.author_id = a.id
632
+ WHERE d.author_id != ? AND d.created_at > datetime('now', '-30 minutes')
620
633
  ORDER BY d.created_at DESC LIMIT 10`).all(myAgent?.id ?? '');
621
634
  if (peerDecisions.length > 0) {
622
635
  parts.push(`\n**Peer decisions (last 30 min):**`);
@@ -635,13 +648,13 @@ Use this at the start of every session or after compaction to pick up where you
635
648
  };
636
649
  });
637
650
  // --- Task Management Tools ---
638
- server.tool('memory_task_add', `Create a task that you need to come back to. Tasks are memories with status and priority tracking.
639
-
640
- Use this when:
641
- - You identify work that needs doing but can't do it right now
642
- - The user mentions something to do later
643
- - You want to park a sub-task while focusing on something more urgent
644
-
651
+ server.tool('memory_task_add', `Create a task that you need to come back to. Tasks are memories with status and priority tracking.
652
+
653
+ Use this when:
654
+ - You identify work that needs doing but can't do it right now
655
+ - The user mentions something to do later
656
+ - You want to park a sub-task while focusing on something more urgent
657
+
645
658
  Tasks automatically get high salience so they won't be discarded.`, {
646
659
  concept: z.string().describe('Short task title (3-10 words)'),
647
660
  content: z.string().describe('Full task description — what needs doing, context, acceptance criteria'),
@@ -650,7 +663,7 @@ Tasks automatically get high salience so they won't be discarded.`, {
650
663
  .describe('Task priority: urgent (do now), high (do soon), medium (normal), low (backlog)'),
651
664
  blocked_by: z.string().optional().describe('ID of a task that must finish first'),
652
665
  }, async (params) => {
653
- const engram = store.createEngram({
666
+ const engram = await store.createEngram({
654
667
  agentId: AGENT_ID,
655
668
  concept: params.concept,
656
669
  content: params.content,
@@ -671,8 +684,8 @@ Tasks automatically get high salience so they won't be discarded.`, {
671
684
  });
672
685
  connectionEngine.enqueue(engram.id);
673
686
  // Generate embedding asynchronously
674
- embed(`${params.concept} ${params.content}`).then(vec => {
675
- store.updateEmbedding(engram.id, vec);
687
+ embed(`${params.concept} ${params.content}`).then(async (vec) => {
688
+ await store.updateEmbedding(engram.id, vec);
676
689
  }).catch(() => { });
677
690
  return {
678
691
  content: [{
@@ -681,11 +694,11 @@ Tasks automatically get high salience so they won't be discarded.`, {
681
694
  }],
682
695
  };
683
696
  });
684
- server.tool('memory_task_update', `Update a task's status or priority. Use this to:
685
- - Start working on a task (open → in_progress)
686
- - Mark a task done (→ done)
687
- - Block a task on another (→ blocked)
688
- - Reprioritize (change priority)
697
+ server.tool('memory_task_update', `Update a task's status or priority. Use this to:
698
+ - Start working on a task (open → in_progress)
699
+ - Mark a task done (→ done)
700
+ - Block a task on another (→ blocked)
701
+ - Reprioritize (change priority)
689
702
  - Unblock a task (clear blocked_by)`, {
690
703
  task_id: z.string().describe('ID of the task to update'),
691
704
  status: z.enum(['open', 'in_progress', 'blocked', 'done']).optional()
@@ -694,20 +707,20 @@ Tasks automatically get high salience so they won't be discarded.`, {
694
707
  .describe('New priority'),
695
708
  blocked_by: z.string().optional().describe('ID of blocking task (set to empty string to unblock)'),
696
709
  }, async (params) => {
697
- const engram = store.getEngram(params.task_id);
710
+ const engram = await store.getEngram(params.task_id);
698
711
  if (!engram || !engram.taskStatus) {
699
712
  return { content: [{ type: 'text', text: `Task not found: ${params.task_id}` }] };
700
713
  }
701
714
  if (params.blocked_by !== undefined) {
702
- store.updateBlockedBy(params.task_id, params.blocked_by || null);
715
+ await store.updateBlockedBy(params.task_id, params.blocked_by || null);
703
716
  }
704
717
  if (params.status) {
705
- store.updateTaskStatus(params.task_id, params.status);
718
+ await store.updateTaskStatus(params.task_id, params.status);
706
719
  }
707
720
  if (params.priority) {
708
- store.updateTaskPriority(params.task_id, params.priority);
721
+ await store.updateTaskPriority(params.task_id, params.priority);
709
722
  }
710
- const updated = store.getEngram(params.task_id);
723
+ const updated = (await store.getEngram(params.task_id));
711
724
  return {
712
725
  content: [{
713
726
  type: 'text',
@@ -715,15 +728,15 @@ Tasks automatically get high salience so they won't be discarded.`, {
715
728
  }],
716
729
  };
717
730
  });
718
- server.tool('memory_task_list', `List tasks with optional status filter. Shows tasks ordered by priority (urgent first).
719
-
731
+ server.tool('memory_task_list', `List tasks with optional status filter. Shows tasks ordered by priority (urgent first).
732
+
720
733
  Use at the start of a session to see what's pending, or to check blocked/done tasks.`, {
721
734
  status: z.enum(['open', 'in_progress', 'blocked', 'done']).optional()
722
735
  .describe('Filter by status (omit to see all active tasks)'),
723
736
  include_done: z.boolean().optional().default(false)
724
737
  .describe('Include completed tasks?'),
725
738
  }, async (params) => {
726
- let tasks = store.getTasks(AGENT_ID, params.status);
739
+ let tasks = await store.getTasks(AGENT_ID, params.status);
727
740
  if (!params.include_done && !params.status) {
728
741
  tasks = tasks.filter(t => t.taskStatus !== 'done');
729
742
  }
@@ -742,12 +755,12 @@ Use at the start of a session to see what's pending, or to check blocked/done ta
742
755
  }],
743
756
  };
744
757
  });
745
- server.tool('memory_task_next', `Get the single most important task to work on next.
746
-
747
- Prioritizes: in_progress tasks first (finish what you started), then by priority level, then oldest first. Skips blocked and done tasks.
748
-
758
+ server.tool('memory_task_next', `Get the single most important task to work on next.
759
+
760
+ Prioritizes: in_progress tasks first (finish what you started), then by priority level, then oldest first. Skips blocked and done tasks.
761
+
749
762
  Use this when you finish a task or need to decide what to do next.`, {}, async () => {
750
- const next = store.getNextTask(AGENT_ID);
763
+ const next = await store.getNextTask(AGENT_ID);
751
764
  if (!next) {
752
765
  return { content: [{ type: 'text', text: 'No actionable tasks. All clear!' }] };
753
766
  }
@@ -761,13 +774,13 @@ Use this when you finish a task or need to decide what to do next.`, {}, async (
761
774
  };
762
775
  });
763
776
  // --- Task Bracket Tools ---
764
- server.tool('memory_task_begin', `Signal that you're starting a significant task. Auto-checkpoints current state and recalls relevant memories.
765
-
766
- CALL THIS when starting:
767
- - A multi-step operation (doc generation, large refactor, migration)
768
- - Work on a new topic or project area
769
- - Anything that might fill the context window
770
-
777
+ server.tool('memory_task_begin', `Signal that you're starting a significant task. Auto-checkpoints current state and recalls relevant memories.
778
+
779
+ CALL THIS when starting:
780
+ - A multi-step operation (doc generation, large refactor, migration)
781
+ - Work on a new topic or project area
782
+ - Anything that might fill the context window
783
+
771
784
  This ensures your state is saved before you start, and primes recall with relevant context.`, {
772
785
  topic: z.string().describe('What task are you starting? (3-15 words)'),
773
786
  files: z.array(z.string()).optional().default([])
@@ -776,9 +789,9 @@ This ensures your state is saved before you start, and primes recall with releva
776
789
  .describe('Any additional context'),
777
790
  }, async (params) => {
778
791
  // 1. Checkpoint current state
779
- const checkpoint = store.getCheckpoint(AGENT_ID);
792
+ const checkpoint = await store.getCheckpoint(AGENT_ID);
780
793
  const prevTask = checkpoint?.executionState?.currentTask ?? 'None';
781
- store.saveCheckpoint(AGENT_ID, {
794
+ await store.saveCheckpoint(AGENT_ID, {
782
795
  currentTask: params.topic,
783
796
  decisions: [],
784
797
  activeFiles: params.files,
@@ -806,7 +819,7 @@ This ensures your state is saved before you start, and primes recall with releva
806
819
  });
807
820
  recalledSummary = `\n\n**Recalled memories (${results.length}):**\n${lines.join('\n')}`;
808
821
  // Track recall
809
- store.updateAutoCheckpointRecall(AGENT_ID, params.topic, results.map(r => r.engram.id));
822
+ await store.updateAutoCheckpointRecall(AGENT_ID, params.topic, results.map(r => r.engram.id));
810
823
  }
811
824
  }
812
825
  catch { /* recall failure is non-fatal */ }
@@ -818,13 +831,13 @@ This ensures your state is saved before you start, and primes recall with releva
818
831
  }],
819
832
  };
820
833
  });
821
- server.tool('memory_task_end', `Signal that you've finished a significant task. Writes a summary memory and auto-checkpoints.
822
-
823
- CALL THIS when you finish:
824
- - A multi-step operation
825
- - Before switching to a different topic
826
- - At the end of a work session
827
-
834
+ server.tool('memory_task_end', `Signal that you've finished a significant task. Writes a summary memory and auto-checkpoints.
835
+
836
+ CALL THIS when you finish:
837
+ - A multi-step operation
838
+ - Before switching to a different topic
839
+ - At the end of a work session
840
+
828
841
  This captures what was accomplished so future sessions can recall it.`, {
829
842
  summary: z.string().describe('What was accomplished? Include key outcomes, decisions, and any issues.'),
830
843
  tags: z.array(z.string()).optional().default([])
@@ -842,7 +855,7 @@ This captures what was accomplished so future sessions can recall it.`, {
842
855
  resolutionEffort: 0.5,
843
856
  });
844
857
  // Determine the real task name for the summary engram
845
- const checkpoint = store.getCheckpoint(AGENT_ID);
858
+ const checkpoint = await store.getCheckpoint(AGENT_ID);
846
859
  const rawTask = checkpoint?.executionState?.currentTask ?? 'Unknown task';
847
860
  // Strip any "Completed: " prefixes to avoid cascading
848
861
  const cleanedTask = rawTask.replace(/^(Completed: )+/, '');
@@ -851,7 +864,7 @@ This captures what was accomplished so future sessions can recall it.`, {
851
864
  const completedTask = isNamedTask
852
865
  ? cleanedTask
853
866
  : params.summary.slice(0, 60).replace(/\n/g, ' ');
854
- const engram = store.createEngram({
867
+ const engram = await store.createEngram({
855
868
  agentId: AGENT_ID,
856
869
  concept: completedTask.slice(0, 80),
857
870
  content: params.summary,
@@ -865,20 +878,20 @@ This captures what was accomplished so future sessions can recall it.`, {
865
878
  // 2. Handle supersessions — mark old memories as outdated
866
879
  let supersededCount = 0;
867
880
  for (const oldId of params.supersedes) {
868
- const oldEngram = store.getEngram(oldId);
881
+ const oldEngram = await store.getEngram(oldId);
869
882
  if (oldEngram) {
870
- store.supersedeEngram(oldId, engram.id);
871
- store.upsertAssociation(engram.id, oldId, 0.8, 'causal', 0.9);
872
- store.updateConfidence(oldId, Math.max(0.2, oldEngram.confidence * 0.4));
883
+ await store.supersedeEngram(oldId, engram.id);
884
+ await store.upsertAssociation(engram.id, oldId, 0.8, 'causal', 0.9);
885
+ await store.updateConfidence(oldId, Math.max(0.2, oldEngram.confidence * 0.4));
873
886
  supersededCount++;
874
887
  }
875
888
  }
876
889
  // Generate embedding asynchronously
877
- embed(`Task completed: ${params.summary}`).then(vec => {
878
- store.updateEmbedding(engram.id, vec);
890
+ embed(`Task completed: ${params.summary}`).then(async (vec) => {
891
+ await store.updateEmbedding(engram.id, vec);
879
892
  }).catch(() => { });
880
893
  // 2. Update checkpoint to reflect completion
881
- store.saveCheckpoint(AGENT_ID, {
894
+ await store.saveCheckpoint(AGENT_ID, {
882
895
  currentTask: `Completed: ${completedTask}`,
883
896
  decisions: checkpoint?.executionState?.decisions ?? [],
884
897
  activeFiles: [],
@@ -887,7 +900,7 @@ This captures what was accomplished so future sessions can recall it.`, {
887
900
  notes: `Task completed. Summary memory: ${engram.id}`,
888
901
  episodeId: null,
889
902
  });
890
- store.updateAutoCheckpointWrite(AGENT_ID, engram.id);
903
+ await store.updateAutoCheckpointWrite(AGENT_ID, engram.id);
891
904
  log(AGENT_ID, 'task:end', `"${completedTask}" summary=${engram.id} salience=${salience.score.toFixed(2)} superseded=${supersededCount}`);
892
905
  const supersededNote = supersededCount > 0 ? ` (${supersededCount} old memories superseded)` : '';
893
906
  return {
@@ -910,18 +923,26 @@ This captures what was accomplished so future sessions can recall it.`, {
910
923
  onConsolidate: async (agentId, reason) => {
911
924
  console.error(`[mcp] consolidation triggered: ${reason}`);
912
925
  const result = await consolidationEngine.consolidate(agentId);
913
- store.markConsolidation(agentId, false);
926
+ await store.markConsolidation(agentId, false);
914
927
  console.error(`[mcp] consolidation done: ${result.edgesStrengthened} strengthened, ${result.memoriesForgotten} forgotten`);
915
928
  },
916
929
  });
917
930
  // Coordination MCP tools (opt-in via AWM_COORDINATION=true)
918
- const coordEnabled = process.env.AWM_COORDINATION === 'true' || process.env.AWM_COORDINATION === '1';
931
+ // AWM 0.8.x: coordination requires SQLite (uses store.getDb()). On PGlite,
932
+ // coordination is auto-disabled with a warning; re-enable when coordination
933
+ // is ported to async/PGlite.
934
+ const coordRequested = process.env.AWM_COORDINATION === 'true' || process.env.AWM_COORDINATION === '1';
935
+ const coordEnabled = coordRequested && BACKEND === 'sqlite';
919
936
  if (coordEnabled) {
920
937
  const { initCoordinationTables } = await import('./coordination/schema.js');
921
938
  const { registerCoordinationTools } = await import('./coordination/mcp-tools.js');
922
- initCoordinationTables(store.getDb());
923
- registerCoordinationTools(server, store.getDb());
924
- coordDb = store.getDb();
939
+ const sqliteStore = store;
940
+ initCoordinationTables(sqliteStore.getDb());
941
+ registerCoordinationTools(server, sqliteStore.getDb());
942
+ coordDb = sqliteStore.getDb();
943
+ }
944
+ else if (coordRequested && BACKEND === 'pglite') {
945
+ console.error('AWM: coordination requested but disabled — coordination plugin requires SQLite backend');
925
946
  }
926
947
  else {
927
948
  console.error('AWM: coordination tools disabled (set AWM_COORDINATION=true to enable)');
@@ -930,18 +951,23 @@ This captures what was accomplished so future sessions can recall it.`, {
930
951
  console.error(`AgentWorkingMemory MCP server started (agent: ${AGENT_ID}, db: ${DB_PATH})`);
931
952
  console.error(`Hook sidecar on 127.0.0.1:${HOOK_PORT}${HOOK_SECRET ? ' (auth enabled)' : ' (no auth — set AWM_HOOK_SECRET)'}`);
932
953
  // Clean shutdown
933
- const cleanup = () => {
954
+ const cleanup = async () => {
934
955
  sidecar.close();
935
956
  consolidationScheduler.stop();
936
957
  stagingBuffer.stop();
958
+ if (BACKEND === 'sqlite') {
959
+ try {
960
+ store.walCheckpoint?.();
961
+ }
962
+ catch { /* non-fatal */ }
963
+ }
937
964
  try {
938
- store.walCheckpoint();
965
+ await store.close?.();
939
966
  }
940
- catch { /* non-fatal */ }
941
- store.close();
967
+ catch { /* best-effort */ }
942
968
  };
943
- process.on('SIGINT', () => { cleanup(); process.exit(0); });
944
- process.on('SIGTERM', () => { cleanup(); process.exit(0); });
969
+ process.on('SIGINT', () => { void cleanup().finally(() => process.exit(0)); });
970
+ process.on('SIGTERM', () => { void cleanup().finally(() => process.exit(0)); });
945
971
  }
946
972
  main().catch(err => {
947
973
  console.error('MCP server failed:', err);