agent-working-memory 0.5.5 → 0.6.0
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 +428 -399
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +60 -5
- package/dist/api/routes.js.map +1 -1
- package/dist/cli.js +468 -68
- package/dist/cli.js.map +1 -1
- package/dist/coordination/index.d.ts +11 -0
- package/dist/coordination/index.d.ts.map +1 -0
- package/dist/coordination/index.js +39 -0
- package/dist/coordination/index.js.map +1 -0
- package/dist/coordination/mcp-tools.d.ts +8 -0
- package/dist/coordination/mcp-tools.d.ts.map +1 -0
- package/dist/coordination/mcp-tools.js +221 -0
- package/dist/coordination/mcp-tools.js.map +1 -0
- package/dist/coordination/routes.d.ts +9 -0
- package/dist/coordination/routes.d.ts.map +1 -0
- package/dist/coordination/routes.js +573 -0
- package/dist/coordination/routes.js.map +1 -0
- package/dist/coordination/schema.d.ts +12 -0
- package/dist/coordination/schema.d.ts.map +1 -0
- package/dist/coordination/schema.js +125 -0
- package/dist/coordination/schema.js.map +1 -0
- package/dist/coordination/schemas.d.ts +227 -0
- package/dist/coordination/schemas.d.ts.map +1 -0
- package/dist/coordination/schemas.js +125 -0
- package/dist/coordination/schemas.js.map +1 -0
- package/dist/coordination/stale.d.ts +27 -0
- package/dist/coordination/stale.d.ts.map +1 -0
- package/dist/coordination/stale.js +58 -0
- package/dist/coordination/stale.js.map +1 -0
- package/dist/engine/activation.d.ts.map +1 -1
- package/dist/engine/activation.js +119 -23
- package/dist/engine/activation.js.map +1 -1
- package/dist/engine/consolidation.d.ts.map +1 -1
- package/dist/engine/consolidation.js +27 -6
- package/dist/engine/consolidation.js.map +1 -1
- package/dist/index.js +100 -4
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +149 -80
- package/dist/mcp.js.map +1 -1
- package/dist/storage/sqlite.d.ts +21 -0
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +331 -282
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/types/engram.d.ts +24 -0
- package/dist/types/engram.d.ts.map +1 -1
- package/dist/types/engram.js.map +1 -1
- package/package.json +57 -55
- package/src/api/index.ts +3 -3
- package/src/api/routes.ts +600 -536
- package/src/cli.ts +850 -397
- package/src/coordination/index.ts +47 -0
- package/src/coordination/mcp-tools.ts +318 -0
- package/src/coordination/routes.ts +846 -0
- package/src/coordination/schema.ts +120 -0
- package/src/coordination/schemas.ts +155 -0
- package/src/coordination/stale.ts +97 -0
- package/src/core/decay.ts +63 -63
- package/src/core/embeddings.ts +88 -88
- package/src/core/hebbian.ts +93 -93
- package/src/core/index.ts +5 -5
- package/src/core/logger.ts +36 -36
- package/src/core/query-expander.ts +66 -66
- package/src/core/reranker.ts +101 -101
- package/src/engine/activation.ts +758 -656
- package/src/engine/connections.ts +103 -103
- package/src/engine/consolidation-scheduler.ts +125 -125
- package/src/engine/consolidation.ts +29 -6
- 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 +100 -100
- package/src/engine/staging.ts +74 -74
- package/src/index.ts +208 -121
- package/src/mcp.ts +1093 -1013
- package/src/storage/index.ts +3 -3
- package/src/storage/sqlite.ts +1017 -963
- package/src/types/agent.ts +67 -67
- package/src/types/checkpoint.ts +46 -46
- package/src/types/engram.ts +245 -217
- package/src/types/eval.ts +100 -100
- package/src/types/index.ts +6 -6
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Copyright 2026 Robert Winter / Complete Ideas
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
import { readFileSync, copyFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
3
|
+
import { readFileSync, copyFileSync, existsSync, mkdirSync, readdirSync, unlinkSync } from 'node:fs';
|
|
4
4
|
import { resolve, dirname, basename } from 'node:path';
|
|
5
5
|
import Fastify from 'fastify';
|
|
6
6
|
// Load .env file if present (no external dependency)
|
|
@@ -59,6 +59,37 @@ async function main() {
|
|
|
59
59
|
initLogger(DB_PATH);
|
|
60
60
|
// Storage
|
|
61
61
|
const store = new EngramStore(DB_PATH);
|
|
62
|
+
// Integrity check
|
|
63
|
+
const integrity = store.integrityCheck();
|
|
64
|
+
if (!integrity.ok) {
|
|
65
|
+
console.error(`DB integrity check FAILED: ${integrity.result}`);
|
|
66
|
+
// Close corrupt DB, restore from backup, and exit for process manager to restart
|
|
67
|
+
store.close();
|
|
68
|
+
const dbDir = dirname(resolve(DB_PATH));
|
|
69
|
+
const backupDir = resolve(dbDir, 'backups');
|
|
70
|
+
if (existsSync(backupDir)) {
|
|
71
|
+
const backups = readdirSync(backupDir)
|
|
72
|
+
.filter(f => f.endsWith('.db'))
|
|
73
|
+
.sort()
|
|
74
|
+
.reverse();
|
|
75
|
+
if (backups.length > 0) {
|
|
76
|
+
const restorePath = resolve(backupDir, backups[0]);
|
|
77
|
+
console.error(`Attempting restore from: ${restorePath}`);
|
|
78
|
+
try {
|
|
79
|
+
copyFileSync(restorePath, resolve(DB_PATH));
|
|
80
|
+
console.error('Restore complete — exiting for restart with restored DB');
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
catch (restoreErr) {
|
|
84
|
+
console.error(`Restore failed: ${restoreErr.message}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
console.error('No backup available — continuing with potentially corrupt DB');
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
console.log(' DB integrity check: ok');
|
|
92
|
+
}
|
|
62
93
|
// Engines
|
|
63
94
|
const activationEngine = new ActivationEngine(store);
|
|
64
95
|
const connectionEngine = new ConnectionEngine(store, activationEngine);
|
|
@@ -68,8 +99,8 @@ async function main() {
|
|
|
68
99
|
const evalEngine = new EvalEngine(store);
|
|
69
100
|
const consolidationEngine = new ConsolidationEngine(store);
|
|
70
101
|
const consolidationScheduler = new ConsolidationScheduler(store, consolidationEngine);
|
|
71
|
-
// API
|
|
72
|
-
const app = Fastify({ logger:
|
|
102
|
+
// API — disable Fastify's default request logging (too noisy for hive polling)
|
|
103
|
+
const app = Fastify({ logger: false });
|
|
73
104
|
// Bearer token auth — only enforced when AWM_API_KEY is explicitly set and non-empty
|
|
74
105
|
if (API_KEY && API_KEY !== 'NONE' && API_KEY.length > 1) {
|
|
75
106
|
app.addHook('onRequest', async (req, reply) => {
|
|
@@ -88,20 +119,85 @@ async function main() {
|
|
|
88
119
|
evictionEngine, retractionEngine, evalEngine,
|
|
89
120
|
consolidationEngine, consolidationScheduler,
|
|
90
121
|
});
|
|
122
|
+
// Coordination module (opt-in via AWM_COORDINATION=true)
|
|
123
|
+
let heartbeatPruneTimer = null;
|
|
124
|
+
const { isCoordinationEnabled, initCoordination } = await import('./coordination/index.js');
|
|
125
|
+
if (isCoordinationEnabled()) {
|
|
126
|
+
initCoordination(app, store.getDb());
|
|
127
|
+
// Prune stale heartbeat events every 30s (keeps assignment/command events permanently)
|
|
128
|
+
// Purge dead agents older than 24h every 30s to prevent table bloat
|
|
129
|
+
const { pruneOldHeartbeats, purgeDeadAgents } = await import('./coordination/stale.js');
|
|
130
|
+
heartbeatPruneTimer = setInterval(() => {
|
|
131
|
+
const pruned = pruneOldHeartbeats(store.getDb());
|
|
132
|
+
if (pruned > 0)
|
|
133
|
+
console.log(`[coordination] pruned ${pruned} old heartbeat event(s)`);
|
|
134
|
+
const purged = purgeDeadAgents(store.getDb());
|
|
135
|
+
if (purged > 0)
|
|
136
|
+
console.log(`[coordination] purged ${purged} dead agent(s) older than 24h`);
|
|
137
|
+
}, 30_000);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
console.log(' Coordination module disabled (set AWM_COORDINATION=true to enable)');
|
|
141
|
+
}
|
|
91
142
|
// Background tasks
|
|
92
143
|
stagingBuffer.start(DEFAULT_AGENT_CONFIG.stagingTtlMs);
|
|
93
144
|
consolidationScheduler.start();
|
|
145
|
+
// Periodic hot backup every 10 minutes (keep last 6 = 1hr coverage)
|
|
146
|
+
const dbDir = dirname(resolve(DB_PATH));
|
|
147
|
+
const backupDir = resolve(dbDir, 'backups');
|
|
148
|
+
mkdirSync(backupDir, { recursive: true });
|
|
149
|
+
// Cleanup old backups on startup (older than 2 hours)
|
|
150
|
+
try {
|
|
151
|
+
const TWO_HOURS_MS = 2 * 60 * 60 * 1000;
|
|
152
|
+
const now = Date.now();
|
|
153
|
+
for (const f of readdirSync(backupDir).filter(f => f.endsWith('.db'))) {
|
|
154
|
+
const match = f.match(/(\d{4})-(\d{2})-(\d{2})T(\d{2})-(\d{2})-(\d{2})/);
|
|
155
|
+
if (match) {
|
|
156
|
+
const fileDate = new Date(`${match[1]}-${match[2]}-${match[3]}T${match[4]}:${match[5]}:${match[6]}Z`);
|
|
157
|
+
if (now - fileDate.getTime() > TWO_HOURS_MS) {
|
|
158
|
+
unlinkSync(resolve(backupDir, f));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch { /* cleanup is non-fatal */ }
|
|
164
|
+
const backupTimer = setInterval(() => {
|
|
165
|
+
try {
|
|
166
|
+
const ts = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
167
|
+
const backupPath = resolve(backupDir, `${basename(DB_PATH, '.db')}-${ts}.db`);
|
|
168
|
+
store.backup(backupPath);
|
|
169
|
+
// Prune: keep only last 6 backups
|
|
170
|
+
const backups = readdirSync(backupDir).filter(f => f.endsWith('.db')).sort();
|
|
171
|
+
while (backups.length > 6) {
|
|
172
|
+
const old = backups.shift();
|
|
173
|
+
try {
|
|
174
|
+
unlinkSync(resolve(backupDir, old));
|
|
175
|
+
}
|
|
176
|
+
catch { /* non-fatal */ }
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
console.warn(`[backup] failed: ${err.message}`);
|
|
181
|
+
}
|
|
182
|
+
}, 10 * 60_000); // 10 minutes
|
|
94
183
|
// Pre-load ML models (downloads on first run: embeddings ~22MB, reranker ~22MB, expander ~80MB)
|
|
95
184
|
getEmbedder().catch(err => console.warn('Embedding model unavailable:', err.message));
|
|
96
185
|
getReranker().catch(err => console.warn('Reranker model unavailable:', err.message));
|
|
97
186
|
getExpander().catch(err => console.warn('Query expander model unavailable:', err.message));
|
|
98
187
|
// Start server
|
|
99
188
|
await app.listen({ port: PORT, host: '0.0.0.0' });
|
|
100
|
-
console.log(`AgentWorkingMemory v0.
|
|
189
|
+
console.log(`AgentWorkingMemory v0.6.0 listening on port ${PORT}`);
|
|
101
190
|
// Graceful shutdown
|
|
102
191
|
const shutdown = () => {
|
|
192
|
+
clearInterval(backupTimer);
|
|
193
|
+
if (heartbeatPruneTimer)
|
|
194
|
+
clearInterval(heartbeatPruneTimer);
|
|
103
195
|
consolidationScheduler.stop();
|
|
104
196
|
stagingBuffer.stop();
|
|
197
|
+
try {
|
|
198
|
+
store.walCheckpoint();
|
|
199
|
+
}
|
|
200
|
+
catch { /* non-fatal */ }
|
|
105
201
|
store.close();
|
|
106
202
|
process.exit(0);
|
|
107
203
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,sCAAsC;AACtC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp.js
CHANGED
|
@@ -66,7 +66,7 @@ import { initLogger, log, getLogPath } from './core/logger.js';
|
|
|
66
66
|
const INCOGNITO = process.env.AWM_INCOGNITO === '1' || process.env.AWM_INCOGNITO === 'true';
|
|
67
67
|
if (INCOGNITO) {
|
|
68
68
|
console.error('AWM: incognito mode — all memory tools disabled, nothing will be recorded');
|
|
69
|
-
const server = new McpServer({ name: 'agent-working-memory', version: '0.
|
|
69
|
+
const server = new McpServer({ name: 'agent-working-memory', version: '0.6.0' });
|
|
70
70
|
const transport = new StdioServerTransport();
|
|
71
71
|
server.connect(transport).catch(err => {
|
|
72
72
|
console.error('MCP server failed:', err);
|
|
@@ -93,20 +93,39 @@ else {
|
|
|
93
93
|
const consolidationScheduler = new ConsolidationScheduler(store, consolidationEngine);
|
|
94
94
|
stagingBuffer.start(DEFAULT_AGENT_CONFIG.stagingTtlMs);
|
|
95
95
|
consolidationScheduler.start();
|
|
96
|
+
// Coordination DB handle — set when AWM_COORDINATION=true, used by memory_write for decision propagation
|
|
97
|
+
let coordDb = null;
|
|
96
98
|
const server = new McpServer({
|
|
97
99
|
name: 'agent-working-memory',
|
|
98
|
-
version: '0.
|
|
100
|
+
version: '0.6.0',
|
|
99
101
|
});
|
|
102
|
+
// --- Auto-classification for memory types ---
|
|
103
|
+
function classifyMemoryType(content) {
|
|
104
|
+
const lower = content.toLowerCase();
|
|
105
|
+
// Procedural: how-to, steps, numbered lists
|
|
106
|
+
if (/\bhow to\b|\bsteps?:/i.test(content) || /^\s*\d+[\.\)]\s/m.test(content) || /\bthen run\b|\bfirst,?\s/i.test(content)) {
|
|
107
|
+
return 'procedural';
|
|
108
|
+
}
|
|
109
|
+
// Episodic: past tense events, incidents, specific time references
|
|
110
|
+
if (/\b(discovered|debugged|fixed|encountered|happened|resolved|found that|we did|i did|yesterday|last week|today)\b/i.test(content)) {
|
|
111
|
+
return 'episodic';
|
|
112
|
+
}
|
|
113
|
+
// Semantic: facts, decisions, rules, patterns
|
|
114
|
+
if (/\b(is|are|should|always|never|must|uses?|requires?|means|pattern|decision|rule|convention)\b/i.test(content) && content.length < 500) {
|
|
115
|
+
return 'semantic';
|
|
116
|
+
}
|
|
117
|
+
return 'unclassified';
|
|
118
|
+
}
|
|
100
119
|
// --- Tools ---
|
|
101
|
-
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.
|
|
102
|
-
|
|
103
|
-
CALL THIS PROACTIVELY — do not wait to be asked. Write memories when you:
|
|
104
|
-
- Discover something about the codebase, bugs, or architecture
|
|
105
|
-
- Make a decision and want to remember why
|
|
106
|
-
- Encounter and resolve an error
|
|
107
|
-
- Learn a user preference or project pattern
|
|
108
|
-
- Complete a significant piece of work
|
|
109
|
-
|
|
120
|
+
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.
|
|
121
|
+
|
|
122
|
+
CALL THIS PROACTIVELY — do not wait to be asked. Write memories when you:
|
|
123
|
+
- Discover something about the codebase, bugs, or architecture
|
|
124
|
+
- Make a decision and want to remember why
|
|
125
|
+
- Encounter and resolve an error
|
|
126
|
+
- Learn a user preference or project pattern
|
|
127
|
+
- Complete a significant piece of work
|
|
128
|
+
|
|
110
129
|
The concept should be a short label (3-8 words). The content should be the full detail.`, {
|
|
111
130
|
concept: z.string().describe('Short label for this memory (3-8 words)'),
|
|
112
131
|
content: z.string().describe('Full detail of what was learned'),
|
|
@@ -124,6 +143,8 @@ The concept should be a short label (3-8 words). The content should be the full
|
|
|
124
143
|
.describe('How much effort to resolve? 0=trivial, 1=significant debugging'),
|
|
125
144
|
memory_class: z.enum(['canonical', 'working', 'ephemeral']).optional().default('working')
|
|
126
145
|
.describe('Memory class: canonical (source-of-truth, never stages), working (default), ephemeral (temporary, decays faster)'),
|
|
146
|
+
memory_type: z.enum(['episodic', 'semantic', 'procedural', 'unclassified']).optional()
|
|
147
|
+
.describe('Memory type: episodic (events/incidents), semantic (facts/decisions), procedural (how-to/steps). Auto-classified if omitted.'),
|
|
127
148
|
supersedes: z.string().optional()
|
|
128
149
|
.describe('ID of an older memory this one replaces. The old memory is down-ranked, not deleted.'),
|
|
129
150
|
}, async (params) => {
|
|
@@ -190,6 +211,7 @@ The concept should be a short label (3-8 words). The content should be the full
|
|
|
190
211
|
: salience.disposition === 'staging'
|
|
191
212
|
? 0.40
|
|
192
213
|
: CONFIDENCE_PRIORS[params.event_type ?? 'observation'] ?? 0.45;
|
|
214
|
+
const memoryType = params.memory_type ?? classifyMemoryType(params.content);
|
|
193
215
|
const engram = store.createEngram({
|
|
194
216
|
agentId: AGENT_ID,
|
|
195
217
|
concept: params.concept,
|
|
@@ -201,6 +223,7 @@ The concept should be a short label (3-8 words). The content should be the full
|
|
|
201
223
|
reasonCodes: salience.reasonCodes,
|
|
202
224
|
ttl: salience.disposition === 'staging' ? DEFAULT_AGENT_CONFIG.stagingTtlMs : undefined,
|
|
203
225
|
memoryClass: params.memory_class,
|
|
226
|
+
memoryType,
|
|
204
227
|
supersedes: params.supersedes,
|
|
205
228
|
});
|
|
206
229
|
if (salience.disposition === 'staging') {
|
|
@@ -227,24 +250,35 @@ The concept should be a short label (3-8 words). The content should be the full
|
|
|
227
250
|
store.updateAutoCheckpointWrite(AGENT_ID, engram.id);
|
|
228
251
|
}
|
|
229
252
|
catch { /* non-fatal */ }
|
|
253
|
+
// Decision propagation: when decision_made=true and coordination is enabled,
|
|
254
|
+
// broadcast to coord_decisions so other agents can discover it
|
|
255
|
+
if (params.decision_made && coordDb) {
|
|
256
|
+
try {
|
|
257
|
+
const agent = coordDb.prepare(`SELECT id, current_task FROM coord_agents WHERE name = ? AND status != 'dead' ORDER BY last_seen DESC LIMIT 1`).get(AGENT_ID);
|
|
258
|
+
if (agent) {
|
|
259
|
+
coordDb.prepare(`INSERT INTO coord_decisions (author_id, assignment_id, tags, summary) VALUES (?, ?, ?, ?)`).run(agent.id, agent.current_task, params.tags ? JSON.stringify(params.tags) : null, params.concept);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
catch { /* decision propagation is non-fatal */ }
|
|
263
|
+
}
|
|
230
264
|
const logDisposition = isLowSalience ? 'low-salience' : salience.disposition;
|
|
231
265
|
log(AGENT_ID, `write:${logDisposition}`, `"${params.concept}" salience=${salience.score.toFixed(2)} novelty=${novelty.toFixed(1)} id=${engram.id}`);
|
|
232
266
|
return {
|
|
233
267
|
content: [{
|
|
234
268
|
type: 'text',
|
|
235
|
-
text: `Stored (${salience.disposition}) "${params.concept}" [${salience.score.toFixed(2)}]`,
|
|
269
|
+
text: `Stored (${salience.disposition}) "${params.concept}" [${salience.score.toFixed(2)}]\nID: ${engram.id}`,
|
|
236
270
|
}],
|
|
237
271
|
};
|
|
238
272
|
});
|
|
239
|
-
server.tool('memory_recall', `Recall memories relevant to a query. Uses cognitive activation — not keyword search.
|
|
240
|
-
|
|
241
|
-
ALWAYS call this when:
|
|
242
|
-
- Starting work on a project or topic (recall what you know)
|
|
243
|
-
- Debugging (recall similar errors and solutions)
|
|
244
|
-
- Making decisions (recall past decisions and outcomes)
|
|
245
|
-
- The user mentions a topic you might have stored memories about
|
|
246
|
-
|
|
247
|
-
Accepts either "query" or "context" parameter — both work identically.
|
|
273
|
+
server.tool('memory_recall', `Recall memories relevant to a query. Uses cognitive activation — not keyword search.
|
|
274
|
+
|
|
275
|
+
ALWAYS call this when:
|
|
276
|
+
- Starting work on a project or topic (recall what you know)
|
|
277
|
+
- Debugging (recall similar errors and solutions)
|
|
278
|
+
- Making decisions (recall past decisions and outcomes)
|
|
279
|
+
- The user mentions a topic you might have stored memories about
|
|
280
|
+
|
|
281
|
+
Accepts either "query" or "context" parameter — both work identically.
|
|
248
282
|
Returns the most relevant memories ranked by text relevance, temporal recency, and associative strength.`, {
|
|
249
283
|
query: z.string().optional().describe('What to search for — describe the situation, question, or topic'),
|
|
250
284
|
context: z.string().optional().describe('Alias for query (either works)'),
|
|
@@ -253,6 +287,7 @@ Returns the most relevant memories ranked by text relevance, temporal recency, a
|
|
|
253
287
|
include_staging: z.boolean().optional().default(false).describe('Include weak/unconfirmed memories?'),
|
|
254
288
|
use_reranker: z.boolean().optional().default(true).describe('Use cross-encoder re-ranking for better relevance (default true)'),
|
|
255
289
|
use_expansion: z.boolean().optional().default(true).describe('Expand query with synonyms for better recall (default true)'),
|
|
290
|
+
memory_type: z.enum(['episodic', 'semantic', 'procedural']).optional().describe('Filter by memory type (omit to search all types)'),
|
|
256
291
|
}, async (params) => {
|
|
257
292
|
const queryText = params.query ?? params.context;
|
|
258
293
|
if (!queryText) {
|
|
@@ -271,6 +306,7 @@ Returns the most relevant memories ranked by text relevance, temporal recency, a
|
|
|
271
306
|
includeStaging: params.include_staging,
|
|
272
307
|
useReranker: params.use_reranker,
|
|
273
308
|
useExpansion: params.use_expansion,
|
|
309
|
+
memoryType: params.memory_type,
|
|
274
310
|
});
|
|
275
311
|
// Auto-checkpoint: track recall
|
|
276
312
|
try {
|
|
@@ -297,8 +333,8 @@ Returns the most relevant memories ranked by text relevance, temporal recency, a
|
|
|
297
333
|
}],
|
|
298
334
|
};
|
|
299
335
|
});
|
|
300
|
-
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.
|
|
301
|
-
|
|
336
|
+
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.
|
|
337
|
+
|
|
302
338
|
Always call this after using a recalled memory so the system learns what's valuable.`, {
|
|
303
339
|
engram_id: z.string().describe('ID of the memory (from memory_recall results)'),
|
|
304
340
|
useful: z.boolean().describe('Was this memory actually helpful?'),
|
|
@@ -319,8 +355,8 @@ Always call this after using a recalled memory so the system learns what's valua
|
|
|
319
355
|
}],
|
|
320
356
|
};
|
|
321
357
|
});
|
|
322
|
-
server.tool('memory_retract', `Retract a memory that turned out to be wrong. Creates a correction and reduces confidence of related memories.
|
|
323
|
-
|
|
358
|
+
server.tool('memory_retract', `Retract a memory that turned out to be wrong. Creates a correction and reduces confidence of related memories.
|
|
359
|
+
|
|
324
360
|
Use this when you discover a memory contains incorrect information.`, {
|
|
325
361
|
engram_id: z.string().describe('ID of the wrong memory'),
|
|
326
362
|
reason: z.string().describe('Why is this memory wrong?'),
|
|
@@ -344,13 +380,13 @@ Use this when you discover a memory contains incorrect information.`, {
|
|
|
344
380
|
}],
|
|
345
381
|
};
|
|
346
382
|
});
|
|
347
|
-
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.
|
|
348
|
-
|
|
349
|
-
Use this when:
|
|
350
|
-
- A status or count has changed (e.g., "5 reviews done" → "7 reviews done")
|
|
351
|
-
- Architecture or infrastructure evolved (e.g., "two-repo model" → "three-repo model")
|
|
352
|
-
- A schedule or plan was updated
|
|
353
|
-
|
|
383
|
+
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.
|
|
384
|
+
|
|
385
|
+
Use this when:
|
|
386
|
+
- A status or count has changed (e.g., "5 reviews done" → "7 reviews done")
|
|
387
|
+
- Architecture or infrastructure evolved (e.g., "two-repo model" → "three-repo model")
|
|
388
|
+
- A schedule or plan was updated
|
|
389
|
+
|
|
354
390
|
The old memory stays in the database (searchable for history) but is heavily down-ranked in recall so the current version dominates.`, {
|
|
355
391
|
old_engram_id: z.string().describe('ID of the outdated memory'),
|
|
356
392
|
new_engram_id: z.string().describe('ID of the replacement memory'),
|
|
@@ -377,7 +413,7 @@ The old memory stays in the database (searchable for history) but is heavily dow
|
|
|
377
413
|
}],
|
|
378
414
|
};
|
|
379
415
|
});
|
|
380
|
-
server.tool('memory_stats', `Get memory health stats — how many memories, confidence levels, association count, and system performance.
|
|
416
|
+
server.tool('memory_stats', `Get memory health stats — how many memories, confidence levels, association count, and system performance.
|
|
381
417
|
Also shows the activity log path so the user can tail it to see what's happening.`, {}, async () => {
|
|
382
418
|
const metrics = evalEngine.computeMetrics(AGENT_ID);
|
|
383
419
|
const checkpoint = store.getCheckpoint(AGENT_ID);
|
|
@@ -408,13 +444,13 @@ Also shows the activity log path so the user can tail it to see what's happening
|
|
|
408
444
|
};
|
|
409
445
|
});
|
|
410
446
|
// --- Checkpointing Tools ---
|
|
411
|
-
server.tool('memory_checkpoint', `Save your current execution state so you can recover after context compaction.
|
|
412
|
-
|
|
413
|
-
ALWAYS call this before:
|
|
414
|
-
- Long operations (multi-file generation, large refactors, overnight work)
|
|
415
|
-
- Anything that might fill the context window
|
|
416
|
-
- Switching to a different task
|
|
417
|
-
|
|
447
|
+
server.tool('memory_checkpoint', `Save your current execution state so you can recover after context compaction.
|
|
448
|
+
|
|
449
|
+
ALWAYS call this before:
|
|
450
|
+
- Long operations (multi-file generation, large refactors, overnight work)
|
|
451
|
+
- Anything that might fill the context window
|
|
452
|
+
- Switching to a different task
|
|
453
|
+
|
|
418
454
|
Also call periodically during long sessions to avoid losing state. The state is saved per-agent and overwrites any previous checkpoint.`, {
|
|
419
455
|
current_task: z.string().describe('What you are currently working on'),
|
|
420
456
|
decisions: z.array(z.string()).optional().default([])
|
|
@@ -448,14 +484,14 @@ Also call periodically during long sessions to avoid losing state. The state is
|
|
|
448
484
|
}],
|
|
449
485
|
};
|
|
450
486
|
});
|
|
451
|
-
server.tool('memory_restore', `Restore your previous execution state after context compaction or at session start.
|
|
452
|
-
|
|
453
|
-
Returns:
|
|
454
|
-
- Your saved execution state (task, decisions, next steps, files)
|
|
455
|
-
- Recently recalled memories for context
|
|
456
|
-
- Your last write for continuity
|
|
457
|
-
- How long you were idle
|
|
458
|
-
|
|
487
|
+
server.tool('memory_restore', `Restore your previous execution state after context compaction or at session start.
|
|
488
|
+
|
|
489
|
+
Returns:
|
|
490
|
+
- Your saved execution state (task, decisions, next steps, files)
|
|
491
|
+
- Recently recalled memories for context
|
|
492
|
+
- Your last write for continuity
|
|
493
|
+
- How long you were idle
|
|
494
|
+
|
|
459
495
|
Use this at the start of every session or after compaction to pick up where you left off.`, {}, async () => {
|
|
460
496
|
const checkpoint = store.getCheckpoint(AGENT_ID);
|
|
461
497
|
const now = Date.now();
|
|
@@ -558,6 +594,23 @@ Use this at the start of every session or after compaction to pick up where you
|
|
|
558
594
|
parts.push(`- **${m.concept}** (${m.score.toFixed(3)}): ${m.content.slice(0, 150)}${m.content.length > 150 ? '...' : ''}`);
|
|
559
595
|
}
|
|
560
596
|
}
|
|
597
|
+
// Peer decisions: show recent decisions from other agents (last 30 min)
|
|
598
|
+
if (coordDb) {
|
|
599
|
+
try {
|
|
600
|
+
const myAgent = coordDb.prepare(`SELECT id FROM coord_agents WHERE name = ? AND status != 'dead' ORDER BY last_seen DESC LIMIT 1`).get(AGENT_ID);
|
|
601
|
+
const peerDecisions = coordDb.prepare(`SELECT d.summary, a.name AS author_name, d.created_at
|
|
602
|
+
FROM coord_decisions d JOIN coord_agents a ON d.author_id = a.id
|
|
603
|
+
WHERE d.author_id != ? AND d.created_at > datetime('now', '-30 minutes')
|
|
604
|
+
ORDER BY d.created_at DESC LIMIT 10`).all(myAgent?.id ?? '');
|
|
605
|
+
if (peerDecisions.length > 0) {
|
|
606
|
+
parts.push(`\n**Peer decisions (last 30 min):**`);
|
|
607
|
+
for (const d of peerDecisions) {
|
|
608
|
+
parts.push(`- [${d.author_name}] ${d.summary} (${d.created_at})`);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
catch { /* peer decisions are non-fatal */ }
|
|
613
|
+
}
|
|
561
614
|
return {
|
|
562
615
|
content: [{
|
|
563
616
|
type: 'text',
|
|
@@ -566,13 +619,13 @@ Use this at the start of every session or after compaction to pick up where you
|
|
|
566
619
|
};
|
|
567
620
|
});
|
|
568
621
|
// --- Task Management Tools ---
|
|
569
|
-
server.tool('memory_task_add', `Create a task that you need to come back to. Tasks are memories with status and priority tracking.
|
|
570
|
-
|
|
571
|
-
Use this when:
|
|
572
|
-
- You identify work that needs doing but can't do it right now
|
|
573
|
-
- The user mentions something to do later
|
|
574
|
-
- You want to park a sub-task while focusing on something more urgent
|
|
575
|
-
|
|
622
|
+
server.tool('memory_task_add', `Create a task that you need to come back to. Tasks are memories with status and priority tracking.
|
|
623
|
+
|
|
624
|
+
Use this when:
|
|
625
|
+
- You identify work that needs doing but can't do it right now
|
|
626
|
+
- The user mentions something to do later
|
|
627
|
+
- You want to park a sub-task while focusing on something more urgent
|
|
628
|
+
|
|
576
629
|
Tasks automatically get high salience so they won't be discarded.`, {
|
|
577
630
|
concept: z.string().describe('Short task title (3-10 words)'),
|
|
578
631
|
content: z.string().describe('Full task description — what needs doing, context, acceptance criteria'),
|
|
@@ -612,11 +665,11 @@ Tasks automatically get high salience so they won't be discarded.`, {
|
|
|
612
665
|
}],
|
|
613
666
|
};
|
|
614
667
|
});
|
|
615
|
-
server.tool('memory_task_update', `Update a task's status or priority. Use this to:
|
|
616
|
-
- Start working on a task (open → in_progress)
|
|
617
|
-
- Mark a task done (→ done)
|
|
618
|
-
- Block a task on another (→ blocked)
|
|
619
|
-
- Reprioritize (change priority)
|
|
668
|
+
server.tool('memory_task_update', `Update a task's status or priority. Use this to:
|
|
669
|
+
- Start working on a task (open → in_progress)
|
|
670
|
+
- Mark a task done (→ done)
|
|
671
|
+
- Block a task on another (→ blocked)
|
|
672
|
+
- Reprioritize (change priority)
|
|
620
673
|
- Unblock a task (clear blocked_by)`, {
|
|
621
674
|
task_id: z.string().describe('ID of the task to update'),
|
|
622
675
|
status: z.enum(['open', 'in_progress', 'blocked', 'done']).optional()
|
|
@@ -646,8 +699,8 @@ Tasks automatically get high salience so they won't be discarded.`, {
|
|
|
646
699
|
}],
|
|
647
700
|
};
|
|
648
701
|
});
|
|
649
|
-
server.tool('memory_task_list', `List tasks with optional status filter. Shows tasks ordered by priority (urgent first).
|
|
650
|
-
|
|
702
|
+
server.tool('memory_task_list', `List tasks with optional status filter. Shows tasks ordered by priority (urgent first).
|
|
703
|
+
|
|
651
704
|
Use at the start of a session to see what's pending, or to check blocked/done tasks.`, {
|
|
652
705
|
status: z.enum(['open', 'in_progress', 'blocked', 'done']).optional()
|
|
653
706
|
.describe('Filter by status (omit to see all active tasks)'),
|
|
@@ -673,10 +726,10 @@ Use at the start of a session to see what's pending, or to check blocked/done ta
|
|
|
673
726
|
}],
|
|
674
727
|
};
|
|
675
728
|
});
|
|
676
|
-
server.tool('memory_task_next', `Get the single most important task to work on next.
|
|
677
|
-
|
|
678
|
-
Prioritizes: in_progress tasks first (finish what you started), then by priority level, then oldest first. Skips blocked and done tasks.
|
|
679
|
-
|
|
729
|
+
server.tool('memory_task_next', `Get the single most important task to work on next.
|
|
730
|
+
|
|
731
|
+
Prioritizes: in_progress tasks first (finish what you started), then by priority level, then oldest first. Skips blocked and done tasks.
|
|
732
|
+
|
|
680
733
|
Use this when you finish a task or need to decide what to do next.`, {}, async () => {
|
|
681
734
|
const next = store.getNextTask(AGENT_ID);
|
|
682
735
|
if (!next) {
|
|
@@ -692,13 +745,13 @@ Use this when you finish a task or need to decide what to do next.`, {}, async (
|
|
|
692
745
|
};
|
|
693
746
|
});
|
|
694
747
|
// --- Task Bracket Tools ---
|
|
695
|
-
server.tool('memory_task_begin', `Signal that you're starting a significant task. Auto-checkpoints current state and recalls relevant memories.
|
|
696
|
-
|
|
697
|
-
CALL THIS when starting:
|
|
698
|
-
- A multi-step operation (doc generation, large refactor, migration)
|
|
699
|
-
- Work on a new topic or project area
|
|
700
|
-
- Anything that might fill the context window
|
|
701
|
-
|
|
748
|
+
server.tool('memory_task_begin', `Signal that you're starting a significant task. Auto-checkpoints current state and recalls relevant memories.
|
|
749
|
+
|
|
750
|
+
CALL THIS when starting:
|
|
751
|
+
- A multi-step operation (doc generation, large refactor, migration)
|
|
752
|
+
- Work on a new topic or project area
|
|
753
|
+
- Anything that might fill the context window
|
|
754
|
+
|
|
702
755
|
This ensures your state is saved before you start, and primes recall with relevant context.`, {
|
|
703
756
|
topic: z.string().describe('What task are you starting? (3-15 words)'),
|
|
704
757
|
files: z.array(z.string()).optional().default([])
|
|
@@ -748,13 +801,13 @@ This ensures your state is saved before you start, and primes recall with releva
|
|
|
748
801
|
}],
|
|
749
802
|
};
|
|
750
803
|
});
|
|
751
|
-
server.tool('memory_task_end', `Signal that you've finished a significant task. Writes a summary memory and auto-checkpoints.
|
|
752
|
-
|
|
753
|
-
CALL THIS when you finish:
|
|
754
|
-
- A multi-step operation
|
|
755
|
-
- Before switching to a different topic
|
|
756
|
-
- At the end of a work session
|
|
757
|
-
|
|
804
|
+
server.tool('memory_task_end', `Signal that you've finished a significant task. Writes a summary memory and auto-checkpoints.
|
|
805
|
+
|
|
806
|
+
CALL THIS when you finish:
|
|
807
|
+
- A multi-step operation
|
|
808
|
+
- Before switching to a different topic
|
|
809
|
+
- At the end of a work session
|
|
810
|
+
|
|
758
811
|
This captures what was accomplished so future sessions can recall it.`, {
|
|
759
812
|
summary: z.string().describe('What was accomplished? Include key outcomes, decisions, and any issues.'),
|
|
760
813
|
tags: z.array(z.string()).optional().default([])
|
|
@@ -844,6 +897,18 @@ This captures what was accomplished so future sessions can recall it.`, {
|
|
|
844
897
|
console.error(`[mcp] consolidation done: ${result.edgesStrengthened} strengthened, ${result.memoriesForgotten} forgotten`);
|
|
845
898
|
},
|
|
846
899
|
});
|
|
900
|
+
// Coordination MCP tools (opt-in via AWM_COORDINATION=true)
|
|
901
|
+
const coordEnabled = process.env.AWM_COORDINATION === 'true' || process.env.AWM_COORDINATION === '1';
|
|
902
|
+
if (coordEnabled) {
|
|
903
|
+
const { initCoordinationTables } = await import('./coordination/schema.js');
|
|
904
|
+
const { registerCoordinationTools } = await import('./coordination/mcp-tools.js');
|
|
905
|
+
initCoordinationTables(store.getDb());
|
|
906
|
+
registerCoordinationTools(server, store.getDb());
|
|
907
|
+
coordDb = store.getDb();
|
|
908
|
+
}
|
|
909
|
+
else {
|
|
910
|
+
console.error('AWM: coordination tools disabled (set AWM_COORDINATION=true to enable)');
|
|
911
|
+
}
|
|
847
912
|
// Log to stderr (stdout is reserved for MCP protocol)
|
|
848
913
|
console.error(`AgentWorkingMemory MCP server started (agent: ${AGENT_ID}, db: ${DB_PATH})`);
|
|
849
914
|
console.error(`Hook sidecar on 127.0.0.1:${HOOK_PORT}${HOOK_SECRET ? ' (auth enabled)' : ' (no auth — set AWM_HOOK_SECRET)'}`);
|
|
@@ -852,6 +917,10 @@ This captures what was accomplished so future sessions can recall it.`, {
|
|
|
852
917
|
sidecar.close();
|
|
853
918
|
consolidationScheduler.stop();
|
|
854
919
|
stagingBuffer.stop();
|
|
920
|
+
try {
|
|
921
|
+
store.walCheckpoint();
|
|
922
|
+
}
|
|
923
|
+
catch { /* non-fatal */ }
|
|
855
924
|
store.close();
|
|
856
925
|
};
|
|
857
926
|
process.on('SIGINT', () => { cleanup(); process.exit(0); });
|