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.
- package/README.md +156 -15
- package/dist/adapters/claude-code.d.ts.map +1 -1
- package/dist/adapters/claude-code.js +2 -16
- package/dist/adapters/claude-code.js.map +1 -1
- package/dist/adapters/codex.d.ts.map +1 -1
- package/dist/adapters/codex.js +2 -11
- package/dist/adapters/codex.js.map +1 -1
- package/dist/adapters/common.d.ts +18 -0
- package/dist/adapters/common.d.ts.map +1 -1
- package/dist/adapters/common.js +254 -141
- package/dist/adapters/common.js.map +1 -1
- package/dist/adapters/cursor.d.ts.map +1 -1
- package/dist/adapters/cursor.js +2 -15
- package/dist/adapters/cursor.js.map +1 -1
- package/dist/adapters/http.d.ts.map +1 -1
- package/dist/adapters/http.js +28 -34
- package/dist/adapters/http.js.map +1 -1
- package/dist/api/routes.d.ts +1 -1
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +280 -113
- package/dist/api/routes.js.map +1 -1
- package/dist/cli/migrate.d.ts +42 -0
- package/dist/cli/migrate.d.ts.map +1 -0
- package/dist/cli/migrate.js +268 -0
- package/dist/cli/migrate.js.map +1 -0
- package/dist/cli.js +45 -1
- package/dist/cli.js.map +1 -1
- package/dist/coordination/circuit-breaker.d.ts +24 -0
- package/dist/coordination/circuit-breaker.d.ts.map +1 -0
- package/dist/coordination/circuit-breaker.js +70 -0
- package/dist/coordination/circuit-breaker.js.map +1 -0
- package/dist/coordination/failure-modes.d.ts +21 -0
- package/dist/coordination/failure-modes.d.ts.map +1 -0
- package/dist/coordination/failure-modes.js +49 -0
- package/dist/coordination/failure-modes.js.map +1 -0
- package/dist/coordination/peer-decisions.js +7 -7
- package/dist/coordination/routes.d.ts.map +1 -1
- package/dist/coordination/routes.js +44 -1
- package/dist/coordination/routes.js.map +1 -1
- package/dist/coordination/schema.d.ts.map +1 -1
- package/dist/coordination/schema.js +18 -0
- package/dist/coordination/schema.js.map +1 -1
- package/dist/coordination/schemas.d.ts +9 -9
- package/dist/coordination/stale.d.ts +9 -1
- package/dist/coordination/stale.d.ts.map +1 -1
- package/dist/coordination/stale.js +53 -9
- package/dist/coordination/stale.js.map +1 -1
- package/dist/core/embeddings.d.ts +16 -10
- package/dist/core/embeddings.d.ts.map +1 -1
- package/dist/core/embeddings.js +54 -38
- package/dist/core/embeddings.js.map +1 -1
- package/dist/core/ml-worker-entry.d.ts +17 -0
- package/dist/core/ml-worker-entry.d.ts.map +1 -0
- package/dist/core/ml-worker-entry.js +193 -0
- package/dist/core/ml-worker-entry.js.map +1 -0
- package/dist/core/ml-worker.d.ts +59 -0
- package/dist/core/ml-worker.d.ts.map +1 -0
- package/dist/core/ml-worker.js +253 -0
- package/dist/core/ml-worker.js.map +1 -0
- package/dist/core/query-expander.d.ts +13 -15
- package/dist/core/query-expander.d.ts.map +1 -1
- package/dist/core/query-expander.js +48 -54
- package/dist/core/query-expander.js.map +1 -1
- package/dist/core/reranker.d.ts +9 -19
- package/dist/core/reranker.d.ts.map +1 -1
- package/dist/core/reranker.js +33 -35
- package/dist/core/reranker.js.map +1 -1
- package/dist/core/salience.d.ts +32 -5
- package/dist/core/salience.d.ts.map +1 -1
- package/dist/core/salience.js +201 -28
- package/dist/core/salience.js.map +1 -1
- package/dist/core/write-pipeline.d.ts +128 -0
- package/dist/core/write-pipeline.d.ts.map +1 -0
- package/dist/core/write-pipeline.js +405 -0
- package/dist/core/write-pipeline.js.map +1 -0
- package/dist/engine/activation.d.ts +2 -2
- package/dist/engine/activation.d.ts.map +1 -1
- package/dist/engine/activation.js +238 -115
- package/dist/engine/activation.js.map +1 -1
- package/dist/engine/confidence.d.ts +62 -0
- package/dist/engine/confidence.d.ts.map +1 -0
- package/dist/engine/confidence.js +100 -0
- package/dist/engine/confidence.js.map +1 -0
- package/dist/engine/connections.d.ts +47 -8
- package/dist/engine/connections.d.ts.map +1 -1
- package/dist/engine/connections.js +83 -22
- package/dist/engine/connections.js.map +1 -1
- package/dist/engine/consolidation-scheduler.d.ts +46 -10
- package/dist/engine/consolidation-scheduler.d.ts.map +1 -1
- package/dist/engine/consolidation-scheduler.js +170 -50
- package/dist/engine/consolidation-scheduler.js.map +1 -1
- package/dist/engine/consolidation.d.ts +16 -2
- package/dist/engine/consolidation.d.ts.map +1 -1
- package/dist/engine/consolidation.js +143 -44
- package/dist/engine/consolidation.js.map +1 -1
- package/dist/engine/eval.d.ts +2 -2
- package/dist/engine/eval.d.ts.map +1 -1
- package/dist/engine/eval.js +9 -9
- package/dist/engine/eval.js.map +1 -1
- package/dist/engine/eviction.d.ts +4 -4
- package/dist/engine/eviction.d.ts.map +1 -1
- package/dist/engine/eviction.js +15 -15
- package/dist/engine/eviction.js.map +1 -1
- package/dist/engine/retraction.d.ts +93 -5
- package/dist/engine/retraction.d.ts.map +1 -1
- package/dist/engine/retraction.js +238 -19
- package/dist/engine/retraction.js.map +1 -1
- package/dist/engine/staging.d.ts +1 -1
- package/dist/engine/staging.d.ts.map +1 -1
- package/dist/engine/staging.js +3 -3
- package/dist/engine/staging.js.map +1 -1
- package/dist/hooks/sidecar.d.ts +1 -1
- package/dist/hooks/sidecar.d.ts.map +1 -1
- package/dist/hooks/sidecar.js +4 -4
- package/dist/hooks/sidecar.js.map +1 -1
- package/dist/index.js +48 -31
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +110 -155
- package/dist/mcp.js.map +1 -1
- package/dist/storage/factory.d.ts +55 -0
- package/dist/storage/factory.d.ts.map +1 -0
- package/dist/storage/factory.js +135 -0
- package/dist/storage/factory.js.map +1 -0
- package/dist/storage/pglite-schema.d.ts +20 -0
- package/dist/storage/pglite-schema.d.ts.map +1 -0
- package/dist/storage/pglite-schema.js +165 -0
- package/dist/storage/pglite-schema.js.map +1 -0
- package/dist/storage/pglite.d.ts +187 -0
- package/dist/storage/pglite.d.ts.map +1 -0
- package/dist/storage/pglite.js +1114 -0
- package/dist/storage/pglite.js.map +1 -0
- package/dist/storage/sqlite.d.ts +117 -0
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +357 -8
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/storage/store.d.ts +63 -0
- package/dist/storage/store.d.ts.map +1 -0
- package/dist/storage/store.js +27 -0
- package/dist/storage/store.js.map +1 -0
- package/dist/types/engram.d.ts +92 -2
- package/dist/types/engram.d.ts.map +1 -1
- package/dist/types/engram.js.map +1 -1
- package/package.json +3 -1
- package/src/adapters/claude-code.ts +218 -234
- package/src/adapters/codex.ts +252 -262
- package/src/adapters/common.ts +411 -284
- package/src/adapters/cursor.ts +135 -150
- package/src/adapters/http.ts +93 -100
- package/src/adapters/index.ts +31 -31
- package/src/adapters/types.ts +75 -75
- package/src/api/index.ts +3 -3
- package/src/api/routes.ts +367 -120
- package/src/cli/migrate.ts +307 -0
- package/src/cli.ts +37 -1
- package/src/coordination/circuit-breaker.ts +83 -0
- package/src/coordination/events.ts +90 -90
- package/src/coordination/failure-modes.ts +50 -0
- package/src/coordination/peer-decisions.ts +105 -105
- package/src/coordination/plugin-loader.ts +60 -60
- package/src/coordination/plugin.ts +44 -44
- package/src/coordination/routes.ts +56 -1
- package/src/coordination/schema.ts +13 -0
- package/src/coordination/stale.ts +80 -11
- package/src/coordination/types.ts +311 -311
- package/src/coordination/write-mutex.ts +69 -69
- package/src/core/auto-tagger.ts +168 -168
- package/src/core/decay.ts +63 -63
- package/src/core/embeddings.ts +110 -93
- package/src/core/index.ts +5 -5
- package/src/core/logger.ts +36 -36
- package/src/core/ml-worker-entry.ts +194 -0
- package/src/core/ml-worker.ts +281 -0
- package/src/core/query-expander.ts +122 -128
- package/src/core/reranker.ts +119 -125
- package/src/core/salience.ts +200 -33
- package/src/core/write-pipeline.ts +521 -0
- package/src/engine/activation.ts +235 -109
- package/src/engine/confidence.ts +120 -0
- package/src/engine/connections.ts +162 -103
- package/src/engine/consolidation-scheduler.ts +242 -125
- package/src/engine/consolidation.ts +138 -45
- package/src/engine/eval.ts +102 -102
- package/src/engine/eviction.ts +101 -101
- package/src/engine/index.ts +8 -8
- package/src/engine/retraction.ts +366 -116
- package/src/engine/staging.ts +74 -74
- package/src/hooks/sidecar.ts +5 -5
- package/src/index.ts +42 -28
- package/src/mcp.ts +105 -152
- package/src/storage/factory.ts +147 -0
- package/src/storage/index.ts +3 -3
- package/src/storage/pglite-schema.ts +166 -0
- package/src/storage/pglite.ts +1363 -0
- package/src/storage/sqlite.ts +402 -7
- package/src/storage/store.ts +80 -0
- package/src/types/agent.ts +67 -67
- package/src/types/checkpoint.ts +46 -46
- package/src/types/engram.ts +101 -3
- package/src/types/eval.ts +100 -100
- 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 {
|
|
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
|
|
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.
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
|
289
|
-
|
|
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:
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
994
|
-
|
|
995
|
-
|
|
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.
|
|
965
|
+
await store.close?.();
|
|
1010
966
|
}
|
|
1011
|
-
catch { /*
|
|
1012
|
-
store.close();
|
|
967
|
+
catch { /* best-effort */ }
|
|
1013
968
|
};
|
|
1014
|
-
process.on('SIGINT', () => { cleanup()
|
|
1015
|
-
process.on('SIGTERM', () => { cleanup()
|
|
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);
|