agent-working-memory 0.7.16 → 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 (200) hide show
  1. package/README.md +156 -15
  2. package/dist/adapters/claude-code.d.ts.map +1 -1
  3. package/dist/adapters/claude-code.js +2 -16
  4. package/dist/adapters/claude-code.js.map +1 -1
  5. package/dist/adapters/codex.d.ts.map +1 -1
  6. package/dist/adapters/codex.js +2 -11
  7. package/dist/adapters/codex.js.map +1 -1
  8. package/dist/adapters/common.d.ts +18 -0
  9. package/dist/adapters/common.d.ts.map +1 -1
  10. package/dist/adapters/common.js +254 -141
  11. package/dist/adapters/common.js.map +1 -1
  12. package/dist/adapters/cursor.d.ts.map +1 -1
  13. package/dist/adapters/cursor.js +2 -15
  14. package/dist/adapters/cursor.js.map +1 -1
  15. package/dist/adapters/http.d.ts.map +1 -1
  16. package/dist/adapters/http.js +28 -34
  17. package/dist/adapters/http.js.map +1 -1
  18. package/dist/api/routes.d.ts +1 -1
  19. package/dist/api/routes.d.ts.map +1 -1
  20. package/dist/api/routes.js +280 -113
  21. package/dist/api/routes.js.map +1 -1
  22. package/dist/cli/migrate.d.ts +42 -0
  23. package/dist/cli/migrate.d.ts.map +1 -0
  24. package/dist/cli/migrate.js +268 -0
  25. package/dist/cli/migrate.js.map +1 -0
  26. package/dist/cli.js +45 -1
  27. package/dist/cli.js.map +1 -1
  28. package/dist/coordination/circuit-breaker.d.ts +24 -0
  29. package/dist/coordination/circuit-breaker.d.ts.map +1 -0
  30. package/dist/coordination/circuit-breaker.js +70 -0
  31. package/dist/coordination/circuit-breaker.js.map +1 -0
  32. package/dist/coordination/failure-modes.d.ts +21 -0
  33. package/dist/coordination/failure-modes.d.ts.map +1 -0
  34. package/dist/coordination/failure-modes.js +49 -0
  35. package/dist/coordination/failure-modes.js.map +1 -0
  36. package/dist/coordination/peer-decisions.js +7 -7
  37. package/dist/coordination/routes.d.ts.map +1 -1
  38. package/dist/coordination/routes.js +44 -1
  39. package/dist/coordination/routes.js.map +1 -1
  40. package/dist/coordination/schema.d.ts.map +1 -1
  41. package/dist/coordination/schema.js +18 -0
  42. package/dist/coordination/schema.js.map +1 -1
  43. package/dist/coordination/schemas.d.ts +9 -9
  44. package/dist/coordination/stale.d.ts +9 -1
  45. package/dist/coordination/stale.d.ts.map +1 -1
  46. package/dist/coordination/stale.js +53 -9
  47. package/dist/coordination/stale.js.map +1 -1
  48. package/dist/core/embeddings.d.ts +16 -10
  49. package/dist/core/embeddings.d.ts.map +1 -1
  50. package/dist/core/embeddings.js +54 -38
  51. package/dist/core/embeddings.js.map +1 -1
  52. package/dist/core/ml-worker-entry.d.ts +17 -0
  53. package/dist/core/ml-worker-entry.d.ts.map +1 -0
  54. package/dist/core/ml-worker-entry.js +193 -0
  55. package/dist/core/ml-worker-entry.js.map +1 -0
  56. package/dist/core/ml-worker.d.ts +59 -0
  57. package/dist/core/ml-worker.d.ts.map +1 -0
  58. package/dist/core/ml-worker.js +253 -0
  59. package/dist/core/ml-worker.js.map +1 -0
  60. package/dist/core/query-expander.d.ts +13 -15
  61. package/dist/core/query-expander.d.ts.map +1 -1
  62. package/dist/core/query-expander.js +48 -54
  63. package/dist/core/query-expander.js.map +1 -1
  64. package/dist/core/reranker.d.ts +9 -19
  65. package/dist/core/reranker.d.ts.map +1 -1
  66. package/dist/core/reranker.js +33 -35
  67. package/dist/core/reranker.js.map +1 -1
  68. package/dist/core/salience.d.ts +32 -5
  69. package/dist/core/salience.d.ts.map +1 -1
  70. package/dist/core/salience.js +201 -28
  71. package/dist/core/salience.js.map +1 -1
  72. package/dist/core/write-pipeline.d.ts +128 -0
  73. package/dist/core/write-pipeline.d.ts.map +1 -0
  74. package/dist/core/write-pipeline.js +405 -0
  75. package/dist/core/write-pipeline.js.map +1 -0
  76. package/dist/engine/activation.d.ts +2 -2
  77. package/dist/engine/activation.d.ts.map +1 -1
  78. package/dist/engine/activation.js +238 -115
  79. package/dist/engine/activation.js.map +1 -1
  80. package/dist/engine/confidence.d.ts +62 -0
  81. package/dist/engine/confidence.d.ts.map +1 -0
  82. package/dist/engine/confidence.js +100 -0
  83. package/dist/engine/confidence.js.map +1 -0
  84. package/dist/engine/connections.d.ts +47 -8
  85. package/dist/engine/connections.d.ts.map +1 -1
  86. package/dist/engine/connections.js +83 -22
  87. package/dist/engine/connections.js.map +1 -1
  88. package/dist/engine/consolidation-scheduler.d.ts +46 -10
  89. package/dist/engine/consolidation-scheduler.d.ts.map +1 -1
  90. package/dist/engine/consolidation-scheduler.js +170 -50
  91. package/dist/engine/consolidation-scheduler.js.map +1 -1
  92. package/dist/engine/consolidation.d.ts +16 -2
  93. package/dist/engine/consolidation.d.ts.map +1 -1
  94. package/dist/engine/consolidation.js +143 -44
  95. package/dist/engine/consolidation.js.map +1 -1
  96. package/dist/engine/eval.d.ts +2 -2
  97. package/dist/engine/eval.d.ts.map +1 -1
  98. package/dist/engine/eval.js +9 -9
  99. package/dist/engine/eval.js.map +1 -1
  100. package/dist/engine/eviction.d.ts +4 -4
  101. package/dist/engine/eviction.d.ts.map +1 -1
  102. package/dist/engine/eviction.js +15 -15
  103. package/dist/engine/eviction.js.map +1 -1
  104. package/dist/engine/retraction.d.ts +93 -5
  105. package/dist/engine/retraction.d.ts.map +1 -1
  106. package/dist/engine/retraction.js +238 -19
  107. package/dist/engine/retraction.js.map +1 -1
  108. package/dist/engine/staging.d.ts +1 -1
  109. package/dist/engine/staging.d.ts.map +1 -1
  110. package/dist/engine/staging.js +3 -3
  111. package/dist/engine/staging.js.map +1 -1
  112. package/dist/hooks/sidecar.d.ts +1 -1
  113. package/dist/hooks/sidecar.d.ts.map +1 -1
  114. package/dist/hooks/sidecar.js +4 -4
  115. package/dist/hooks/sidecar.js.map +1 -1
  116. package/dist/index.js +48 -31
  117. package/dist/index.js.map +1 -1
  118. package/dist/mcp.js +110 -155
  119. package/dist/mcp.js.map +1 -1
  120. package/dist/storage/factory.d.ts +55 -0
  121. package/dist/storage/factory.d.ts.map +1 -0
  122. package/dist/storage/factory.js +135 -0
  123. package/dist/storage/factory.js.map +1 -0
  124. package/dist/storage/pglite-schema.d.ts +20 -0
  125. package/dist/storage/pglite-schema.d.ts.map +1 -0
  126. package/dist/storage/pglite-schema.js +165 -0
  127. package/dist/storage/pglite-schema.js.map +1 -0
  128. package/dist/storage/pglite.d.ts +187 -0
  129. package/dist/storage/pglite.d.ts.map +1 -0
  130. package/dist/storage/pglite.js +1114 -0
  131. package/dist/storage/pglite.js.map +1 -0
  132. package/dist/storage/sqlite.d.ts +117 -0
  133. package/dist/storage/sqlite.d.ts.map +1 -1
  134. package/dist/storage/sqlite.js +357 -8
  135. package/dist/storage/sqlite.js.map +1 -1
  136. package/dist/storage/store.d.ts +63 -0
  137. package/dist/storage/store.d.ts.map +1 -0
  138. package/dist/storage/store.js +27 -0
  139. package/dist/storage/store.js.map +1 -0
  140. package/dist/types/engram.d.ts +92 -2
  141. package/dist/types/engram.d.ts.map +1 -1
  142. package/dist/types/engram.js.map +1 -1
  143. package/package.json +3 -1
  144. package/src/adapters/claude-code.ts +218 -234
  145. package/src/adapters/codex.ts +252 -262
  146. package/src/adapters/common.ts +411 -284
  147. package/src/adapters/cursor.ts +135 -150
  148. package/src/adapters/http.ts +93 -100
  149. package/src/adapters/index.ts +31 -31
  150. package/src/adapters/types.ts +75 -75
  151. package/src/api/index.ts +3 -3
  152. package/src/api/routes.ts +367 -120
  153. package/src/cli/migrate.ts +307 -0
  154. package/src/cli.ts +37 -1
  155. package/src/coordination/circuit-breaker.ts +83 -0
  156. package/src/coordination/events.ts +90 -90
  157. package/src/coordination/failure-modes.ts +50 -0
  158. package/src/coordination/peer-decisions.ts +105 -105
  159. package/src/coordination/plugin-loader.ts +60 -60
  160. package/src/coordination/plugin.ts +44 -44
  161. package/src/coordination/routes.ts +56 -1
  162. package/src/coordination/schema.ts +13 -0
  163. package/src/coordination/stale.ts +80 -11
  164. package/src/coordination/types.ts +311 -311
  165. package/src/coordination/write-mutex.ts +69 -69
  166. package/src/core/auto-tagger.ts +168 -168
  167. package/src/core/decay.ts +63 -63
  168. package/src/core/embeddings.ts +110 -93
  169. package/src/core/index.ts +5 -5
  170. package/src/core/logger.ts +36 -36
  171. package/src/core/ml-worker-entry.ts +194 -0
  172. package/src/core/ml-worker.ts +281 -0
  173. package/src/core/query-expander.ts +122 -128
  174. package/src/core/reranker.ts +119 -125
  175. package/src/core/salience.ts +200 -33
  176. package/src/core/write-pipeline.ts +521 -0
  177. package/src/engine/activation.ts +235 -109
  178. package/src/engine/confidence.ts +120 -0
  179. package/src/engine/connections.ts +162 -103
  180. package/src/engine/consolidation-scheduler.ts +242 -125
  181. package/src/engine/consolidation.ts +138 -45
  182. package/src/engine/eval.ts +102 -102
  183. package/src/engine/eviction.ts +101 -101
  184. package/src/engine/index.ts +8 -8
  185. package/src/engine/retraction.ts +366 -116
  186. package/src/engine/staging.ts +74 -74
  187. package/src/hooks/sidecar.ts +5 -5
  188. package/src/index.ts +42 -28
  189. package/src/mcp.ts +105 -152
  190. package/src/storage/factory.ts +147 -0
  191. package/src/storage/index.ts +3 -3
  192. package/src/storage/pglite-schema.ts +166 -0
  193. package/src/storage/pglite.ts +1363 -0
  194. package/src/storage/sqlite.ts +402 -7
  195. package/src/storage/store.ts +80 -0
  196. package/src/types/agent.ts +67 -67
  197. package/src/types/checkpoint.ts +46 -46
  198. package/src/types/engram.ts +101 -3
  199. package/src/types/eval.ts +100 -100
  200. 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';
