agent-working-memory 0.10.0 → 0.12.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 +118 -19
- 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 +329 -302
- package/dist/adapters/common.js.map +1 -1
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +31 -8
- package/dist/api/routes.js.map +1 -1
- package/dist/cli/migrate.js +29 -29
- package/dist/cli.js +82 -2
- package/dist/cli.js.map +1 -1
- package/dist/coordination/circuit-breaker.js +23 -23
- 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 +3 -0
- 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/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/index.js +21 -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 +212 -97
- package/dist/mcp.js.map +1 -1
- package/dist/onboard/index.d.ts +68 -0
- package/dist/onboard/index.d.ts.map +1 -0
- package/dist/onboard/index.js +265 -0
- package/dist/onboard/index.js.map +1 -0
- 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 +170 -143
- 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 +44 -2
- 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/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +27 -0
- package/dist/version.js.map +1 -0
- package/package.json +9 -1
- package/src/adapters/claude-code.ts +66 -3
- package/src/adapters/common.ts +538 -511
- package/src/api/index.ts +3 -3
- package/src/api/routes.ts +999 -970
- package/src/cli/migrate.ts +307 -307
- package/src/cli.ts +77 -2
- package/src/coordination/circuit-breaker.ts +83 -83
- package/src/coordination/failure-modes.ts +50 -50
- package/src/coordination/routes.ts +2155 -2150
- package/src/core/decay.ts +63 -63
- package/src/core/embeddings.ts +113 -110
- package/src/core/entity-extract.ts +47 -0
- package/src/core/index.ts +5 -5
- package/src/core/logger.ts +36 -36
- package/src/core/ml-worker-entry.ts +194 -194
- package/src/core/ml-worker.ts +281 -281
- package/src/core/query-expander.ts +122 -122
- package/src/core/reranker.ts +119 -119
- 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/confidence.ts +120 -120
- package/src/engine/consolidation-scheduler.ts +242 -242
- package/src/engine/consolidation.ts +887 -869
- package/src/engine/eval.ts +107 -102
- package/src/engine/eviction.ts +101 -101
- package/src/engine/index.ts +8 -8
- package/src/engine/retraction.ts +366 -366
- package/src/engine/staging.ts +74 -74
- package/src/index.ts +248 -226
- package/src/mcp.ts +1341 -1211
- package/src/onboard/index.ts +298 -0
- package/src/recipes/index.ts +125 -0
- package/src/storage/index.ts +3 -3
- package/src/storage/pglite-schema.ts +193 -166
- package/src/storage/pglite.ts +1420 -1372
- package/src/storage/postgres.ts +50 -2
- package/src/storage/sqlite.ts +1936 -1861
- package/src/storage/store.ts +80 -80
- package/src/types/agent.ts +67 -67
- package/src/types/checkpoint.ts +46 -46
- package/src/types/engram.ts +22 -0
- package/src/types/eval.ts +100 -100
- package/src/types/index.ts +6 -6
- package/src/version.ts +26 -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
|
|
@@ -28,7 +29,7 @@
|
|
|
28
29
|
* Config: add to ~/.claude.json or .mcp.json
|
|
29
30
|
*/
|
|
30
31
|
import { readFileSync } from 'node:fs';
|
|
31
|
-
import { resolve } from 'node:path';
|
|
32
|
+
import { resolve, basename } from 'node:path';
|
|
32
33
|
import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
33
34
|
// Load .env file if present (no external dependency)
|
|
34
35
|
try {
|
|
@@ -69,15 +70,20 @@ import { DEFAULT_AGENT_CONFIG } from './types/agent.js';
|
|
|
69
70
|
import { embed } from './core/embeddings.js';
|
|
70
71
|
import { startSidecar } from './hooks/sidecar.js';
|
|
71
72
|
import { initLogger, log, getLogPath } from './core/logger.js';
|
|
73
|
+
import { VERSION } from './version.js';
|
|
74
|
+
import { buildPack, INTERVIEW_QUESTIONS } from './onboard/index.js';
|
|
72
75
|
import { liteCompress, retrieveOriginal } from './core/lite-compress.js';
|
|
73
76
|
import { queryPeerDecisions, formatPeerDecisions } from './coordination/peer-decisions.js';
|
|
77
|
+
import { startLoopLagMonitor } from './core/write-telemetry.js';
|
|
78
|
+
import { buildWhoami, formatWhoami } from './core/whoami.js';
|
|
79
|
+
import { renderTaskEndInvitation, validateRecipeWrite, recipeSlug, getRecipe } from './recipes/index.js';
|
|
74
80
|
// --- Incognito Mode ---
|
|
75
81
|
// When AWM_INCOGNITO=1, register zero tools. Claude won't see memory tools at all.
|
|
76
82
|
// No DB, no engines, no sidecar — just a bare MCP server that exposes nothing.
|
|
77
83
|
const INCOGNITO = process.env.AWM_INCOGNITO === '1' || process.env.AWM_INCOGNITO === 'true';
|
|
78
84
|
if (INCOGNITO) {
|
|
79
85
|
console.error('AWM: incognito mode — all memory tools disabled, nothing will be recorded');
|
|
80
|
-
const server = new McpServer({ name: 'agent-working-memory', version:
|
|
86
|
+
const server = new McpServer({ name: 'agent-working-memory', version: VERSION });
|
|
81
87
|
const transport = new StdioServerTransport();
|
|
82
88
|
server.connect(transport).catch(err => {
|
|
83
89
|
console.error('MCP server failed:', err);
|
|
@@ -122,11 +128,12 @@ else {
|
|
|
122
128
|
const consolidationScheduler = new ConsolidationScheduler(store, consolidationEngine);
|
|
123
129
|
stagingBuffer.start(DEFAULT_AGENT_CONFIG.stagingTtlMs);
|
|
124
130
|
consolidationScheduler.start();
|
|
131
|
+
startLoopLagMonitor();
|
|
125
132
|
// Coordination DB handle — set when AWM_COORDINATION=true, used by memory_write for decision propagation
|
|
126
133
|
let coordDb = null;
|
|
127
134
|
const server = new McpServer({
|
|
128
135
|
name: 'agent-working-memory',
|
|
129
|
-
version:
|
|
136
|
+
version: VERSION,
|
|
130
137
|
});
|
|
131
138
|
server.registerResource('awm-overview', 'awm://server/overview', {
|
|
132
139
|
title: 'AWM Overview',
|
|
@@ -186,15 +193,15 @@ else {
|
|
|
186
193
|
return 'unclassified';
|
|
187
194
|
}
|
|
188
195
|
// --- Tools ---
|
|
189
|
-
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.
|
|
190
|
-
|
|
191
|
-
CALL THIS PROACTIVELY — do not wait to be asked. Write memories when you:
|
|
192
|
-
- Discover something about the codebase, bugs, or architecture
|
|
193
|
-
- Make a decision and want to remember why
|
|
194
|
-
- Encounter and resolve an error
|
|
195
|
-
- Learn a user preference or project pattern
|
|
196
|
-
- Complete a significant piece of work
|
|
197
|
-
|
|
196
|
+
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.
|
|
197
|
+
|
|
198
|
+
CALL THIS PROACTIVELY — do not wait to be asked. Write memories when you:
|
|
199
|
+
- Discover something about the codebase, bugs, or architecture
|
|
200
|
+
- Make a decision and want to remember why
|
|
201
|
+
- Encounter and resolve an error
|
|
202
|
+
- Learn a user preference or project pattern
|
|
203
|
+
- Complete a significant piece of work
|
|
204
|
+
|
|
198
205
|
The concept should be a short label (3-8 words). The content should be the full detail.`, {
|
|
199
206
|
concept: z.string().describe('Short label for this memory (3-8 words)'),
|
|
200
207
|
content: z.string().describe('Full detail of what was learned'),
|
|
@@ -227,6 +234,14 @@ The concept should be a short label (3-8 words). The content should be the full
|
|
|
227
234
|
.describe('Confidence: verified (tested), observed (read in code), assumed (reasoning).'),
|
|
228
235
|
session_id: z.string().optional()
|
|
229
236
|
.describe('Session/conversation grouping ID. Memories with same session_id are associated.'),
|
|
237
|
+
origin_class: z.enum(['user-stated', 'tool-output', 'inference', 'recipe']).optional()
|
|
238
|
+
.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.'),
|
|
239
|
+
recipe_id: z.string().optional()
|
|
240
|
+
.describe('Cognition-recipe id+version when origin_class is recipe.'),
|
|
241
|
+
valid_from: z.string().optional()
|
|
242
|
+
.describe('ISO date when the FACT becomes valid (temporal validity, not ingestion time).'),
|
|
243
|
+
valid_to: z.string().optional()
|
|
244
|
+
.describe('ISO date when the FACT stops being valid (e.g., a deadline or a superseding change).'),
|
|
230
245
|
intent: z.enum(['decision', 'question', 'todo', 'finding', 'context']).optional()
|
|
231
246
|
.describe('What kind of memory this is.'),
|
|
232
247
|
}, async (params) => {
|
|
@@ -245,6 +260,37 @@ The concept should be a short label (3-8 words). The content should be the full
|
|
|
245
260
|
metaTags.push(`sid=${params.session_id}`);
|
|
246
261
|
if (params.intent)
|
|
247
262
|
metaTags.push(`intent=${params.intent}`);
|
|
263
|
+
// D14 (2026-07-30): recipe write-backs are contract-checked. Provenance
|
|
264
|
+
// must never claim a recipe that does not exist, and malformed
|
|
265
|
+
// derivations are rejected with the contract echoed back so the host
|
|
266
|
+
// can self-correct in one retry.
|
|
267
|
+
if (params.origin_class === 'recipe') {
|
|
268
|
+
if (!params.recipe_id) {
|
|
269
|
+
return { content: [{ type: 'text', text: "Recipe write rejected: origin_class 'recipe' requires recipe_id (e.g. 'skill-derivation@1')." }] };
|
|
270
|
+
}
|
|
271
|
+
const v = validateRecipeWrite(params.recipe_id, params.concept, params.content);
|
|
272
|
+
if (!v.ok) {
|
|
273
|
+
const contract = getRecipe(params.recipe_id)?.writeBack ?? 'unknown recipe';
|
|
274
|
+
return { content: [{ type: 'text', text: `Recipe write rejected (${params.recipe_id}): ${v.errors.join('; ')}\nContract: ${contract}` }] };
|
|
275
|
+
}
|
|
276
|
+
// Standardize recipe write-backs: canonical class, standard tags.
|
|
277
|
+
const slug = recipeSlug(params.concept);
|
|
278
|
+
const ensure = (tag) => { if (!userTags.includes(tag) && !metaTags.includes(tag))
|
|
279
|
+
metaTags.push(tag); };
|
|
280
|
+
if (params.recipe_id.startsWith('skill-derivation')) {
|
|
281
|
+
ensure('topic=skill');
|
|
282
|
+
ensure(`skill=${slug}`);
|
|
283
|
+
params.memory_type = params.memory_type ?? 'procedural';
|
|
284
|
+
}
|
|
285
|
+
else if (params.recipe_id.startsWith('friction-lesson')) {
|
|
286
|
+
ensure('topic=friction');
|
|
287
|
+
ensure(`about=${slug}`);
|
|
288
|
+
// zod defaults event_type to 'observation', so force the recipe's
|
|
289
|
+
// contract value rather than ??-guarding against undefined.
|
|
290
|
+
params.event_type = 'friction';
|
|
291
|
+
}
|
|
292
|
+
params.memory_class = 'canonical';
|
|
293
|
+
}
|
|
248
294
|
const memoryType = params.memory_type ?? classifyMemoryType(params.content);
|
|
249
295
|
const result = await performWrite({ store, connectionEngine }, {
|
|
250
296
|
agentId: AGENT_ID,
|
|
@@ -259,6 +305,11 @@ The concept should be a short label (3-8 words). The content should be the full
|
|
|
259
305
|
memoryClass: params.memory_class,
|
|
260
306
|
memoryType,
|
|
261
307
|
supersedes: params.supersedes,
|
|
308
|
+
originClass: params.origin_class,
|
|
309
|
+
writerSession: params.session_id,
|
|
310
|
+
recipeId: params.recipe_id,
|
|
311
|
+
validFrom: params.valid_from,
|
|
312
|
+
validTo: params.valid_to,
|
|
262
313
|
});
|
|
263
314
|
// Auto-checkpoint — covers create/reinforce/supersede uniformly
|
|
264
315
|
try {
|
|
@@ -294,19 +345,22 @@ The concept should be a short label (3-8 words). The content should be the full
|
|
|
294
345
|
return {
|
|
295
346
|
content: [{
|
|
296
347
|
type: 'text',
|
|
297
|
-
text: `Stored (${salience.disposition}) "${params.concept}" [${salience.score.toFixed(2)}]\nID: ${engram.id}
|
|
348
|
+
text: `Stored (${salience.disposition}) "${params.concept}" [${salience.score.toFixed(2)}]\nID: ${engram.id}`
|
|
349
|
+
+ (isLowSalience
|
|
350
|
+
? `\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))})`
|
|
351
|
+
: ''),
|
|
298
352
|
}],
|
|
299
353
|
};
|
|
300
354
|
});
|
|
301
|
-
server.tool('memory_recall', `Recall memories relevant to a query. Uses cognitive activation — not keyword search.
|
|
302
|
-
|
|
303
|
-
ALWAYS call this when:
|
|
304
|
-
- Starting work on a project or topic (recall what you know)
|
|
305
|
-
- Debugging (recall similar errors and solutions)
|
|
306
|
-
- Making decisions (recall past decisions and outcomes)
|
|
307
|
-
- The user mentions a topic you might have stored memories about
|
|
308
|
-
|
|
309
|
-
Accepts either "query" or "context" parameter — both work identically.
|
|
355
|
+
server.tool('memory_recall', `Recall memories relevant to a query. Uses cognitive activation — not keyword search.
|
|
356
|
+
|
|
357
|
+
ALWAYS call this when:
|
|
358
|
+
- Starting work on a project or topic (recall what you know)
|
|
359
|
+
- Debugging (recall similar errors and solutions)
|
|
360
|
+
- Making decisions (recall past decisions and outcomes)
|
|
361
|
+
- The user mentions a topic you might have stored memories about
|
|
362
|
+
|
|
363
|
+
Accepts either "query" or "context" parameter — both work identically.
|
|
310
364
|
Returns the most relevant memories ranked by text relevance, temporal recency, and associative strength.`, {
|
|
311
365
|
query: z.string().optional().describe('What to search for — describe the situation, question, or topic'),
|
|
312
366
|
context: z.string().optional().describe('Alias for query (either works)'),
|
|
@@ -368,7 +422,16 @@ Returns the most relevant memories ranked by text relevance, temporal recency, a
|
|
|
368
422
|
// requested 'compact' or 'auto' granularity, surface the engine-computed
|
|
369
423
|
// summary instead of the full content — same engram, less to read.
|
|
370
424
|
const body = r.summary ?? r.engram.content;
|
|
371
|
-
|
|
425
|
+
// D8 (2026-07-30): conflict surfacing — a superseded memory that still
|
|
426
|
+
// ranks is shown WITH its replacement pointer instead of silently
|
|
427
|
+
// down-ranked. The model should trust the successor.
|
|
428
|
+
const chain = r.engram.supersededBy
|
|
429
|
+
? ` ⚠ SUPERSEDED by ${r.engram.supersededBy} — treat as historical; recall/fetch the successor before relying on this.`
|
|
430
|
+
: '';
|
|
431
|
+
const validity = r.engram.validTo
|
|
432
|
+
? ` [valid until ${r.engram.validTo}]`
|
|
433
|
+
: '';
|
|
434
|
+
return `${i + 1}. **${r.engram.concept}** (${r.score.toFixed(3)})${validity}: ${body}${chain}`;
|
|
372
435
|
});
|
|
373
436
|
return {
|
|
374
437
|
content: [{
|
|
@@ -377,8 +440,8 @@ Returns the most relevant memories ranked by text relevance, temporal recency, a
|
|
|
377
440
|
}],
|
|
378
441
|
};
|
|
379
442
|
});
|
|
380
|
-
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.
|
|
381
|
-
|
|
443
|
+
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.
|
|
444
|
+
|
|
382
445
|
Always call this after using a recalled memory so the system learns what's valuable.`, {
|
|
383
446
|
engram_id: z.string().describe('ID of the memory (from memory_recall results)'),
|
|
384
447
|
useful: z.boolean().describe('Was this memory actually helpful?'),
|
|
@@ -401,8 +464,8 @@ Always call this after using a recalled memory so the system learns what's valua
|
|
|
401
464
|
}],
|
|
402
465
|
};
|
|
403
466
|
});
|
|
404
|
-
server.tool('memory_retract', `Retract a memory that turned out to be wrong. Creates a correction and reduces confidence of related memories.
|
|
405
|
-
|
|
467
|
+
server.tool('memory_retract', `Retract a memory that turned out to be wrong. Creates a correction and reduces confidence of related memories.
|
|
468
|
+
|
|
406
469
|
Use this when you discover a memory contains incorrect information.`, {
|
|
407
470
|
engram_id: z.string().describe('ID of the wrong memory'),
|
|
408
471
|
reason: z.string().describe('Why is this memory wrong?'),
|
|
@@ -426,13 +489,13 @@ Use this when you discover a memory contains incorrect information.`, {
|
|
|
426
489
|
}],
|
|
427
490
|
};
|
|
428
491
|
});
|
|
429
|
-
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.
|
|
430
|
-
|
|
431
|
-
Use this when:
|
|
432
|
-
- A status or count has changed (e.g., "5 reviews done" → "7 reviews done")
|
|
433
|
-
- Architecture or infrastructure evolved (e.g., "two-repo model" → "three-repo model")
|
|
434
|
-
- A schedule or plan was updated
|
|
435
|
-
|
|
492
|
+
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.
|
|
493
|
+
|
|
494
|
+
Use this when:
|
|
495
|
+
- A status or count has changed (e.g., "5 reviews done" → "7 reviews done")
|
|
496
|
+
- Architecture or infrastructure evolved (e.g., "two-repo model" → "three-repo model")
|
|
497
|
+
- A schedule or plan was updated
|
|
498
|
+
|
|
436
499
|
The old memory stays in the database (searchable for history) but is heavily down-ranked in recall so the current version dominates.`, {
|
|
437
500
|
old_engram_id: z.string().describe('ID of the outdated memory'),
|
|
438
501
|
new_engram_id: z.string().describe('ID of the replacement memory'),
|
|
@@ -459,7 +522,11 @@ The old memory stays in the database (searchable for history) but is heavily dow
|
|
|
459
522
|
}],
|
|
460
523
|
};
|
|
461
524
|
});
|
|
462
|
-
server.tool('
|
|
525
|
+
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 () => {
|
|
526
|
+
const info = await buildWhoami(store, AGENT_ID, 'mcp');
|
|
527
|
+
return { content: [{ type: 'text', text: formatWhoami(info) }] };
|
|
528
|
+
});
|
|
529
|
+
server.tool('memory_stats', `Get memory health stats — how many memories, confidence levels, association count, and system performance.
|
|
463
530
|
Also shows the activity log path so the user can tail it to see what's happening.`, {}, async () => {
|
|
464
531
|
const metrics = await evalEngine.computeMetrics(AGENT_ID);
|
|
465
532
|
const checkpoint = await store.getCheckpoint(AGENT_ID);
|
|
@@ -490,13 +557,13 @@ Also shows the activity log path so the user can tail it to see what's happening
|
|
|
490
557
|
};
|
|
491
558
|
});
|
|
492
559
|
// --- Checkpointing Tools ---
|
|
493
|
-
server.tool('memory_checkpoint', `Save your current execution state so you can recover after context compaction.
|
|
494
|
-
|
|
495
|
-
ALWAYS call this before:
|
|
496
|
-
- Long operations (multi-file generation, large refactors, overnight work)
|
|
497
|
-
- Anything that might fill the context window
|
|
498
|
-
- Switching to a different task
|
|
499
|
-
|
|
560
|
+
server.tool('memory_checkpoint', `Save your current execution state so you can recover after context compaction.
|
|
561
|
+
|
|
562
|
+
ALWAYS call this before:
|
|
563
|
+
- Long operations (multi-file generation, large refactors, overnight work)
|
|
564
|
+
- Anything that might fill the context window
|
|
565
|
+
- Switching to a different task
|
|
566
|
+
|
|
500
567
|
Also call periodically during long sessions to avoid losing state. The state is saved per-agent and overwrites any previous checkpoint.`, {
|
|
501
568
|
current_task: z.string().describe('What you are currently working on'),
|
|
502
569
|
decisions: z.array(z.string()).optional().default([])
|
|
@@ -530,16 +597,25 @@ Also call periodically during long sessions to avoid losing state. The state is
|
|
|
530
597
|
}],
|
|
531
598
|
};
|
|
532
599
|
});
|
|
533
|
-
server.tool('memory_restore', `Restore your previous execution state after context compaction or at session start.
|
|
534
|
-
|
|
535
|
-
Returns:
|
|
536
|
-
- Your saved execution state (task, decisions, next steps, files)
|
|
537
|
-
- Recently recalled memories for context
|
|
538
|
-
- Your last write for continuity
|
|
539
|
-
- How long you were idle
|
|
540
|
-
|
|
600
|
+
server.tool('memory_restore', `Restore your previous execution state after context compaction or at session start.
|
|
601
|
+
|
|
602
|
+
Returns:
|
|
603
|
+
- Your saved execution state (task, decisions, next steps, files)
|
|
604
|
+
- Recently recalled memories for context
|
|
605
|
+
- Your last write for continuity
|
|
606
|
+
- How long you were idle
|
|
607
|
+
|
|
541
608
|
Use this at the start of every session or after compaction to pick up where you left off.`, {}, async () => {
|
|
542
609
|
const checkpoint = await store.getCheckpoint(AGENT_ID);
|
|
610
|
+
// Cold-store nudge: an empty store means the agent has nothing to recall — offer to warm-start.
|
|
611
|
+
let coldStoreNudge = '';
|
|
612
|
+
try {
|
|
613
|
+
const activeCount = (await store.getEngramsByAgent(AGENT_ID)).length;
|
|
614
|
+
if (activeCount < 3) {
|
|
615
|
+
coldStoreNudge = `🌱 **This memory store is nearly empty (${activeCount} ${activeCount === 1 ? 'memory' : 'memories'}).** Warm-start it before other work: recall the "onboard a new project" skill and follow it — or call \`onboard_scan\` on this project's docs/repo, refine the results, and save them with \`memory_write\` (canonical). Recall becomes useful immediately.`;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
catch { /* count is best-effort */ }
|
|
543
619
|
const now = Date.now();
|
|
544
620
|
const idleMs = checkpoint
|
|
545
621
|
? now - checkpoint.auto.lastActivityAt.getTime()
|
|
@@ -614,6 +690,8 @@ Use this at the start of every session or after compaction to pick up where you
|
|
|
614
690
|
: '';
|
|
615
691
|
log(AGENT_ID, 'restore', `idle=${idleMin}min checkpoint=${!!checkpoint?.executionState} recalled=${recalledMemories.length} lastWrite=${lastWrite?.concept ?? 'none'}${fullConsolidationTriggered ? ' FULL_CONSOLIDATION' : ''}`);
|
|
616
692
|
parts.push(`Idle: ${idleMin}min${consolidationNote}`);
|
|
693
|
+
if (coldStoreNudge)
|
|
694
|
+
parts.push(`\n${coldStoreNudge}`);
|
|
617
695
|
if (checkpoint?.executionState) {
|
|
618
696
|
const s = checkpoint.executionState;
|
|
619
697
|
parts.push(`\n**Current task:** ${s.currentTask}`);
|
|
@@ -645,9 +723,9 @@ Use this at the start of every session or after compaction to pick up where you
|
|
|
645
723
|
if (coordDb) {
|
|
646
724
|
try {
|
|
647
725
|
const myAgent = coordDb.prepare(`SELECT id FROM coord_agents WHERE name = ? AND status != 'dead' ORDER BY last_seen DESC LIMIT 1`).get(AGENT_ID);
|
|
648
|
-
const peerDecisions = coordDb.prepare(`SELECT d.summary, a.name AS author_name, d.created_at
|
|
649
|
-
FROM coord_decisions d JOIN coord_agents a ON d.author_id = a.id
|
|
650
|
-
WHERE d.author_id != ? AND d.created_at > datetime('now', '-30 minutes')
|
|
726
|
+
const peerDecisions = coordDb.prepare(`SELECT d.summary, a.name AS author_name, d.created_at
|
|
727
|
+
FROM coord_decisions d JOIN coord_agents a ON d.author_id = a.id
|
|
728
|
+
WHERE d.author_id != ? AND d.created_at > datetime('now', '-30 minutes')
|
|
651
729
|
ORDER BY d.created_at DESC LIMIT 10`).all(myAgent?.id ?? '');
|
|
652
730
|
if (peerDecisions.length > 0) {
|
|
653
731
|
parts.push(`\n**Peer decisions (last 30 min):**`);
|
|
@@ -665,14 +743,48 @@ Use this at the start of every session or after compaction to pick up where you
|
|
|
665
743
|
}],
|
|
666
744
|
};
|
|
667
745
|
});
|
|
746
|
+
// --- Onboarding Tools (warm-start a cold store) ---
|
|
747
|
+
server.tool('onboard_scan', `Scan a project's documentation + repository and return CANDIDATE memories to seed a cold store.
|
|
748
|
+
|
|
749
|
+
Use this when the store is empty / you're new to a project. The scan is deterministic
|
|
750
|
+
(real file contents, not guesses) — YOUR job is to refine the candidates into atomic,
|
|
751
|
+
recall-shaped memories, run the interview (onboard_questions), confirm with the user, then
|
|
752
|
+
save the good ones with memory_write (memory_class="canonical"). Nothing is saved by this tool.`, {
|
|
753
|
+
docs: z.array(z.string()).optional()
|
|
754
|
+
.describe('Doc files/dirs to scan (Markdown/text). Defaults to the repo (or cwd).'),
|
|
755
|
+
repo: z.string().optional()
|
|
756
|
+
.describe('Repo root — also derives stack (package.json) + layout memories.'),
|
|
757
|
+
project: z.string().optional()
|
|
758
|
+
.describe('Project name (becomes a tag). Defaults to the repo/dir name.'),
|
|
759
|
+
purpose: z.string().optional()
|
|
760
|
+
.describe('The goal of this memory system, if known — becomes the anchor memory.'),
|
|
761
|
+
}, async (params) => {
|
|
762
|
+
const repo = params.repo;
|
|
763
|
+
const docs = params.docs && params.docs.length ? params.docs : [repo ?? process.cwd()];
|
|
764
|
+
const project = params.project ?? basename(resolve(repo ?? docs[0] ?? process.cwd()));
|
|
765
|
+
const pack = buildPack({ docs, repo, project, agentId: AGENT_ID, purpose: params.purpose });
|
|
766
|
+
log(AGENT_ID, 'onboard', `scan ${docs.join(',')}${repo ? ' +repo' : ''} → ${pack.memories.length} candidates`);
|
|
767
|
+
const text = [
|
|
768
|
+
`Scanned ${docs.join(', ')}${repo ? ` (+repo ${repo})` : ''} → ${pack.memories.length} CANDIDATE memories (NOT saved).`,
|
|
769
|
+
`Next: refine each into an atomic memory (lead with the fact + identifiers), run onboard_questions,`,
|
|
770
|
+
`confirm with the user, then save the good ones with memory_write (memory_class="canonical").`,
|
|
771
|
+
``,
|
|
772
|
+
JSON.stringify({ project, candidates: pack.memories, questions: pack.questions }, null, 2),
|
|
773
|
+
].join('\n');
|
|
774
|
+
return { content: [{ type: 'text', text }] };
|
|
775
|
+
});
|
|
776
|
+
server.tool('onboard_questions', `Return the onboarding interview questions. Ask the user ONE at a time, starting with the
|
|
777
|
+
goal of the memory system, and ask follow-ups for clarity. Turn each answer into a canonical memory.`, {}, async () => ({
|
|
778
|
+
content: [{ type: 'text', text: INTERVIEW_QUESTIONS.map((q, i) => `${i + 1}. ${q}`).join('\n') }],
|
|
779
|
+
}));
|
|
668
780
|
// --- Task Management Tools ---
|
|
669
|
-
server.tool('memory_task_add', `Create a task that you need to come back to. Tasks are memories with status and priority tracking.
|
|
670
|
-
|
|
671
|
-
Use this when:
|
|
672
|
-
- You identify work that needs doing but can't do it right now
|
|
673
|
-
- The user mentions something to do later
|
|
674
|
-
- You want to park a sub-task while focusing on something more urgent
|
|
675
|
-
|
|
781
|
+
server.tool('memory_task_add', `Create a task that you need to come back to. Tasks are memories with status and priority tracking.
|
|
782
|
+
|
|
783
|
+
Use this when:
|
|
784
|
+
- You identify work that needs doing but can't do it right now
|
|
785
|
+
- The user mentions something to do later
|
|
786
|
+
- You want to park a sub-task while focusing on something more urgent
|
|
787
|
+
|
|
676
788
|
Tasks automatically get high salience so they won't be discarded.`, {
|
|
677
789
|
concept: z.string().describe('Short task title (3-10 words)'),
|
|
678
790
|
content: z.string().describe('Full task description — what needs doing, context, acceptance criteria'),
|
|
@@ -712,11 +824,11 @@ Tasks automatically get high salience so they won't be discarded.`, {
|
|
|
712
824
|
}],
|
|
713
825
|
};
|
|
714
826
|
});
|
|
715
|
-
server.tool('memory_task_update', `Update a task's status or priority. Use this to:
|
|
716
|
-
- Start working on a task (open → in_progress)
|
|
717
|
-
- Mark a task done (→ done)
|
|
718
|
-
- Block a task on another (→ blocked)
|
|
719
|
-
- Reprioritize (change priority)
|
|
827
|
+
server.tool('memory_task_update', `Update a task's status or priority. Use this to:
|
|
828
|
+
- Start working on a task (open → in_progress)
|
|
829
|
+
- Mark a task done (→ done)
|
|
830
|
+
- Block a task on another (→ blocked)
|
|
831
|
+
- Reprioritize (change priority)
|
|
720
832
|
- Unblock a task (clear blocked_by)`, {
|
|
721
833
|
task_id: z.string().describe('ID of the task to update'),
|
|
722
834
|
status: z.enum(['open', 'in_progress', 'blocked', 'done']).optional()
|
|
@@ -746,8 +858,8 @@ Tasks automatically get high salience so they won't be discarded.`, {
|
|
|
746
858
|
}],
|
|
747
859
|
};
|
|
748
860
|
});
|
|
749
|
-
server.tool('memory_task_list', `List tasks with optional status filter. Shows tasks ordered by priority (urgent first).
|
|
750
|
-
|
|
861
|
+
server.tool('memory_task_list', `List tasks with optional status filter. Shows tasks ordered by priority (urgent first).
|
|
862
|
+
|
|
751
863
|
Use at the start of a session to see what's pending, or to check blocked/done tasks.`, {
|
|
752
864
|
status: z.enum(['open', 'in_progress', 'blocked', 'done']).optional()
|
|
753
865
|
.describe('Filter by status (omit to see all active tasks)'),
|
|
@@ -773,10 +885,10 @@ Use at the start of a session to see what's pending, or to check blocked/done ta
|
|
|
773
885
|
}],
|
|
774
886
|
};
|
|
775
887
|
});
|
|
776
|
-
server.tool('memory_task_next', `Get the single most important task to work on next.
|
|
777
|
-
|
|
778
|
-
Prioritizes: in_progress tasks first (finish what you started), then by priority level, then oldest first. Skips blocked and done tasks.
|
|
779
|
-
|
|
888
|
+
server.tool('memory_task_next', `Get the single most important task to work on next.
|
|
889
|
+
|
|
890
|
+
Prioritizes: in_progress tasks first (finish what you started), then by priority level, then oldest first. Skips blocked and done tasks.
|
|
891
|
+
|
|
780
892
|
Use this when you finish a task or need to decide what to do next.`, {}, async () => {
|
|
781
893
|
const next = await store.getNextTask(AGENT_ID);
|
|
782
894
|
if (!next) {
|
|
@@ -792,13 +904,13 @@ Use this when you finish a task or need to decide what to do next.`, {}, async (
|
|
|
792
904
|
};
|
|
793
905
|
});
|
|
794
906
|
// --- Task Bracket Tools ---
|
|
795
|
-
server.tool('memory_task_begin', `Signal that you're starting a significant task. Auto-checkpoints current state and recalls relevant memories.
|
|
796
|
-
|
|
797
|
-
CALL THIS when starting:
|
|
798
|
-
- A multi-step operation (doc generation, large refactor, migration)
|
|
799
|
-
- Work on a new topic or project area
|
|
800
|
-
- Anything that might fill the context window
|
|
801
|
-
|
|
907
|
+
server.tool('memory_task_begin', `Signal that you're starting a significant task. Auto-checkpoints current state and recalls relevant memories.
|
|
908
|
+
|
|
909
|
+
CALL THIS when starting:
|
|
910
|
+
- A multi-step operation (doc generation, large refactor, migration)
|
|
911
|
+
- Work on a new topic or project area
|
|
912
|
+
- Anything that might fill the context window
|
|
913
|
+
|
|
802
914
|
This ensures your state is saved before you start, and primes recall with relevant context.`, {
|
|
803
915
|
topic: z.string().describe('What task are you starting? (3-15 words)'),
|
|
804
916
|
files: z.array(z.string()).optional().default([])
|
|
@@ -849,13 +961,13 @@ This ensures your state is saved before you start, and primes recall with releva
|
|
|
849
961
|
}],
|
|
850
962
|
};
|
|
851
963
|
});
|
|
852
|
-
server.tool('memory_task_end', `Signal that you've finished a significant task. Writes a summary memory and auto-checkpoints.
|
|
853
|
-
|
|
854
|
-
CALL THIS when you finish:
|
|
855
|
-
- A multi-step operation
|
|
856
|
-
- Before switching to a different topic
|
|
857
|
-
- At the end of a work session
|
|
858
|
-
|
|
964
|
+
server.tool('memory_task_end', `Signal that you've finished a significant task. Writes a summary memory and auto-checkpoints.
|
|
965
|
+
|
|
966
|
+
CALL THIS when you finish:
|
|
967
|
+
- A multi-step operation
|
|
968
|
+
- Before switching to a different topic
|
|
969
|
+
- At the end of a work session
|
|
970
|
+
|
|
859
971
|
This captures what was accomplished so future sessions can recall it.`, {
|
|
860
972
|
summary: z.string().describe('What was accomplished? Include key outcomes, decisions, and any issues.'),
|
|
861
973
|
tags: z.array(z.string()).optional().default([])
|
|
@@ -924,18 +1036,21 @@ This captures what was accomplished so future sessions can recall it.`, {
|
|
|
924
1036
|
return {
|
|
925
1037
|
content: [{
|
|
926
1038
|
type: 'text',
|
|
927
|
-
|
|
1039
|
+
// D14 (2026-07-30): task end is the recipe moment — invite the host
|
|
1040
|
+
// to distill a skill and/or a failure lesson in separate focused
|
|
1041
|
+
// passes. The host owns the gates; AWM validates the write-backs.
|
|
1042
|
+
text: `Completed: "${completedTask}" [${salience.score.toFixed(2)}]${supersededNote}\n${renderTaskEndInvitation()}`,
|
|
928
1043
|
}],
|
|
929
1044
|
};
|
|
930
1045
|
});
|
|
931
|
-
server.tool('compress_output', `Compress a STRUCTURED tool output (JSON object/array, query rows, log records) into TOON —
|
|
932
|
-
a compact, schema-aware tabular encoding — before putting it in your context. Cuts ~50-65%
|
|
933
|
-
of the tokens on uniform arrays at zero comprehension cost (validated: models read TOON as
|
|
934
|
-
accurately as JSON). Use this on large tool results you need to keep in context.
|
|
935
|
-
|
|
936
|
-
Output-only and safe: it never changes the data. Non-JSON / prose is returned unchanged.
|
|
937
|
-
TOON is only emitted when it reproduces the input exactly (self-verified round-trip);
|
|
938
|
-
otherwise you get plain JSON back. When compressed, you also get a 'ref' — call
|
|
1046
|
+
server.tool('compress_output', `Compress a STRUCTURED tool output (JSON object/array, query rows, log records) into TOON —
|
|
1047
|
+
a compact, schema-aware tabular encoding — before putting it in your context. Cuts ~50-65%
|
|
1048
|
+
of the tokens on uniform arrays at zero comprehension cost (validated: models read TOON as
|
|
1049
|
+
accurately as JSON). Use this on large tool results you need to keep in context.
|
|
1050
|
+
|
|
1051
|
+
Output-only and safe: it never changes the data. Non-JSON / prose is returned unchanged.
|
|
1052
|
+
TOON is only emitted when it reproduces the input exactly (self-verified round-trip);
|
|
1053
|
+
otherwise you get plain JSON back. When compressed, you also get a 'ref' — call
|
|
939
1054
|
retrieve_original(ref) to get the verbatim source back if you ever need it.`, {
|
|
940
1055
|
output: z.string().describe('The tool output to compress — JSON text (preferred) or any string. Non-JSON is returned unchanged.'),
|
|
941
1056
|
min_saving_chars: z.number().optional().describe('Only emit TOON if it saves at least this many characters (default 40).'),
|
|
@@ -949,8 +1064,8 @@ retrieve_original(ref) to get the verbatim source back if you ever need it.`, {
|
|
|
949
1064
|
content: [{ type: 'text', text: header + r.text }],
|
|
950
1065
|
};
|
|
951
1066
|
});
|
|
952
|
-
server.tool('retrieve_original', `Retrieve the verbatim original text for a 'ref' returned by compress_output. Use this when
|
|
953
|
-
you need the exact, uncompressed source (e.g. to pass it to another tool unchanged). Returns
|
|
1067
|
+
server.tool('retrieve_original', `Retrieve the verbatim original text for a 'ref' returned by compress_output. Use this when
|
|
1068
|
+
you need the exact, uncompressed source (e.g. to pass it to another tool unchanged). Returns
|
|
954
1069
|
an error if the ref has expired (originals are kept for the most recent compressions only).`, {
|
|
955
1070
|
ref: z.string().describe('The ref handle returned by compress_output (e.g. "awm_orig_12").'),
|
|
956
1071
|
}, async (params) => {
|