agent-working-memory 0.11.0 → 0.12.3
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 +71 -297
- package/dist/adapters/claude-code.d.ts.map +1 -1
- package/dist/adapters/claude-code.js +63 -3
- package/dist/adapters/claude-code.js.map +1 -1
- package/dist/adapters/common.d.ts.map +1 -1
- package/dist/adapters/common.js +358 -306
- package/dist/adapters/common.js.map +1 -1
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +29 -7
- package/dist/api/routes.js.map +1 -1
- package/dist/coordination/routes.d.ts.map +1 -1
- package/dist/coordination/routes.js +174 -170
- package/dist/coordination/routes.js.map +1 -1
- package/dist/core/embeddings.d.ts.map +1 -1
- package/dist/core/embeddings.js +4 -1
- package/dist/core/embeddings.js.map +1 -1
- package/dist/core/entity-extract.d.ts +3 -0
- package/dist/core/entity-extract.d.ts.map +1 -0
- package/dist/core/entity-extract.js +47 -0
- package/dist/core/entity-extract.js.map +1 -0
- package/dist/core/format-recall.d.ts +16 -0
- package/dist/core/format-recall.d.ts.map +1 -0
- package/dist/core/format-recall.js +24 -0
- package/dist/core/format-recall.js.map +1 -0
- package/dist/core/query-expander.js +1 -1
- package/dist/core/query-expander.js.map +1 -1
- package/dist/core/reranker.js +1 -1
- package/dist/core/reranker.js.map +1 -1
- package/dist/core/salience.d.ts.map +1 -1
- package/dist/core/salience.js +14 -2
- package/dist/core/salience.js.map +1 -1
- package/dist/core/whoami.d.ts +24 -0
- package/dist/core/whoami.d.ts.map +1 -0
- package/dist/core/whoami.js +66 -0
- package/dist/core/whoami.js.map +1 -0
- package/dist/core/write-pipeline.d.ts +9 -0
- package/dist/core/write-pipeline.d.ts.map +1 -1
- package/dist/core/write-pipeline.js +109 -68
- package/dist/core/write-pipeline.js.map +1 -1
- package/dist/core/write-telemetry.d.ts +33 -0
- package/dist/core/write-telemetry.d.ts.map +1 -0
- package/dist/core/write-telemetry.js +110 -0
- package/dist/core/write-telemetry.js.map +1 -0
- package/dist/engine/activation.d.ts +22 -12
- package/dist/engine/activation.d.ts.map +1 -1
- package/dist/engine/activation.js +133 -17
- package/dist/engine/activation.js.map +1 -1
- package/dist/engine/consolidation-scheduler.d.ts +1 -1
- package/dist/engine/consolidation-scheduler.js +1 -1
- package/dist/engine/consolidation.d.ts +1 -0
- package/dist/engine/consolidation.d.ts.map +1 -1
- package/dist/engine/consolidation.js +18 -0
- package/dist/engine/consolidation.js.map +1 -1
- package/dist/engine/eval.d.ts.map +1 -1
- package/dist/engine/eval.js +5 -1
- package/dist/engine/eval.js.map +1 -1
- package/dist/hooks/sidecar.d.ts +26 -0
- package/dist/hooks/sidecar.d.ts.map +1 -1
- package/dist/hooks/sidecar.js +30 -0
- package/dist/hooks/sidecar.js.map +1 -1
- package/dist/index.js +20 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp.d.ts +2 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +222 -108
- package/dist/mcp.js.map +1 -1
- package/dist/recipes/index.d.ts +57 -0
- package/dist/recipes/index.d.ts.map +1 -0
- package/dist/recipes/index.js +81 -0
- package/dist/recipes/index.js.map +1 -0
- package/dist/storage/pglite-schema.d.ts.map +1 -1
- package/dist/storage/pglite-schema.js +27 -0
- package/dist/storage/pglite-schema.js.map +1 -1
- package/dist/storage/pglite.d.ts +5 -0
- package/dist/storage/pglite.d.ts.map +1 -1
- package/dist/storage/pglite.js +180 -138
- package/dist/storage/pglite.js.map +1 -1
- package/dist/storage/postgres.d.ts +5 -0
- package/dist/storage/postgres.d.ts.map +1 -1
- package/dist/storage/postgres.js +180 -138
- package/dist/storage/postgres.js.map +1 -1
- package/dist/storage/sqlite.d.ts +9 -0
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +394 -326
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/types/engram.d.ts +14 -0
- package/dist/types/engram.d.ts.map +1 -1
- package/dist/types/engram.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/claude-code.ts +66 -3
- package/src/adapters/common.ts +567 -515
- package/src/api/routes.ts +999 -971
- package/src/coordination/routes.ts +2155 -2150
- package/src/core/embeddings.ts +4 -1
- package/src/core/entity-extract.ts +47 -0
- package/src/core/format-recall.ts +25 -0
- package/src/core/query-expander.ts +1 -1
- package/src/core/reranker.ts +1 -1
- package/src/core/salience.ts +529 -514
- package/src/core/whoami.ts +92 -0
- package/src/core/write-pipeline.ts +60 -8
- package/src/core/write-telemetry.ts +131 -0
- package/src/engine/activation.ts +1468 -1369
- package/src/engine/consolidation-scheduler.ts +1 -1
- package/src/engine/consolidation.ts +887 -869
- package/src/engine/eval.ts +6 -1
- package/src/hooks/sidecar.ts +55 -0
- package/src/index.ts +248 -227
- package/src/mcp.ts +1387 -1270
- package/src/recipes/index.ts +125 -0
- package/src/storage/pglite-schema.ts +27 -0
- package/src/storage/pglite.ts +1420 -1372
- package/src/storage/postgres.ts +1523 -1475
- package/src/storage/sqlite.ts +1936 -1861
- package/src/types/engram.ts +22 -0
package/dist/mcp.js
CHANGED
|
@@ -6,13 +6,14 @@
|
|
|
6
6
|
* Runs as a stdio-based MCP server that Claude Code connects to directly.
|
|
7
7
|
* Uses the storage and engine layers in-process (no HTTP overhead).
|
|
8
8
|
*
|
|
9
|
-
* Tools exposed (
|
|
9
|
+
* Tools exposed (19):
|
|
10
10
|
* memory_write — store a memory (salience filter decides disposition)
|
|
11
11
|
* memory_recall — activate memories by context (cognitive retrieval)
|
|
12
12
|
* memory_feedback — report whether a recalled memory was useful
|
|
13
13
|
* memory_retract — invalidate a wrong memory with optional correction
|
|
14
14
|
* memory_supersede — replace an outdated memory with a current one
|
|
15
15
|
* memory_stats — get memory health metrics
|
|
16
|
+
* memory_whoami — identify this instance, mode, store, and sibling agent spaces
|
|
16
17
|
* memory_checkpoint — save structured execution state (survives compaction)
|
|
17
18
|
* memory_restore — restore state + targeted recall after compaction
|
|
18
19
|
* memory_task_add — create a prioritized task
|
|
@@ -66,13 +67,19 @@ import { ConsolidationScheduler } from './engine/consolidation-scheduler.js';
|
|
|
66
67
|
import { evaluateSalience } from './core/salience.js';
|
|
67
68
|
import { performWrite } from './core/write-pipeline.js';
|
|
68
69
|
import { DEFAULT_AGENT_CONFIG } from './types/agent.js';
|
|
69
|
-
import { embed } from './core/embeddings.js';
|
|
70
|
+
import { embed, getEmbedder } from './core/embeddings.js';
|
|
71
|
+
import { getReranker } from './core/reranker.js';
|
|
72
|
+
import { getExpander } from './core/query-expander.js';
|
|
70
73
|
import { startSidecar } from './hooks/sidecar.js';
|
|
71
74
|
import { initLogger, log, getLogPath } from './core/logger.js';
|
|
72
75
|
import { VERSION } from './version.js';
|
|
73
76
|
import { buildPack, INTERVIEW_QUESTIONS } from './onboard/index.js';
|
|
74
77
|
import { liteCompress, retrieveOriginal } from './core/lite-compress.js';
|
|
75
78
|
import { queryPeerDecisions, formatPeerDecisions } from './coordination/peer-decisions.js';
|
|
79
|
+
import { startLoopLagMonitor } from './core/write-telemetry.js';
|
|
80
|
+
import { buildWhoami, formatWhoami } from './core/whoami.js';
|
|
81
|
+
import { renderTaskEndInvitation, validateRecipeWrite, recipeSlug, getRecipe } from './recipes/index.js';
|
|
82
|
+
import { formatRecallResultLine } from './core/format-recall.js';
|
|
76
83
|
// --- Incognito Mode ---
|
|
77
84
|
// When AWM_INCOGNITO=1, register zero tools. Claude won't see memory tools at all.
|
|
78
85
|
// No DB, no engines, no sidecar — just a bare MCP server that exposes nothing.
|
|
@@ -124,6 +131,7 @@ else {
|
|
|
124
131
|
const consolidationScheduler = new ConsolidationScheduler(store, consolidationEngine);
|
|
125
132
|
stagingBuffer.start(DEFAULT_AGENT_CONFIG.stagingTtlMs);
|
|
126
133
|
consolidationScheduler.start();
|
|
134
|
+
startLoopLagMonitor();
|
|
127
135
|
// Coordination DB handle — set when AWM_COORDINATION=true, used by memory_write for decision propagation
|
|
128
136
|
let coordDb = null;
|
|
129
137
|
const server = new McpServer({
|
|
@@ -188,15 +196,15 @@ else {
|
|
|
188
196
|
return 'unclassified';
|
|
189
197
|
}
|
|
190
198
|
// --- Tools ---
|
|
191
|
-
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.
|
|
192
|
-
|
|
193
|
-
CALL THIS PROACTIVELY — do not wait to be asked. Write memories when you:
|
|
194
|
-
- Discover something about the codebase, bugs, or architecture
|
|
195
|
-
- Make a decision and want to remember why
|
|
196
|
-
- Encounter and resolve an error
|
|
197
|
-
- Learn a user preference or project pattern
|
|
198
|
-
- Complete a significant piece of work
|
|
199
|
-
|
|
199
|
+
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.
|
|
200
|
+
|
|
201
|
+
CALL THIS PROACTIVELY — do not wait to be asked. Write memories when you:
|
|
202
|
+
- Discover something about the codebase, bugs, or architecture
|
|
203
|
+
- Make a decision and want to remember why
|
|
204
|
+
- Encounter and resolve an error
|
|
205
|
+
- Learn a user preference or project pattern
|
|
206
|
+
- Complete a significant piece of work
|
|
207
|
+
|
|
200
208
|
The concept should be a short label (3-8 words). The content should be the full detail.`, {
|
|
201
209
|
concept: z.string().describe('Short label for this memory (3-8 words)'),
|
|
202
210
|
content: z.string().describe('Full detail of what was learned'),
|
|
@@ -229,6 +237,14 @@ The concept should be a short label (3-8 words). The content should be the full
|
|
|
229
237
|
.describe('Confidence: verified (tested), observed (read in code), assumed (reasoning).'),
|
|
230
238
|
session_id: z.string().optional()
|
|
231
239
|
.describe('Session/conversation grouping ID. Memories with same session_id are associated.'),
|
|
240
|
+
origin_class: z.enum(['user-stated', 'tool-output', 'inference', 'recipe']).optional()
|
|
241
|
+
.describe('Provenance (D5, log-only): where this knowledge came from. user-stated = the human said it; tool-output = read from a tool/system; inference = your reasoning; recipe = produced by a cognition recipe.'),
|
|
242
|
+
recipe_id: z.string().optional()
|
|
243
|
+
.describe('Cognition-recipe id+version when origin_class is recipe.'),
|
|
244
|
+
valid_from: z.string().optional()
|
|
245
|
+
.describe('ISO date when the FACT becomes valid (temporal validity, not ingestion time).'),
|
|
246
|
+
valid_to: z.string().optional()
|
|
247
|
+
.describe('ISO date when the FACT stops being valid (e.g., a deadline or a superseding change).'),
|
|
232
248
|
intent: z.enum(['decision', 'question', 'todo', 'finding', 'context']).optional()
|
|
233
249
|
.describe('What kind of memory this is.'),
|
|
234
250
|
}, async (params) => {
|
|
@@ -247,6 +263,37 @@ The concept should be a short label (3-8 words). The content should be the full
|
|
|
247
263
|
metaTags.push(`sid=${params.session_id}`);
|
|
248
264
|
if (params.intent)
|
|
249
265
|
metaTags.push(`intent=${params.intent}`);
|
|
266
|
+
// D14 (2026-07-30): recipe write-backs are contract-checked. Provenance
|
|
267
|
+
// must never claim a recipe that does not exist, and malformed
|
|
268
|
+
// derivations are rejected with the contract echoed back so the host
|
|
269
|
+
// can self-correct in one retry.
|
|
270
|
+
if (params.origin_class === 'recipe') {
|
|
271
|
+
if (!params.recipe_id) {
|
|
272
|
+
return { content: [{ type: 'text', text: "Recipe write rejected: origin_class 'recipe' requires recipe_id (e.g. 'skill-derivation@1')." }] };
|
|
273
|
+
}
|
|
274
|
+
const v = validateRecipeWrite(params.recipe_id, params.concept, params.content);
|
|
275
|
+
if (!v.ok) {
|
|
276
|
+
const contract = getRecipe(params.recipe_id)?.writeBack ?? 'unknown recipe';
|
|
277
|
+
return { content: [{ type: 'text', text: `Recipe write rejected (${params.recipe_id}): ${v.errors.join('; ')}\nContract: ${contract}` }] };
|
|
278
|
+
}
|
|
279
|
+
// Standardize recipe write-backs: canonical class, standard tags.
|
|
280
|
+
const slug = recipeSlug(params.concept);
|
|
281
|
+
const ensure = (tag) => { if (!userTags.includes(tag) && !metaTags.includes(tag))
|
|
282
|
+
metaTags.push(tag); };
|
|
283
|
+
if (params.recipe_id.startsWith('skill-derivation')) {
|
|
284
|
+
ensure('topic=skill');
|
|
285
|
+
ensure(`skill=${slug}`);
|
|
286
|
+
params.memory_type = params.memory_type ?? 'procedural';
|
|
287
|
+
}
|
|
288
|
+
else if (params.recipe_id.startsWith('friction-lesson')) {
|
|
289
|
+
ensure('topic=friction');
|
|
290
|
+
ensure(`about=${slug}`);
|
|
291
|
+
// zod defaults event_type to 'observation', so force the recipe's
|
|
292
|
+
// contract value rather than ??-guarding against undefined.
|
|
293
|
+
params.event_type = 'friction';
|
|
294
|
+
}
|
|
295
|
+
params.memory_class = 'canonical';
|
|
296
|
+
}
|
|
250
297
|
const memoryType = params.memory_type ?? classifyMemoryType(params.content);
|
|
251
298
|
const result = await performWrite({ store, connectionEngine }, {
|
|
252
299
|
agentId: AGENT_ID,
|
|
@@ -261,6 +308,11 @@ The concept should be a short label (3-8 words). The content should be the full
|
|
|
261
308
|
memoryClass: params.memory_class,
|
|
262
309
|
memoryType,
|
|
263
310
|
supersedes: params.supersedes,
|
|
311
|
+
originClass: params.origin_class,
|
|
312
|
+
writerSession: params.session_id,
|
|
313
|
+
recipeId: params.recipe_id,
|
|
314
|
+
validFrom: params.valid_from,
|
|
315
|
+
validTo: params.valid_to,
|
|
264
316
|
});
|
|
265
317
|
// Auto-checkpoint — covers create/reinforce/supersede uniformly
|
|
266
318
|
try {
|
|
@@ -296,19 +348,22 @@ The concept should be a short label (3-8 words). The content should be the full
|
|
|
296
348
|
return {
|
|
297
349
|
content: [{
|
|
298
350
|
type: 'text',
|
|
299
|
-
text: `Stored (${salience.disposition}) "${params.concept}" [${salience.score.toFixed(2)}]\nID: ${engram.id}
|
|
351
|
+
text: `Stored (${salience.disposition}) "${params.concept}" [${salience.score.toFixed(2)}]\nID: ${engram.id}`
|
|
352
|
+
+ (isLowSalience
|
|
353
|
+
? `\nNOTE: low salience — this memory is kept but demoted and may fade first. If it MUST survive and be recallable, retry with memory_class: 'canonical'. (Discards are audited: reason codes ${JSON.stringify(salience.reasonCodes.slice(0, 4))})`
|
|
354
|
+
: ''),
|
|
300
355
|
}],
|
|
301
356
|
};
|
|
302
357
|
});
|
|
303
|
-
server.tool('memory_recall', `Recall memories relevant to a query. Uses cognitive activation — not keyword search.
|
|
304
|
-
|
|
305
|
-
ALWAYS call this when:
|
|
306
|
-
- Starting work on a project or topic (recall what you know)
|
|
307
|
-
- Debugging (recall similar errors and solutions)
|
|
308
|
-
- Making decisions (recall past decisions and outcomes)
|
|
309
|
-
- The user mentions a topic you might have stored memories about
|
|
310
|
-
|
|
311
|
-
Accepts either "query" or "context" parameter — both work identically.
|
|
358
|
+
server.tool('memory_recall', `Recall memories relevant to a query. Uses cognitive activation — not keyword search.
|
|
359
|
+
|
|
360
|
+
ALWAYS call this when:
|
|
361
|
+
- Starting work on a project or topic (recall what you know)
|
|
362
|
+
- Debugging (recall similar errors and solutions)
|
|
363
|
+
- Making decisions (recall past decisions and outcomes)
|
|
364
|
+
- The user mentions a topic you might have stored memories about
|
|
365
|
+
|
|
366
|
+
Accepts either "query" or "context" parameter — both work identically.
|
|
312
367
|
Returns the most relevant memories ranked by text relevance, temporal recency, and associative strength.`, {
|
|
313
368
|
query: z.string().optional().describe('What to search for — describe the situation, question, or topic'),
|
|
314
369
|
context: z.string().optional().describe('Alias for query (either works)'),
|
|
@@ -365,13 +420,11 @@ Returns the most relevant memories ranked by text relevance, temporal recency, a
|
|
|
365
420
|
}],
|
|
366
421
|
};
|
|
367
422
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
return `${i + 1}. **${r.engram.concept}** (${r.score.toFixed(3)}): ${body}`;
|
|
374
|
-
});
|
|
423
|
+
// Confidence-adaptive output (Paper 3: cognitive teaming) and D8
|
|
424
|
+
// (2026-07-30) conflict surfacing both live in the shared formatter now —
|
|
425
|
+
// see core/format-recall.ts for why it's extracted (0.12.1: unit-testable
|
|
426
|
+
// without booting the server) and why the id sits after the score.
|
|
427
|
+
const lines = results.map(formatRecallResultLine);
|
|
375
428
|
return {
|
|
376
429
|
content: [{
|
|
377
430
|
type: 'text',
|
|
@@ -379,8 +432,8 @@ Returns the most relevant memories ranked by text relevance, temporal recency, a
|
|
|
379
432
|
}],
|
|
380
433
|
};
|
|
381
434
|
});
|
|
382
|
-
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.
|
|
383
|
-
|
|
435
|
+
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.
|
|
436
|
+
|
|
384
437
|
Always call this after using a recalled memory so the system learns what's valuable.`, {
|
|
385
438
|
engram_id: z.string().describe('ID of the memory (from memory_recall results)'),
|
|
386
439
|
useful: z.boolean().describe('Was this memory actually helpful?'),
|
|
@@ -403,8 +456,8 @@ Always call this after using a recalled memory so the system learns what's valua
|
|
|
403
456
|
}],
|
|
404
457
|
};
|
|
405
458
|
});
|
|
406
|
-
server.tool('memory_retract', `Retract a memory that turned out to be wrong. Creates a correction and reduces confidence of related memories.
|
|
407
|
-
|
|
459
|
+
server.tool('memory_retract', `Retract a memory that turned out to be wrong. Creates a correction and reduces confidence of related memories.
|
|
460
|
+
|
|
408
461
|
Use this when you discover a memory contains incorrect information.`, {
|
|
409
462
|
engram_id: z.string().describe('ID of the wrong memory'),
|
|
410
463
|
reason: z.string().describe('Why is this memory wrong?'),
|
|
@@ -428,15 +481,15 @@ Use this when you discover a memory contains incorrect information.`, {
|
|
|
428
481
|
}],
|
|
429
482
|
};
|
|
430
483
|
});
|
|
431
|
-
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.
|
|
432
|
-
|
|
433
|
-
Use this when:
|
|
434
|
-
- A status or count has changed (e.g., "5 reviews done" → "7 reviews done")
|
|
435
|
-
- Architecture or infrastructure evolved (e.g., "two-repo model" → "three-repo model")
|
|
436
|
-
- A schedule or plan was updated
|
|
437
|
-
|
|
484
|
+
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.
|
|
485
|
+
|
|
486
|
+
Use this when:
|
|
487
|
+
- A status or count has changed (e.g., "5 reviews done" → "7 reviews done")
|
|
488
|
+
- Architecture or infrastructure evolved (e.g., "two-repo model" → "three-repo model")
|
|
489
|
+
- A schedule or plan was updated
|
|
490
|
+
|
|
438
491
|
The old memory stays in the database (searchable for history) but is heavily down-ranked in recall so the current version dominates.`, {
|
|
439
|
-
old_engram_id: z.string().describe('ID of the outdated memory'),
|
|
492
|
+
old_engram_id: z.string().describe('ID of the outdated memory (from memory_recall results, or memory_write\'s own response if you just wrote it)'),
|
|
440
493
|
new_engram_id: z.string().describe('ID of the replacement memory'),
|
|
441
494
|
reason: z.string().optional().describe('Why the old memory is outdated'),
|
|
442
495
|
}, async (params) => {
|
|
@@ -461,7 +514,11 @@ The old memory stays in the database (searchable for history) but is heavily dow
|
|
|
461
514
|
}],
|
|
462
515
|
};
|
|
463
516
|
});
|
|
464
|
-
server.tool('
|
|
517
|
+
server.tool('memory_whoami', `Identify THIS AWM instance — agent id, mode (standalone/hive), backend, store path, code provenance, ports, and the sibling agent spaces present in the same store. Call when unsure which AWM instance or memory space you are talking to.`, {}, async () => {
|
|
518
|
+
const info = await buildWhoami(store, AGENT_ID, 'mcp');
|
|
519
|
+
return { content: [{ type: 'text', text: formatWhoami(info) }] };
|
|
520
|
+
});
|
|
521
|
+
server.tool('memory_stats', `Get memory health stats — how many memories, confidence levels, association count, and system performance.
|
|
465
522
|
Also shows the activity log path so the user can tail it to see what's happening.`, {}, async () => {
|
|
466
523
|
const metrics = await evalEngine.computeMetrics(AGENT_ID);
|
|
467
524
|
const checkpoint = await store.getCheckpoint(AGENT_ID);
|
|
@@ -492,13 +549,13 @@ Also shows the activity log path so the user can tail it to see what's happening
|
|
|
492
549
|
};
|
|
493
550
|
});
|
|
494
551
|
// --- Checkpointing Tools ---
|
|
495
|
-
server.tool('memory_checkpoint', `Save your current execution state so you can recover after context compaction.
|
|
496
|
-
|
|
497
|
-
ALWAYS call this before:
|
|
498
|
-
- Long operations (multi-file generation, large refactors, overnight work)
|
|
499
|
-
- Anything that might fill the context window
|
|
500
|
-
- Switching to a different task
|
|
501
|
-
|
|
552
|
+
server.tool('memory_checkpoint', `Save your current execution state so you can recover after context compaction.
|
|
553
|
+
|
|
554
|
+
ALWAYS call this before:
|
|
555
|
+
- Long operations (multi-file generation, large refactors, overnight work)
|
|
556
|
+
- Anything that might fill the context window
|
|
557
|
+
- Switching to a different task
|
|
558
|
+
|
|
502
559
|
Also call periodically during long sessions to avoid losing state. The state is saved per-agent and overwrites any previous checkpoint.`, {
|
|
503
560
|
current_task: z.string().describe('What you are currently working on'),
|
|
504
561
|
decisions: z.array(z.string()).optional().default([])
|
|
@@ -532,14 +589,14 @@ Also call periodically during long sessions to avoid losing state. The state is
|
|
|
532
589
|
}],
|
|
533
590
|
};
|
|
534
591
|
});
|
|
535
|
-
server.tool('memory_restore', `Restore your previous execution state after context compaction or at session start.
|
|
536
|
-
|
|
537
|
-
Returns:
|
|
538
|
-
- Your saved execution state (task, decisions, next steps, files)
|
|
539
|
-
- Recently recalled memories for context
|
|
540
|
-
- Your last write for continuity
|
|
541
|
-
- How long you were idle
|
|
542
|
-
|
|
592
|
+
server.tool('memory_restore', `Restore your previous execution state after context compaction or at session start.
|
|
593
|
+
|
|
594
|
+
Returns:
|
|
595
|
+
- Your saved execution state (task, decisions, next steps, files)
|
|
596
|
+
- Recently recalled memories for context
|
|
597
|
+
- Your last write for continuity
|
|
598
|
+
- How long you were idle
|
|
599
|
+
|
|
543
600
|
Use this at the start of every session or after compaction to pick up where you left off.`, {}, async () => {
|
|
544
601
|
const checkpoint = await store.getCheckpoint(AGENT_ID);
|
|
545
602
|
// Cold-store nudge: an empty store means the agent has nothing to recall — offer to warm-start.
|
|
@@ -658,9 +715,9 @@ Use this at the start of every session or after compaction to pick up where you
|
|
|
658
715
|
if (coordDb) {
|
|
659
716
|
try {
|
|
660
717
|
const myAgent = coordDb.prepare(`SELECT id FROM coord_agents WHERE name = ? AND status != 'dead' ORDER BY last_seen DESC LIMIT 1`).get(AGENT_ID);
|
|
661
|
-
const peerDecisions = coordDb.prepare(`SELECT d.summary, a.name AS author_name, d.created_at
|
|
662
|
-
FROM coord_decisions d JOIN coord_agents a ON d.author_id = a.id
|
|
663
|
-
WHERE d.author_id != ? AND d.created_at > datetime('now', '-30 minutes')
|
|
718
|
+
const peerDecisions = coordDb.prepare(`SELECT d.summary, a.name AS author_name, d.created_at
|
|
719
|
+
FROM coord_decisions d JOIN coord_agents a ON d.author_id = a.id
|
|
720
|
+
WHERE d.author_id != ? AND d.created_at > datetime('now', '-30 minutes')
|
|
664
721
|
ORDER BY d.created_at DESC LIMIT 10`).all(myAgent?.id ?? '');
|
|
665
722
|
if (peerDecisions.length > 0) {
|
|
666
723
|
parts.push(`\n**Peer decisions (last 30 min):**`);
|
|
@@ -679,11 +736,11 @@ Use this at the start of every session or after compaction to pick up where you
|
|
|
679
736
|
};
|
|
680
737
|
});
|
|
681
738
|
// --- Onboarding Tools (warm-start a cold store) ---
|
|
682
|
-
server.tool('onboard_scan', `Scan a project's documentation + repository and return CANDIDATE memories to seed a cold store.
|
|
683
|
-
|
|
684
|
-
Use this when the store is empty / you're new to a project. The scan is deterministic
|
|
685
|
-
(real file contents, not guesses) — YOUR job is to refine the candidates into atomic,
|
|
686
|
-
recall-shaped memories, run the interview (onboard_questions), confirm with the user, then
|
|
739
|
+
server.tool('onboard_scan', `Scan a project's documentation + repository and return CANDIDATE memories to seed a cold store.
|
|
740
|
+
|
|
741
|
+
Use this when the store is empty / you're new to a project. The scan is deterministic
|
|
742
|
+
(real file contents, not guesses) — YOUR job is to refine the candidates into atomic,
|
|
743
|
+
recall-shaped memories, run the interview (onboard_questions), confirm with the user, then
|
|
687
744
|
save the good ones with memory_write (memory_class="canonical"). Nothing is saved by this tool.`, {
|
|
688
745
|
docs: z.array(z.string()).optional()
|
|
689
746
|
.describe('Doc files/dirs to scan (Markdown/text). Defaults to the repo (or cwd).'),
|
|
@@ -708,18 +765,18 @@ save the good ones with memory_write (memory_class="canonical"). Nothing is save
|
|
|
708
765
|
].join('\n');
|
|
709
766
|
return { content: [{ type: 'text', text }] };
|
|
710
767
|
});
|
|
711
|
-
server.tool('onboard_questions', `Return the onboarding interview questions. Ask the user ONE at a time, starting with the
|
|
768
|
+
server.tool('onboard_questions', `Return the onboarding interview questions. Ask the user ONE at a time, starting with the
|
|
712
769
|
goal of the memory system, and ask follow-ups for clarity. Turn each answer into a canonical memory.`, {}, async () => ({
|
|
713
770
|
content: [{ type: 'text', text: INTERVIEW_QUESTIONS.map((q, i) => `${i + 1}. ${q}`).join('\n') }],
|
|
714
771
|
}));
|
|
715
772
|
// --- Task Management Tools ---
|
|
716
|
-
server.tool('memory_task_add', `Create a task that you need to come back to. Tasks are memories with status and priority tracking.
|
|
717
|
-
|
|
718
|
-
Use this when:
|
|
719
|
-
- You identify work that needs doing but can't do it right now
|
|
720
|
-
- The user mentions something to do later
|
|
721
|
-
- You want to park a sub-task while focusing on something more urgent
|
|
722
|
-
|
|
773
|
+
server.tool('memory_task_add', `Create a task that you need to come back to. Tasks are memories with status and priority tracking.
|
|
774
|
+
|
|
775
|
+
Use this when:
|
|
776
|
+
- You identify work that needs doing but can't do it right now
|
|
777
|
+
- The user mentions something to do later
|
|
778
|
+
- You want to park a sub-task while focusing on something more urgent
|
|
779
|
+
|
|
723
780
|
Tasks automatically get high salience so they won't be discarded.`, {
|
|
724
781
|
concept: z.string().describe('Short task title (3-10 words)'),
|
|
725
782
|
content: z.string().describe('Full task description — what needs doing, context, acceptance criteria'),
|
|
@@ -759,11 +816,11 @@ Tasks automatically get high salience so they won't be discarded.`, {
|
|
|
759
816
|
}],
|
|
760
817
|
};
|
|
761
818
|
});
|
|
762
|
-
server.tool('memory_task_update', `Update a task's status or priority. Use this to:
|
|
763
|
-
- Start working on a task (open → in_progress)
|
|
764
|
-
- Mark a task done (→ done)
|
|
765
|
-
- Block a task on another (→ blocked)
|
|
766
|
-
- Reprioritize (change priority)
|
|
819
|
+
server.tool('memory_task_update', `Update a task's status or priority. Use this to:
|
|
820
|
+
- Start working on a task (open → in_progress)
|
|
821
|
+
- Mark a task done (→ done)
|
|
822
|
+
- Block a task on another (→ blocked)
|
|
823
|
+
- Reprioritize (change priority)
|
|
767
824
|
- Unblock a task (clear blocked_by)`, {
|
|
768
825
|
task_id: z.string().describe('ID of the task to update'),
|
|
769
826
|
status: z.enum(['open', 'in_progress', 'blocked', 'done']).optional()
|
|
@@ -793,8 +850,8 @@ Tasks automatically get high salience so they won't be discarded.`, {
|
|
|
793
850
|
}],
|
|
794
851
|
};
|
|
795
852
|
});
|
|
796
|
-
server.tool('memory_task_list', `List tasks with optional status filter. Shows tasks ordered by priority (urgent first).
|
|
797
|
-
|
|
853
|
+
server.tool('memory_task_list', `List tasks with optional status filter. Shows tasks ordered by priority (urgent first).
|
|
854
|
+
|
|
798
855
|
Use at the start of a session to see what's pending, or to check blocked/done tasks.`, {
|
|
799
856
|
status: z.enum(['open', 'in_progress', 'blocked', 'done']).optional()
|
|
800
857
|
.describe('Filter by status (omit to see all active tasks)'),
|
|
@@ -820,10 +877,10 @@ Use at the start of a session to see what's pending, or to check blocked/done ta
|
|
|
820
877
|
}],
|
|
821
878
|
};
|
|
822
879
|
});
|
|
823
|
-
server.tool('memory_task_next', `Get the single most important task to work on next.
|
|
824
|
-
|
|
825
|
-
Prioritizes: in_progress tasks first (finish what you started), then by priority level, then oldest first. Skips blocked and done tasks.
|
|
826
|
-
|
|
880
|
+
server.tool('memory_task_next', `Get the single most important task to work on next.
|
|
881
|
+
|
|
882
|
+
Prioritizes: in_progress tasks first (finish what you started), then by priority level, then oldest first. Skips blocked and done tasks.
|
|
883
|
+
|
|
827
884
|
Use this when you finish a task or need to decide what to do next.`, {}, async () => {
|
|
828
885
|
const next = await store.getNextTask(AGENT_ID);
|
|
829
886
|
if (!next) {
|
|
@@ -839,13 +896,13 @@ Use this when you finish a task or need to decide what to do next.`, {}, async (
|
|
|
839
896
|
};
|
|
840
897
|
});
|
|
841
898
|
// --- Task Bracket Tools ---
|
|
842
|
-
server.tool('memory_task_begin', `Signal that you're starting a significant task. Auto-checkpoints current state and recalls relevant memories.
|
|
843
|
-
|
|
844
|
-
CALL THIS when starting:
|
|
845
|
-
- A multi-step operation (doc generation, large refactor, migration)
|
|
846
|
-
- Work on a new topic or project area
|
|
847
|
-
- Anything that might fill the context window
|
|
848
|
-
|
|
899
|
+
server.tool('memory_task_begin', `Signal that you're starting a significant task. Auto-checkpoints current state and recalls relevant memories.
|
|
900
|
+
|
|
901
|
+
CALL THIS when starting:
|
|
902
|
+
- A multi-step operation (doc generation, large refactor, migration)
|
|
903
|
+
- Work on a new topic or project area
|
|
904
|
+
- Anything that might fill the context window
|
|
905
|
+
|
|
849
906
|
This ensures your state is saved before you start, and primes recall with relevant context.`, {
|
|
850
907
|
topic: z.string().describe('What task are you starting? (3-15 words)'),
|
|
851
908
|
files: z.array(z.string()).optional().default([])
|
|
@@ -896,13 +953,13 @@ This ensures your state is saved before you start, and primes recall with releva
|
|
|
896
953
|
}],
|
|
897
954
|
};
|
|
898
955
|
});
|
|
899
|
-
server.tool('memory_task_end', `Signal that you've finished a significant task. Writes a summary memory and auto-checkpoints.
|
|
900
|
-
|
|
901
|
-
CALL THIS when you finish:
|
|
902
|
-
- A multi-step operation
|
|
903
|
-
- Before switching to a different topic
|
|
904
|
-
- At the end of a work session
|
|
905
|
-
|
|
956
|
+
server.tool('memory_task_end', `Signal that you've finished a significant task. Writes a summary memory and auto-checkpoints.
|
|
957
|
+
|
|
958
|
+
CALL THIS when you finish:
|
|
959
|
+
- A multi-step operation
|
|
960
|
+
- Before switching to a different topic
|
|
961
|
+
- At the end of a work session
|
|
962
|
+
|
|
906
963
|
This captures what was accomplished so future sessions can recall it.`, {
|
|
907
964
|
summary: z.string().describe('What was accomplished? Include key outcomes, decisions, and any issues.'),
|
|
908
965
|
tags: z.array(z.string()).optional().default([])
|
|
@@ -971,18 +1028,21 @@ This captures what was accomplished so future sessions can recall it.`, {
|
|
|
971
1028
|
return {
|
|
972
1029
|
content: [{
|
|
973
1030
|
type: 'text',
|
|
974
|
-
|
|
1031
|
+
// D14 (2026-07-30): task end is the recipe moment — invite the host
|
|
1032
|
+
// to distill a skill and/or a failure lesson in separate focused
|
|
1033
|
+
// passes. The host owns the gates; AWM validates the write-backs.
|
|
1034
|
+
text: `Completed: "${completedTask}" [${salience.score.toFixed(2)}]${supersededNote}\n${renderTaskEndInvitation()}`,
|
|
975
1035
|
}],
|
|
976
1036
|
};
|
|
977
1037
|
});
|
|
978
|
-
server.tool('compress_output', `Compress a STRUCTURED tool output (JSON object/array, query rows, log records) into TOON —
|
|
979
|
-
a compact, schema-aware tabular encoding — before putting it in your context. Cuts ~50-65%
|
|
980
|
-
of the tokens on uniform arrays at zero comprehension cost (validated: models read TOON as
|
|
981
|
-
accurately as JSON). Use this on large tool results you need to keep in context.
|
|
982
|
-
|
|
983
|
-
Output-only and safe: it never changes the data. Non-JSON / prose is returned unchanged.
|
|
984
|
-
TOON is only emitted when it reproduces the input exactly (self-verified round-trip);
|
|
985
|
-
otherwise you get plain JSON back. When compressed, you also get a 'ref' — call
|
|
1038
|
+
server.tool('compress_output', `Compress a STRUCTURED tool output (JSON object/array, query rows, log records) into TOON —
|
|
1039
|
+
a compact, schema-aware tabular encoding — before putting it in your context. Cuts ~50-65%
|
|
1040
|
+
of the tokens on uniform arrays at zero comprehension cost (validated: models read TOON as
|
|
1041
|
+
accurately as JSON). Use this on large tool results you need to keep in context.
|
|
1042
|
+
|
|
1043
|
+
Output-only and safe: it never changes the data. Non-JSON / prose is returned unchanged.
|
|
1044
|
+
TOON is only emitted when it reproduces the input exactly (self-verified round-trip);
|
|
1045
|
+
otherwise you get plain JSON back. When compressed, you also get a 'ref' — call
|
|
986
1046
|
retrieve_original(ref) to get the verbatim source back if you ever need it.`, {
|
|
987
1047
|
output: z.string().describe('The tool output to compress — JSON text (preferred) or any string. Non-JSON is returned unchanged.'),
|
|
988
1048
|
min_saving_chars: z.number().optional().describe('Only emit TOON if it saves at least this many characters (default 40).'),
|
|
@@ -996,8 +1056,8 @@ retrieve_original(ref) to get the verbatim source back if you ever need it.`, {
|
|
|
996
1056
|
content: [{ type: 'text', text: header + r.text }],
|
|
997
1057
|
};
|
|
998
1058
|
});
|
|
999
|
-
server.tool('retrieve_original', `Retrieve the verbatim original text for a 'ref' returned by compress_output. Use this when
|
|
1000
|
-
you need the exact, uncompressed source (e.g. to pass it to another tool unchanged). Returns
|
|
1059
|
+
server.tool('retrieve_original', `Retrieve the verbatim original text for a 'ref' returned by compress_output. Use this when
|
|
1060
|
+
you need the exact, uncompressed source (e.g. to pass it to another tool unchanged). Returns
|
|
1001
1061
|
an error if the ref has expired (originals are kept for the most recent compressions only).`, {
|
|
1002
1062
|
ref: z.string().describe('The ref handle returned by compress_output (e.g. "awm_orig_12").'),
|
|
1003
1063
|
}, async (params) => {
|
|
@@ -1021,6 +1081,35 @@ an error if the ref has expired (originals are kept for the most recent compress
|
|
|
1021
1081
|
agentId: AGENT_ID,
|
|
1022
1082
|
secret: HOOK_SECRET,
|
|
1023
1083
|
port: HOOK_PORT,
|
|
1084
|
+
// 0.12.2: warm recall for hooks — the sidecar shares this process's
|
|
1085
|
+
// activation engine and loaded models, so a UserPromptSubmit hook can get
|
|
1086
|
+
// warm-latency recall without any standing server. Trimmed result shape
|
|
1087
|
+
// (no embeddings/phase scores — hooks don't need them and the vectors
|
|
1088
|
+
// alone would 10x the payload).
|
|
1089
|
+
activate: async (q) => {
|
|
1090
|
+
const results = await activationEngine.activate({
|
|
1091
|
+
agentId: AGENT_ID,
|
|
1092
|
+
context: q.context,
|
|
1093
|
+
limit: q.limit,
|
|
1094
|
+
requireConfidence: q.requireConfidence,
|
|
1095
|
+
granularity: q.granularity,
|
|
1096
|
+
});
|
|
1097
|
+
return results.map(r => ({
|
|
1098
|
+
engram: {
|
|
1099
|
+
id: r.engram.id,
|
|
1100
|
+
concept: r.engram.concept,
|
|
1101
|
+
content: r.engram.content,
|
|
1102
|
+
createdAt: r.engram.createdAt instanceof Date
|
|
1103
|
+
? r.engram.createdAt.toISOString()
|
|
1104
|
+
: r.engram.createdAt,
|
|
1105
|
+
memoryClass: r.engram.memoryClass,
|
|
1106
|
+
validTo: r.engram.validTo,
|
|
1107
|
+
},
|
|
1108
|
+
score: r.score,
|
|
1109
|
+
summary: r.summary,
|
|
1110
|
+
confidence: r.confidence,
|
|
1111
|
+
}));
|
|
1112
|
+
},
|
|
1024
1113
|
onConsolidate: async (agentId, reason) => {
|
|
1025
1114
|
console.error(`[mcp] consolidation triggered: ${reason}`);
|
|
1026
1115
|
const result = await consolidationEngine.consolidate(agentId);
|
|
@@ -1028,6 +1117,31 @@ an error if the ref has expired (originals are kept for the most recent compress
|
|
|
1028
1117
|
console.error(`[mcp] consolidation done: ${result.edgesStrengthened} strengthened, ${result.memoriesForgotten} forgotten`);
|
|
1029
1118
|
},
|
|
1030
1119
|
});
|
|
1120
|
+
// 0.12.2: eager warm — fire-and-forget, mirrors index.ts:208-218. Without
|
|
1121
|
+
// this, every Claude Code session paid the full cold cost (~3s measured on
|
|
1122
|
+
// a 29.7k-engram store: slim cache ~0.9s + three model loads ~1.8s) on its
|
|
1123
|
+
// FIRST recall, which is exactly the "first recall is slow → recall gets
|
|
1124
|
+
// avoided" failure mode. Warming here overlaps session startup instead.
|
|
1125
|
+
// All output is stderr-safe (stdout carries JSON-RPC frames).
|
|
1126
|
+
// Escape hatch: AWM_NO_EAGER_WARM=1 restores lazy loading.
|
|
1127
|
+
if (process.env.AWM_NO_EAGER_WARM !== '1') {
|
|
1128
|
+
getEmbedder().catch(err => console.error('Embedding model unavailable:', err.message));
|
|
1129
|
+
getReranker().catch(err => console.error('Reranker model unavailable:', err.message));
|
|
1130
|
+
getExpander().catch(err => console.error('Query expander model unavailable:', err.message));
|
|
1131
|
+
if (BACKEND === 'sqlite') {
|
|
1132
|
+
setImmediate(() => {
|
|
1133
|
+
try {
|
|
1134
|
+
const t0 = Date.now();
|
|
1135
|
+
store.warmSlimCache();
|
|
1136
|
+
const stats = store.getSlimCacheStats();
|
|
1137
|
+
console.error(`Slim cache warmed: ${stats.size} entries in ${Date.now() - t0}ms`);
|
|
1138
|
+
}
|
|
1139
|
+
catch (err) {
|
|
1140
|
+
console.error(`Slim cache warm failed: ${err.message}`);
|
|
1141
|
+
}
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1031
1145
|
// Coordination MCP tools (opt-in via AWM_COORDINATION=true)
|
|
1032
1146
|
// AWM 0.8.x: coordination requires SQLite (uses store.getDb()). On PGlite,
|
|
1033
1147
|
// coordination is auto-disabled with a warning; re-enable when coordination
|