@@ -59,7 +59,8 @@ import { RetractionEngine } from './engine/retraction.js';
59
59
  import { EvalEngine } from './engine/eval.js';
60
60
  import { ConsolidationEngine } from './engine/consolidation.js';
61
61
  import { ConsolidationScheduler } from './engine/consolidation-scheduler.js';
62
- import { evaluateSalience, computeNoveltyWithMatch } from './core/salience.js';
62
+ import { evaluateSalience } from './core/salience.js';
63
+ import { performWrite } from './core/write-pipeline.js';
63
64
  import { DEFAULT_AGENT_CONFIG } from './types/agent.js';
64
65
  import { embed } from './core/embeddings.js';
65
66
  import { startSidecar } from './hooks/sidecar.js';
@@ -71,7 +72,7 @@ import { queryPeerDecisions, formatPeerDecisions } from './coordination/peer-dec
71
72
  const INCOGNITO = process.env.AWM_INCOGNITO === '1' || process.env.AWM_INCOGNITO === 'true';
72
73
  if (INCOGNITO) {
73
74
  console.error('AWM: incognito mode — all memory tools disabled, nothing will be recorded');
74
- const server = new McpServer({ name: 'agent-working-memory', version: '0.7.16' });
75
+ const server = new McpServer({ name: 'agent-working-memory', version: '0.8.5' });
75
76
  const transport = new StdioServerTransport();
76
77
  server.connect(transport).catch(err => {
77
78
  console.error('MCP server failed:', err);
@@ -81,20 +82,25 @@ if (INCOGNITO) {
81
82
  }
82
83
  else {
83
84
  // --- Setup ---
84
- 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');
85
87
  const AGENT_ID = process.env.AWM_AGENT_ID ?? process.env.WORKER_NAME ?? 'claude-code';
86
88
  const HOOK_PORT = parseInt(process.env.AWM_HOOK_PORT ?? '8401', 10);
87
89
  const HOOK_SECRET = process.env.AWM_HOOK_SECRET ?? null;
88
90
  initLogger(DB_PATH);
89
- log(AGENT_ID, 'startup', `MCP server starting (db: ${DB_PATH}, hooks: ${HOOK_PORT})`);
90
- 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;
91
97
  const activationEngine = new ActivationEngine(store);
92
98
  const connectionEngine = new ConnectionEngine(store, activationEngine);
93
99
  const stagingBuffer = new StagingBuffer(store, activationEngine);
94
100
  const evictionEngine = new EvictionEngine(store);
95
101
  const retractionEngine = new RetractionEngine(store);
96
102
  const evalEngine = new EvalEngine(store);
97
- const consolidationEngine = new ConsolidationEngine(store);
103
+ const consolidationEngine = new ConsolidationEngine(store, connectionEngine);
98
104
  const consolidationScheduler = new ConsolidationScheduler(store, consolidationEngine);
99
105
  stagingBuffer.start(DEFAULT_AGENT_CONFIG.stagingTtlMs);
100
106
  consolidationScheduler.start();
@@ -102,7 +108,7 @@ else {
102
108
  let coordDb = null;
103
109
  const server = new McpServer({
104
110
  name: 'agent-working-memory',
105
- version: '0.7.16',
111
+ version: '0.8.5',
106
112
  });
107
113
  server.registerResource('awm-overview', 'awm://server/overview', {
108
114
  title: 'AWM Overview',
@@ -206,70 +212,6 @@ The concept should be a short label (3-8 words). The content should be the full
206
212
  intent: z.enum(['decision', 'question', 'todo', 'finding', 'context']).optional()
207
213
  .describe('What kind of memory this is.'),
208
214
  }, async (params) => {
209
- // Check novelty with match info for reinforcement
210
- const noveltyResult = computeNoveltyWithMatch(store, AGENT_ID, params.concept, params.content);
211
- const novelty = noveltyResult.novelty;
212
- // --- Reinforce-on-Duplicate check ---
213
- // Tightened thresholds: require near-exact match (novelty < 0.3, BM25 > 0.85, 60% content overlap)
214
- if (novelty < 0.3
215
- && noveltyResult.matchScore > 0.85
216
- && noveltyResult.matchedEngramId) {
217
- const matchedEngram = store.getEngram(noveltyResult.matchedEngramId);
218
- if (matchedEngram) {
219
- const existingTokens = new Set(matchedEngram.content.toLowerCase().split(/\s+/).filter(w => w.length > 3));
220
- const newTokens = new Set(params.content.toLowerCase().split(/\s+/).filter(w => w.length > 3));
221
- let overlap = 0;
222
- for (const t of newTokens) {
223
- if (existingTokens.has(t))
224
- overlap++;
225
- }
226
- const contentOverlap = newTokens.size > 0 ? overlap / newTokens.size : 0;
227
- if (contentOverlap > 0.6) {
228
- // True duplicate — reinforce existing and skip creation
229
- store.touchEngram(noveltyResult.matchedEngramId);
230
- try {
231
- store.updateAutoCheckpointWrite(AGENT_ID, noveltyResult.matchedEngramId);
232
- }
233
- catch { /* non-fatal */ }
234
- log(AGENT_ID, 'write:reinforce', `"${params.concept}" → reinforced "${matchedEngram.concept}" (overlap=${contentOverlap.toFixed(2)})`);
235
- return {
236
- content: [{
237
- type: 'text',
238
- text: `Reinforced existing memory "${matchedEngram.concept}" (overlap ${(contentOverlap * 100).toFixed(0)}%)`,
239
- }],
240
- };
241
- }
242
- // Partial match — continue to create new memory
243
- log(AGENT_ID, 'write:partial-match', `"${params.concept}" partially matched "${matchedEngram.concept}" (overlap=${contentOverlap.toFixed(2)}), creating new memory`);
244
- }
245
- }
246
- const salience = evaluateSalience({
247
- content: params.content,
248
- eventType: params.event_type,
249
- surprise: params.surprise,
250
- decisionMade: params.decision_made,
251
- causalDepth: params.causal_depth,
252
- resolutionEffort: params.resolution_effort,
253
- novelty,
254
- memoryClass: params.memory_class,
255
- });
256
- // v0.5.4: No longer discard — store everything, use salience for ranking.
257
- // Low-salience memories get low confidence so they rank below high-salience
258
- // in retrieval, but remain available for recall when needed.
259
- const isLowSalience = salience.disposition === 'discard';
260
- const CONFIDENCE_PRIORS = {
261
- decision: 0.65,
262
- friction: 0.60,
263
- causal: 0.60,
264
- surprise: 0.55,
265
- observation: 0.45,
266
- };
267
- const confidencePrior = isLowSalience
268
- ? 0.25
269
- : salience.disposition === 'staging'
270
- ? 0.40
271
- : CONFIDENCE_PRIORS[params.event_type ?? 'observation'] ?? 0.45;
272
- const memoryType = params.memory_type ?? classifyMemoryType(params.content);
273
215
  // Assemble tags: user-provided + agent metadata (stored as searchable prefixed tags)
274
216
  const userTags = params.tags ?? [];
275
217
  const metaTags = [];
@@ -285,47 +227,39 @@ The concept should be a short label (3-8 words). The content should be the full
285
227
  metaTags.push(`sid=${params.session_id}`);
286
228
  if (params.intent)
287
229
  metaTags.push(`intent=${params.intent}`);
288
- const allTags = isLowSalience
289
- ? [...userTags, ...metaTags, 'low-salience']
290
- : [...userTags, ...metaTags];
291
- const engram = store.createEngram({
230
+ const memoryType = params.memory_type ?? classifyMemoryType(params.content);
231
+ const result = await performWrite({ store, connectionEngine }, {
292
232
  agentId: AGENT_ID,
293
233
  concept: params.concept,
294
234
  content: params.content,
295
- tags: allTags,
296
- salience: salience.score,
297
- confidence: confidencePrior,
298
- salienceFeatures: salience.features,
299
- reasonCodes: salience.reasonCodes,
300
- ttl: salience.disposition === 'staging' ? DEFAULT_AGENT_CONFIG.stagingTtlMs : undefined,
235
+ tags: [...userTags, ...metaTags],
236
+ eventType: params.event_type,
237
+ surprise: params.surprise,
238
+ decisionMade: params.decision_made,
239
+ causalDepth: params.causal_depth,
240
+ resolutionEffort: params.resolution_effort,
301
241
  memoryClass: params.memory_class,
302
242
  memoryType,
303
243
  supersedes: params.supersedes,
304
244
  });
305
- if (salience.disposition === 'staging') {
306
- store.updateStage(engram.id, 'staging');
307
- }
308
- else {
309
- connectionEngine.enqueue(engram.id);
310
- }
311
- // Handle supersession: mark old memory as superseded
312
- if (params.supersedes) {
313
- const oldEngram = store.getEngram(params.supersedes);
314
- if (oldEngram) {
315
- store.supersedeEngram(params.supersedes, engram.id);
316
- // Create supersession association
317
- store.upsertAssociation(engram.id, oldEngram.id, 0.8, 'causal', 0.9);
318
- }
319
- }
320
- // Generate embedding asynchronously (don't block response)
321
- embed(`${params.concept} ${params.content}`).then(vec => {
322
- store.updateEmbedding(engram.id, vec);
323
- }).catch(() => { }); // Embedding failure is non-fatal
324
- // Auto-checkpoint: track write
245
+ // Auto-checkpoint covers create/reinforce/supersede uniformly
325
246
  try {
326
- store.updateAutoCheckpointWrite(AGENT_ID, engram.id);
247
+ await store.updateAutoCheckpointWrite(AGENT_ID, result.engram.id);
327
248
  }
328
249
  catch { /* non-fatal */ }
250
+ if (result.action === 'reinforce') {
251
+ log(AGENT_ID, 'write:reinforce', `"${params.concept}" → reinforced "${result.engram.concept}" (conf ${result.reinforce.previousConfidence.toFixed(2)} → ${result.reinforce.newConfidence.toFixed(2)}, novelty=${result.noveltyResult.novelty.toFixed(2)})`);
252
+ return {
253
+ content: [{
254
+ type: 'text',
255
+ text: `Reinforced existing memory "${result.engram.concept}" (confidence ${result.reinforce.previousConfidence.toFixed(2)} → ${result.reinforce.newConfidence.toFixed(2)})`,
256
+ }],
257
+ };
258
+ }
259
+ const engram = result.engram;
260
+ const salience = result.salience; // create/supersede always have salience
261
+ const novelty = result.noveltyResult.novelty;
262
+ const isLowSalience = salience.disposition === 'discard';
329
263
  // Decision propagation: when decision_made=true and coordination is enabled,
330
264
  // broadcast to coord_decisions so other agents can discover it
331
265
  if (params.decision_made && coordDb) {
@@ -365,6 +299,8 @@ Returns the most relevant memories ranked by text relevance, temporal recency, a
365
299
  use_expansion: z.boolean().optional().default(true).describe('Expand query with synonyms for better recall (default true)'),
366
300
  memory_type: z.enum(['episodic', 'semantic', 'procedural']).optional().describe('Filter by memory type (omit to search all types)'),
367
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.'),
368
304
  }, async (params) => {
369
305
  const queryText = params.query ?? params.context;
370
306
  if (!queryText) {
@@ -387,11 +323,13 @@ Returns the most relevant memories ranked by text relevance, temporal recency, a
387
323
  useExpansion: params.use_expansion,
388
324
  memoryType: params.memory_type,
389
325
  workspace,
326
+ requireConfidence: params.require_confidence,
327
+ granularity: params.granularity,
390
328
  });
391
329
  // Auto-checkpoint: track recall
392
330
  try {
393
331
  const ids = results.map(r => r.engram.id);
394
- store.updateAutoCheckpointRecall(AGENT_ID, queryText, ids);
332
+ await store.updateAutoCheckpointRecall(AGENT_ID, queryText, ids);
395
333
  }
396
334
  catch { /* non-fatal */ }
397
335
  log(AGENT_ID, 'recall', `"${queryText.slice(0, 80)}" → ${results.length} results`);
@@ -408,7 +346,11 @@ Returns the most relevant memories ranked by text relevance, temporal recency, a
408
346
  };
409
347
  }
410
348
  const lines = results.map((r, i) => {
411
- 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}`;
412
354
  });
413
355
  return {
414
356
  content: [{
@@ -424,16 +366,16 @@ Always call this after using a recalled memory so the system learns what's valua
424
366
  useful: z.boolean().describe('Was this memory actually helpful?'),
425
367
  context: z.string().optional().describe('Brief note on why it was/wasn\'t useful'),
426
368
  }, async (params) => {
427
- store.logRetrievalFeedback(null, params.engram_id, params.useful, params.context ?? '');
428
- 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);
429
371
  if (engram) {
430
372
  const delta = params.useful
431
373
  ? DEFAULT_AGENT_CONFIG.feedbackPositiveBoost
432
374
  : -DEFAULT_AGENT_CONFIG.feedbackNegativePenalty;
433
- store.updateConfidence(engram.id, engram.confidence + delta);
375
+ await store.updateConfidence(engram.id, engram.confidence + delta);
434
376
  }
435
377
  // Validation-gated Hebbian: resolve pending co-activation pairs for this engram
436
- const hebbianUpdated = activationEngine.resolveHebbianFeedback(params.engram_id, params.useful);
378
+ const hebbianUpdated = await activationEngine.resolveHebbianFeedback(params.engram_id, params.useful);
437
379
  return {
438
380
  content: [{
439
381
  type: 'text',
@@ -448,7 +390,7 @@ Use this when you discover a memory contains incorrect information.`, {
448
390
  reason: z.string().describe('Why is this memory wrong?'),
449
391
  correction: z.string().optional().describe('What is the correct information? (creates a new memory)'),
450
392
  }, async (params) => {
451
- const result = retractionEngine.retract({
393
+ const result = await retractionEngine.retract({
452
394
  agentId: AGENT_ID,
453
395
  targetEngramId: params.engram_id,
454
396
  reason: params.reason,
@@ -478,19 +420,19 @@ The old memory stays in the database (searchable for history) but is heavily dow
478
420
  new_engram_id: z.string().describe('ID of the replacement memory'),
479
421
  reason: z.string().optional().describe('Why the old memory is outdated'),
480
422
  }, async (params) => {
481
- const oldEngram = store.getEngram(params.old_engram_id);
423
+ const oldEngram = await store.getEngram(params.old_engram_id);
482
424
  if (!oldEngram) {
483
425
  return { content: [{ type: 'text', text: `Old memory not found: ${params.old_engram_id}` }] };
484
426
  }
485
- const newEngram = store.getEngram(params.new_engram_id);
427
+ const newEngram = await store.getEngram(params.new_engram_id);
486
428
  if (!newEngram) {
487
429
  return { content: [{ type: 'text', text: `New memory not found: ${params.new_engram_id}` }] };
488
430
  }
489
- store.supersedeEngram(params.old_engram_id, params.new_engram_id);
431
+ await store.supersedeEngram(params.old_engram_id, params.new_engram_id);
490
432
  // Create supersession association (new → old)
491
- 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);
492
434
  // Reduce old memory's confidence (not to zero — it's historical, not wrong)
493
- 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));
494
436
  log(AGENT_ID, 'supersede', `"${oldEngram.concept}" → "${newEngram.concept}"${params.reason ? ` (${params.reason})` : ''}`);
495
437
  return {
496
438
  content: [{
@@ -501,8 +443,8 @@ The old memory stays in the database (searchable for history) but is heavily dow
501
443
  });
502
444
  server.tool('memory_stats', `Get memory health stats — how many memories, confidence levels, association count, and system performance.
503
445
  Also shows the activity log path so the user can tail it to see what's happening.`, {}, async () => {
504
- const metrics = evalEngine.computeMetrics(AGENT_ID);
505
- const checkpoint = store.getCheckpoint(AGENT_ID);
446
+ const metrics = await evalEngine.computeMetrics(AGENT_ID);
447
+ const checkpoint = await store.getCheckpoint(AGENT_ID);
506
448
  const lines = [
507
449
  `Agent: ${AGENT_ID}`,
508
450
  `Active memories: ${metrics.activeEngramCount}`,
@@ -561,7 +503,7 @@ Also call periodically during long sessions to avoid losing state. The state is
561
503
  notes: params.notes,
562
504
  episodeId: params.episode_id ?? null,
563
505
  };
564
- store.saveCheckpoint(AGENT_ID, state);
506
+ await store.saveCheckpoint(AGENT_ID, state);
565
507
  log(AGENT_ID, 'checkpoint', `"${params.current_task}" decisions=${params.decisions.length} files=${params.active_files.length}`);
566
508
  return {
567
509
  content: [{
@@ -579,7 +521,7 @@ Returns:
579
521
  - How long you were idle
580
522
 
581
523
  Use this at the start of every session or after compaction to pick up where you left off.`, {}, async () => {
582
- const checkpoint = store.getCheckpoint(AGENT_ID);
524
+ const checkpoint = await store.getCheckpoint(AGENT_ID);
583
525
  const now = Date.now();
584
526
  const idleMs = checkpoint
585
527
  ? now - checkpoint.auto.lastActivityAt.getTime()
@@ -587,7 +529,7 @@ Use this at the start of every session or after compaction to pick up where you
587
529
  // Get last written engram
588
530
  let lastWrite = null;
589
531
  if (checkpoint?.auto.lastWriteId) {
590
- const engram = store.getEngram(checkpoint.auto.lastWriteId);
532
+ const engram = await store.getEngram(checkpoint.auto.lastWriteId);
591
533
  if (engram) {
592
534
  lastWrite = { id: engram.id, concept: engram.concept, content: engram.content };
593
535
  }
@@ -633,7 +575,7 @@ Use this at the start of every session or after compaction to pick up where you
633
575
  fullConsolidationTriggered = true;
634
576
  try {
635
577
  const result = await consolidationEngine.consolidate(AGENT_ID);
636
- store.markConsolidation(AGENT_ID, false);
578
+ await store.markConsolidation(AGENT_ID, false);
637
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`);
638
580
  }
639
581
  catch { /* consolidation failure is non-fatal */ }
@@ -721,7 +663,7 @@ Tasks automatically get high salience so they won't be discarded.`, {
721
663
  .describe('Task priority: urgent (do now), high (do soon), medium (normal), low (backlog)'),
722
664
  blocked_by: z.string().optional().describe('ID of a task that must finish first'),
723
665
  }, async (params) => {
724
- const engram = store.createEngram({
666
+ const engram = await store.createEngram({
725
667
  agentId: AGENT_ID,
726
668
  concept: params.concept,
727
669
  content: params.content,
@@ -742,8 +684,8 @@ Tasks automatically get high salience so they won't be discarded.`, {
742
684
  });
743
685
  connectionEngine.enqueue(engram.id);
744
686
  // Generate embedding asynchronously
745
- embed(`${params.concept} ${params.content}`).then(vec => {
746
- store.updateEmbedding(engram.id, vec);
687
+ embed(`${params.concept} ${params.content}`).then(async (vec) => {
688
+ await store.updateEmbedding(engram.id, vec);
747
689
  }).catch(() => { });
748
690
  return {
749
691
  content: [{
@@ -765,20 +707,20 @@ Tasks automatically get high salience so they won't be discarded.`, {
765
707
  .describe('New priority'),
766
708
  blocked_by: z.string().optional().describe('ID of blocking task (set to empty string to unblock)'),
767
709
  }, async (params) => {
768
- const engram = store.getEngram(params.task_id);
710
+ const engram = await store.getEngram(params.task_id);
769
711
  if (!engram || !engram.taskStatus) {
770
712
  return { content: [{ type: 'text', text: `Task not found: ${params.task_id}` }] };
771
713
  }
772
714
  if (params.blocked_by !== undefined) {
773
- store.updateBlockedBy(params.task_id, params.blocked_by || null);
715
+ await store.updateBlockedBy(params.task_id, params.blocked_by || null);
774
716
  }
775
717
  if (params.status) {
776
- store.updateTaskStatus(params.task_id, params.status);
718
+ await store.updateTaskStatus(params.task_id, params.status);
777
719
  }
778
720
  if (params.priority) {
779
- store.updateTaskPriority(params.task_id, params.priority);
721
+ await store.updateTaskPriority(params.task_id, params.priority);
780
722
  }
781
- const updated = store.getEngram(params.task_id);
723
+ const updated = (await store.getEngram(params.task_id));
782
724
  return {
783
725
  content: [{
784
726
  type: 'text',
@@ -794,7 +736,7 @@ Use at the start of a session to see what's pending, or to check blocked/done ta
794
736
  include_done: z.boolean().optional().default(false)
795
737
  .describe('Include completed tasks?'),
796
738
  }, async (params) => {
797
- let tasks = store.getTasks(AGENT_ID, params.status);
739
+ let tasks = await store.getTasks(AGENT_ID, params.status);
798
740
  if (!params.include_done && !params.status) {
799
741
  tasks = tasks.filter(t => t.taskStatus !== 'done');
800
742
  }
@@ -818,7 +760,7 @@ Use at the start of a session to see what's pending, or to check blocked/done ta
818
760
  Prioritizes: in_progress tasks first (finish what you started), then by priority level, then oldest first. Skips blocked and done tasks.
819
761
 
820
762
  Use this when you finish a task or need to decide what to do next.`, {}, async () => {
821
- const next = store.getNextTask(AGENT_ID);
763
+ const next = await store.getNextTask(AGENT_ID);
822
764
  if (!next) {
823
765
  return { content: [{ type: 'text', text: 'No actionable tasks. All clear!' }] };
824
766
  }
@@ -847,9 +789,9 @@ This ensures your state is saved before you start, and primes recall with releva
847
789
  .describe('Any additional context'),
848
790
  }, async (params) => {
849
791
  // 1. Checkpoint current state
850
- const checkpoint = store.getCheckpoint(AGENT_ID);
792
+ const checkpoint = await store.getCheckpoint(AGENT_ID);
851
793
  const prevTask = checkpoint?.executionState?.currentTask ?? 'None';
852
- store.saveCheckpoint(AGENT_ID, {
794
+ await store.saveCheckpoint(AGENT_ID, {
853
795
  currentTask: params.topic,
854
796
  decisions: [],
855
797
  activeFiles: params.files,
@@ -877,7 +819,7 @@ This ensures your state is saved before you start, and primes recall with releva
877
819
  });
878
820
  recalledSummary = `\n\n**Recalled memories (${results.length}):**\n${lines.join('\n')}`;
879
821
  // Track recall
880
- 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));
881
823
  }
882
824
  }
883
825
  catch { /* recall failure is non-fatal */ }
@@ -913,7 +855,7 @@ This captures what was accomplished so future sessions can recall it.`, {
913
855
  resolutionEffort: 0.5,
914
856
  });
915
857
  // Determine the real task name for the summary engram
916
- const checkpoint = store.getCheckpoint(AGENT_ID);
858
+ const checkpoint = await store.getCheckpoint(AGENT_ID);
917
859
  const rawTask = checkpoint?.executionState?.currentTask ?? 'Unknown task';
918
860
  // Strip any "Completed: " prefixes to avoid cascading
919
861
  const cleanedTask = rawTask.replace(/^(Completed: )+/, '');
@@ -922,7 +864,7 @@ This captures what was accomplished so future sessions can recall it.`, {
922
864
  const completedTask = isNamedTask
923
865
  ? cleanedTask
924
866
  : params.summary.slice(0, 60).replace(/\n/g, ' ');
925
- const engram = store.createEngram({
867
+ const engram = await store.createEngram({
926
868
  agentId: AGENT_ID,
927
869
  concept: completedTask.slice(0, 80),
928
870
  content: params.summary,
@@ -936,20 +878,20 @@ This captures what was accomplished so future sessions can recall it.`, {
936
878
  // 2. Handle supersessions — mark old memories as outdated
937
879
  let supersededCount = 0;
938
880
  for (const oldId of params.supersedes) {
939
- const oldEngram = store.getEngram(oldId);
881
+ const oldEngram = await store.getEngram(oldId);
940
882
  if (oldEngram) {
941
- store.supersedeEngram(oldId, engram.id);
942
- store.upsertAssociation(engram.id, oldId, 0.8, 'causal', 0.9);
943
- 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));
944
886
  supersededCount++;
945
887
  }
946
888
  }
947
889
  // Generate embedding asynchronously
948
- embed(`Task completed: ${params.summary}`).then(vec => {
949
- store.updateEmbedding(engram.id, vec);
890
+ embed(`Task completed: ${params.summary}`).then(async (vec) => {
891
+ await store.updateEmbedding(engram.id, vec);
950
892
  }).catch(() => { });
951
893
  // 2. Update checkpoint to reflect completion
952
- store.saveCheckpoint(AGENT_ID, {
894
+ await store.saveCheckpoint(AGENT_ID, {
953
895
  currentTask: `Completed: ${completedTask}`,
954
896
  decisions: checkpoint?.executionState?.decisions ?? [],
955
897
  activeFiles: [],
@@ -958,7 +900,7 @@ This captures what was accomplished so future sessions can recall it.`, {
958
900
  notes: `Task completed. Summary memory: ${engram.id}`,
959
901
  episodeId: null,
960
902
  });
961
- store.updateAutoCheckpointWrite(AGENT_ID, engram.id);
903
+ await store.updateAutoCheckpointWrite(AGENT_ID, engram.id);
962
904
  log(AGENT_ID, 'task:end', `"${completedTask}" summary=${engram.id} salience=${salience.score.toFixed(2)} superseded=${supersededCount}`);
963
905
  const supersededNote = supersededCount > 0 ? ` (${supersededCount} old memories superseded)` : '';
964
906
  return {
@@ -981,18 +923,26 @@ This captures what was accomplished so future sessions can recall it.`, {
981
923
  onConsolidate: async (agentId, reason) => {
982
924
  console.error(`[mcp] consolidation triggered: ${reason}`);
983
925
  const result = await consolidationEngine.consolidate(agentId);
984
- store.markConsolidation(agentId, false);
926
+ await store.markConsolidation(agentId, false);
985
927
  console.error(`[mcp] consolidation done: ${result.edgesStrengthened} strengthened, ${result.memoriesForgotten} forgotten`);
986
928
  },
987
929
  });
988
930
  // Coordination MCP tools (opt-in via AWM_COORDINATION=true)
989
- 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';
990
936
  if (coordEnabled) {
991
937
  const { initCoordinationTables } = await import('./coordination/schema.js');
992
938
  const { registerCoordinationTools } = await import('./coordination/mcp-tools.js');
993
- initCoordinationTables(store.getDb());
994
- registerCoordinationTools(server, store.getDb());
995
- 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');
996
946
  }
997
947
  else {
998
948
  console.error('AWM: coordination tools disabled (set AWM_COORDINATION=true to enable)');
@@ -1001,18 +951,23 @@ This captures what was accomplished so future sessions can recall it.`, {
1001
951
  console.error(`AgentWorkingMemory MCP server started (agent: ${AGENT_ID}, db: ${DB_PATH})`);
1002
952
  console.error(`Hook sidecar on 127.0.0.1:${HOOK_PORT}${HOOK_SECRET ? ' (auth enabled)' : ' (no auth — set AWM_HOOK_SECRET)'}`);
1003
953
  // Clean shutdown
1004
- const cleanup = () => {
954
+ const cleanup = async () => {
1005
955
  sidecar.close();
1006
956
  consolidationScheduler.stop();
1007
957
  stagingBuffer.stop();
958
+ if (BACKEND === 'sqlite') {
959
+ try {
960
+ store.walCheckpoint?.();
961
+ }
962
+ catch { /* non-fatal */ }
963
+ }
1008
964
  try {
1009
- store.walCheckpoint();
965
+ await store.close?.();
1010
966
  }
1011
- catch { /* non-fatal */ }
1012
- store.close();
967
+ catch { /* best-effort */ }
1013
968
  };
1014
- process.on('SIGINT', () => { cleanup(); process.exit(0); });
1015
- 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)); });
1016
971
  }
1017
972
  main().catch(err => {
1018
973
  console.error('MCP server failed:', err);