agent-working-memory 0.17.0 → 0.18.1
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 +321 -319
- package/dist/adapters/common.d.ts +34 -0
- package/dist/adapters/common.d.ts.map +1 -1
- package/dist/adapters/common.js +528 -446
- package/dist/adapters/common.js.map +1 -1
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +19 -0
- package/dist/api/routes.js.map +1 -1
- package/dist/cli/migrate.d.ts.map +1 -1
- package/dist/cli/migrate.js +31 -29
- package/dist/cli/migrate.js.map +1 -1
- package/dist/core/asked-value.d.ts +37 -0
- package/dist/core/asked-value.d.ts.map +1 -0
- package/dist/core/asked-value.js +77 -0
- package/dist/core/asked-value.js.map +1 -0
- package/dist/core/conflicts.d.ts +89 -0
- package/dist/core/conflicts.d.ts.map +1 -0
- package/dist/core/conflicts.js +267 -0
- package/dist/core/conflicts.js.map +1 -0
- package/dist/core/embeddings.d.ts +14 -2
- package/dist/core/embeddings.d.ts.map +1 -1
- package/dist/core/embeddings.js +32 -10
- package/dist/core/embeddings.js.map +1 -1
- package/dist/core/format-recall.d.ts +21 -1
- package/dist/core/format-recall.d.ts.map +1 -1
- package/dist/core/format-recall.js +60 -5
- package/dist/core/format-recall.js.map +1 -1
- package/dist/core/ml-worker.d.ts +24 -1
- package/dist/core/ml-worker.d.ts.map +1 -1
- package/dist/core/ml-worker.js +165 -4
- package/dist/core/ml-worker.js.map +1 -1
- package/dist/core/model-daemon-client.d.ts +77 -0
- package/dist/core/model-daemon-client.d.ts.map +1 -0
- package/dist/core/model-daemon-client.js +383 -0
- package/dist/core/model-daemon-client.js.map +1 -0
- package/dist/core/model-daemon-server.d.ts +71 -0
- package/dist/core/model-daemon-server.d.ts.map +1 -0
- package/dist/core/model-daemon-server.js +363 -0
- package/dist/core/model-daemon-server.js.map +1 -0
- package/dist/core/model-daemon.d.ts +137 -0
- package/dist/core/model-daemon.d.ts.map +1 -0
- package/dist/core/model-daemon.js +231 -0
- package/dist/core/model-daemon.js.map +1 -0
- package/dist/core/query-expander.d.ts +5 -0
- package/dist/core/query-expander.d.ts.map +1 -1
- package/dist/core/query-expander.js +3 -2
- package/dist/core/query-expander.js.map +1 -1
- package/dist/core/recall-config.d.ts +1 -1
- package/dist/core/recall-config.d.ts.map +1 -1
- package/dist/core/recall-config.js +5 -0
- package/dist/core/recall-config.js.map +1 -1
- package/dist/core/rerank-window.d.ts +7 -2
- package/dist/core/rerank-window.d.ts.map +1 -1
- package/dist/core/rerank-window.js +9 -4
- package/dist/core/rerank-window.js.map +1 -1
- package/dist/core/rerank2.d.ts +52 -2
- package/dist/core/rerank2.d.ts.map +1 -1
- package/dist/core/rerank2.js +60 -3
- package/dist/core/rerank2.js.map +1 -1
- package/dist/core/reranker.d.ts +19 -0
- package/dist/core/reranker.d.ts.map +1 -1
- package/dist/core/reranker.js +76 -12
- package/dist/core/reranker.js.map +1 -1
- package/dist/core/trust.d.ts +32 -0
- package/dist/core/trust.d.ts.map +1 -0
- package/dist/core/trust.js +66 -0
- package/dist/core/trust.js.map +1 -0
- package/dist/core/validity.d.ts +21 -0
- package/dist/core/validity.d.ts.map +1 -0
- package/dist/core/validity.js +41 -0
- package/dist/core/validity.js.map +1 -0
- package/dist/core/whoami.d.ts +3 -0
- package/dist/core/whoami.d.ts.map +1 -1
- package/dist/core/whoami.js +14 -0
- package/dist/core/whoami.js.map +1 -1
- package/dist/core/write-pipeline.d.ts +48 -0
- package/dist/core/write-pipeline.d.ts.map +1 -1
- package/dist/core/write-pipeline.js +80 -3
- package/dist/core/write-pipeline.js.map +1 -1
- package/dist/engine/activation.d.ts.map +1 -1
- package/dist/engine/activation.js +27 -4
- package/dist/engine/activation.js.map +1 -1
- package/dist/engine/consolidation.d.ts.map +1 -1
- package/dist/engine/consolidation.js +53 -4
- package/dist/engine/consolidation.js.map +1 -1
- package/dist/hooks/prime-query.d.ts +57 -0
- package/dist/hooks/prime-query.d.ts.map +1 -0
- package/dist/hooks/prime-query.js +388 -0
- package/dist/hooks/prime-query.js.map +1 -0
- package/dist/hooks/prime.d.ts +8 -0
- package/dist/hooks/prime.d.ts.map +1 -1
- package/dist/hooks/prime.js +21 -2
- package/dist/hooks/prime.js.map +1 -1
- package/dist/hooks/sidecar.d.ts +7 -0
- package/dist/hooks/sidecar.d.ts.map +1 -1
- package/dist/hooks/sidecar.js +10 -2
- package/dist/hooks/sidecar.js.map +1 -1
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +148 -103
- package/dist/mcp.js.map +1 -1
- package/dist/model-daemon.d.ts +8 -0
- package/dist/model-daemon.d.ts.map +1 -0
- package/dist/model-daemon.js +45 -0
- package/dist/model-daemon.js.map +1 -0
- package/dist/storage/factory.d.ts.map +1 -1
- package/dist/storage/factory.js +5 -0
- package/dist/storage/factory.js.map +1 -1
- package/dist/storage/pglite-schema.d.ts.map +1 -1
- package/dist/storage/pglite-schema.js +176 -170
- 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 +165 -153
- 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 +165 -153
- package/dist/storage/postgres.js.map +1 -1
- package/dist/storage/sqlite.d.ts +22 -0
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +400 -363
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/types/engram.d.ts +33 -0
- package/dist/types/engram.d.ts.map +1 -1
- package/dist/types/engram.js.map +1 -1
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/README.md +1 -1
- package/plugin/bin/awm-mcp-launcher.cjs +2 -2
- package/plugin/skills/awm-memory/SKILL.md +11 -2
- package/src/adapters/common.ts +881 -802
- package/src/api/routes.ts +20 -0
- package/src/cli/migrate.ts +309 -307
- package/src/core/asked-value.ts +74 -0
- package/src/core/conflicts.ts +280 -0
- package/src/core/embeddings.ts +35 -10
- package/src/core/format-recall.ts +83 -25
- package/src/core/ml-worker.ts +458 -281
- package/src/core/model-daemon-client.ts +374 -0
- package/src/core/model-daemon-server.ts +344 -0
- package/src/core/model-daemon.ts +243 -0
- package/src/core/query-expander.ts +127 -126
- package/src/core/recall-config.ts +5 -0
- package/src/core/rerank-window.ts +164 -159
- package/src/core/rerank2.ts +143 -82
- package/src/core/reranker.ts +78 -11
- package/src/core/trust.ts +65 -0
- package/src/core/validity.ts +47 -0
- package/src/core/whoami.ts +136 -120
- package/src/core/write-pipeline.ts +119 -3
- package/src/engine/activation.ts +27 -4
- package/src/engine/consolidation.ts +950 -899
- package/src/hooks/prime-query.ts +375 -0
- package/src/hooks/prime.ts +25 -2
- package/src/hooks/sidecar.ts +13 -3
- package/src/index.ts +255 -248
- package/src/mcp.ts +1521 -1475
- package/src/model-daemon.ts +48 -0
- package/src/storage/factory.ts +25 -19
- package/src/storage/pglite-schema.ts +199 -193
- package/src/storage/pglite.ts +1513 -1498
- package/src/storage/postgres.ts +1613 -1598
- package/src/storage/sqlite.ts +2056 -2019
- package/src/types/engram.ts +30 -0
package/dist/adapters/common.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Extracted from the original setup() in cli.ts — path resolution, secrets,
|
|
7
7
|
* environment variables, MCP command building, and the AWM instruction snippet.
|
|
8
8
|
*/
|
|
9
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync, copyFileSync } from 'node:fs';
|
|
9
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, copyFileSync, realpathSync } from 'node:fs';
|
|
10
10
|
import { resolve, join, dirname, basename } from 'node:path';
|
|
11
11
|
import { randomBytes } from 'node:crypto';
|
|
12
12
|
import { homedir as osHomedir } from 'node:os';
|
|
@@ -46,6 +46,79 @@ export function resolveDbPath(packageRoot, explicit) {
|
|
|
46
46
|
}
|
|
47
47
|
return dbPath;
|
|
48
48
|
}
|
|
49
|
+
/** The default live store, i.e. what a path resolves to when nothing was specified. */
|
|
50
|
+
export function isLiveStorePath(dbPath) {
|
|
51
|
+
const target = canonicalPath(dbPath);
|
|
52
|
+
return [join(homedir(), '.awm', 'memory.db'), join(homedir(), '.awm', 'memory-pglite')]
|
|
53
|
+
.some(live => canonicalPath(live) === target);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* A path in the form two spellings of the same file share: real path when it exists
|
|
57
|
+
* (junctions, symlinks, 8.3 short names), no `\\?\` prefix, and case-folded on
|
|
58
|
+
* Windows, whose file system ignores case (`c:\Users` and `C:\users` are one file).
|
|
59
|
+
*/
|
|
60
|
+
function canonicalPath(p) {
|
|
61
|
+
let out = resolve(p.replace(/^\\\\\?\\/, ''));
|
|
62
|
+
try {
|
|
63
|
+
out = realpathSync.native(out);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Not there yet (a fresh machine): resolve the folder, which may itself be a junction.
|
|
67
|
+
try {
|
|
68
|
+
out = join(realpathSync.native(dirname(out)), basename(out));
|
|
69
|
+
}
|
|
70
|
+
catch { /* compare as given */ }
|
|
71
|
+
}
|
|
72
|
+
return process.platform === 'win32' ? out.toLowerCase() : out;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* True when this process is a test or benchmark run.
|
|
76
|
+
*
|
|
77
|
+
* `VITEST` is set by the runner itself, so it needs no cooperation from the test.
|
|
78
|
+
* `AWM_BENCH=1` is for a bench or eval script to set; nothing sets it automatically.
|
|
79
|
+
* Both are absent in every production path: the MCP server, the HTTP server, the CLI
|
|
80
|
+
* and the hook sidecars.
|
|
81
|
+
*
|
|
82
|
+
* Not `NODE_ENV=test`: a developer shell that exports it would hand it to the MCP server
|
|
83
|
+
* Claude Code starts from there, and the server would refuse its own store and exit,
|
|
84
|
+
* which shows up only as "Connection closed".
|
|
85
|
+
*/
|
|
86
|
+
export function isTestOrBenchContext() {
|
|
87
|
+
return process.env.VITEST === 'true' || process.env.VITEST === '1'
|
|
88
|
+
|| process.env.AWM_BENCH === '1';
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Refuse to open the LIVE store from a test or benchmark process (A-3, 2026-10-12).
|
|
92
|
+
*
|
|
93
|
+
* Every launcher defaults `AWM_DB_PATH` to `~/.awm/memory.db` — correct for the product
|
|
94
|
+
* and wrong for a harness that forgot to override it, because the harness writes.
|
|
95
|
+
* 4,745 LoCoMo benchmark fixture engrams were found in the live store across 51 unpinned
|
|
96
|
+
* UUID agent spaces and purged on 2026-10-11; nine real memories sat among them and a
|
|
97
|
+
* naive filter would have destroyed those too.
|
|
98
|
+
*
|
|
99
|
+
* The current eval path is not the offender — `tests/realstore-eval/` reads
|
|
100
|
+
* `snapshot/store-backfilled.db` and the newest fixture found was dated 2026-06-17, four
|
|
101
|
+
* months earlier. So this is a guard against the DEFAULT, placed where the default is
|
|
102
|
+
* produced, not a fix for a live bug.
|
|
103
|
+
*
|
|
104
|
+
* Escape hatch: `AWM_ALLOW_LIVE_STORE=1`, for the deliberate case of measuring against
|
|
105
|
+
* the real store (which `docs/eval-gap-2026-10-11.md` does want, read-only). Loud and
|
|
106
|
+
* opt-in beats a silent default.
|
|
107
|
+
*/
|
|
108
|
+
export function assertNotLiveStoreUnderTest(dbPath) {
|
|
109
|
+
if (process.env.AWM_ALLOW_LIVE_STORE === '1')
|
|
110
|
+
return;
|
|
111
|
+
if (!isTestOrBenchContext())
|
|
112
|
+
return;
|
|
113
|
+
if (!isLiveStorePath(dbPath))
|
|
114
|
+
return;
|
|
115
|
+
throw new Error(`AWM refused to open the live store from a test/bench process.\n`
|
|
116
|
+
+ ` path: ${dbPath}\n`
|
|
117
|
+
+ ` because: this process looks like a harness (VITEST or AWM_BENCH=1) and\n`
|
|
118
|
+
+ ` a harness that writes to the live store contaminates it — 4,745 fixture\n`
|
|
119
|
+
+ ` engrams were purged from it on 2026-10-11 for exactly this reason.\n`
|
|
120
|
+
+ ` fix: set AWM_DB_PATH to a scratch file, or AWM_ALLOW_LIVE_STORE=1 if you mean it.`);
|
|
121
|
+
}
|
|
49
122
|
/** Read or generate the hook secret token. */
|
|
50
123
|
export function resolveHookSecret(dbPath) {
|
|
51
124
|
const secretPath = join(dirname(dbPath), '.awm-hook-secret');
|
|
@@ -300,450 +373,459 @@ export function upsertAwmSection(filePath, newContent, options = {}) {
|
|
|
300
373
|
writeFileSync(filePath, rebuilt);
|
|
301
374
|
return `${fname}: AWM section replaced with a marked generated block (force)`;
|
|
302
375
|
}
|
|
303
|
-
export const AWM_INSTRUCTION_CONTENT = `
|
|
304
|
-
## Memory (AWM) — MANDATORY
|
|
305
|
-
|
|
306
|
-
**AWM is THE memory system.** Use it via the \`agent-working-memory\` MCP server
|
|
307
|
-
(preferred) or HTTP at \`http://127.0.0.1:8400\` (fallback). The file-based
|
|
308
|
-
auto-memory at \`~/.claude/projects/.../memory/*.md\` is a LEGACY bootstrap path —
|
|
309
|
-
**do not write new memories to it.** All persistent knowledge goes through AWM.
|
|
310
|
-
|
|
311
|
-
If MCP tools aren't loaded at session start, use ToolSearch with
|
|
312
|
-
\`select:mcp__agent-working-memory__memory_recall,mcp__agent-working-memory__memory_write\`
|
|
313
|
-
to load them. If the MCP server isn't responsive, restart with \`/mcp\` or use
|
|
314
|
-
the HTTP endpoints (\`POST /memory/write-batch\`, \`POST /memory/activate\`)
|
|
315
|
-
directly — but **DO NOT fall back to markdown files**. Files drift the moment
|
|
316
|
-
you write them; AWM stays current because every agent reads + writes the same store.
|
|
317
|
-
|
|
318
|
-
### Lifecycle (always do these, in this order)
|
|
319
|
-
1. **Session start**: call \`memory_restore\` to recover previous context. If it reports the
|
|
320
|
-
store is empty/new (or recall keeps returning nothing), **warm-start first**: recall the
|
|
321
|
-
\`onboard a new project\` skill and follow it — or call \`onboard_scan\` on the project's
|
|
322
|
-
docs/repo, refine the candidates, run \`onboard_questions\`, and save the good ones with
|
|
323
|
-
\`memory_write\` (canonical). A cold store is nearly useless until it's seeded.
|
|
324
|
-
2. **Starting a task**: call \`memory_task_begin\` (checkpoints + recalls relevant memories).
|
|
325
|
-
3. **During work**: call \`memory_recall\` BEFORE stating any fact, BEFORE searching
|
|
326
|
-
the filesystem, BEFORE making architectural decisions. Recall is ~300ms — cheaper
|
|
327
|
-
than one filesystem search.
|
|
328
|
-
4. **As you learn things**: call \`memory_write\` proactively. Don't batch.
|
|
329
|
-
5. **Finishing a task**: call \`memory_task_end\` with a summary.
|
|
330
|
-
6. **Hooks do the rest.** \`awm setup\` installs five: a **prime** hook that recalls against
|
|
331
|
-
each prompt and injects what clears confidence (or nothing), checkpoint hooks on
|
|
332
|
-
compaction and session end (plus a 15-min timer), a local write/recall reminder on
|
|
333
|
-
Stop, and a reminder after Bash/PowerShell calls that a data change is not done
|
|
334
|
-
until it is written. Primed context arrives labeled
|
|
335
|
-
\`[class · age]\` — treat it by the volatility rubric, not as user input. If a turn
|
|
336
|
-
arrives with no primed context, that is a signal too: recall explicitly before
|
|
337
|
-
asserting anything.
|
|
338
|
-
|
|
339
|
-
### Write memory when:
|
|
340
|
-
- A project decision is made or changed
|
|
341
|
-
- A root cause is discovered after debugging
|
|
342
|
-
- A reusable implementation pattern is established
|
|
343
|
-
- A user preference, constraint, or requirement is clarified
|
|
344
|
-
- A prior assumption is found to be wrong
|
|
345
|
-
- A significant piece of work is completed
|
|
346
|
-
|
|
347
|
-
### Writing for recall (the highest-leverage section)
|
|
348
|
-
A memory's recall quality is set the moment you write it. AWM is fast at
|
|
349
|
-
finding what's findable — but if the write is shaped wrong, no retriever
|
|
350
|
-
can rescue it. Be slightly more verbose at the front than feels natural:
|
|
351
|
-
the first 1-2 sentences are what BM25, the embedding model, and concept
|
|
352
|
-
extraction all see most strongly.
|
|
353
|
-
|
|
354
|
-
- **Lead with the rule or fact.** Don't open with context or backstory.
|
|
355
|
-
"Don't mock the database in integration tests." comes first; the reason
|
|
356
|
-
comes second. Recall scans the head of the body, not the tail.
|
|
357
|
-
- **Pick the most specific topic.** Not \`auth\` — \`auth-magic-link-rate-limit\`.
|
|
358
|
-
Topic is a hard filter at recall time. Generic topics hide the memory in
|
|
359
|
-
a noisy bucket where it competes with everything else in the area.
|
|
360
|
-
- **Include 2+ retrievable identifiers.** File paths, function names, table
|
|
361
|
-
columns, ticket IDs, exact error strings, the literal terms a future query
|
|
362
|
-
will use. \`SettlementService.closeRun()\` beats "the accounting code."
|
|
363
|
-
\`tblMemberDetails.activation_date\` beats "the activation column."
|
|
364
|
-
\`schema/072-settlement-lock.sql\` beats "the migration."
|
|
365
|
-
- **Write in the vocabulary of the future question.** When you imagine asking
|
|
366
|
-
this in three months, what nouns will you use? Use those nouns. Don't
|
|
367
|
-
paraphrase the user's domain language into your own neutral summary.
|
|
368
|
-
- **Name the CATEGORY as well as the specifics — both, always.** Specifics
|
|
369
|
-
make a memory precise; category words make it *reachable*. A memory that
|
|
370
|
-
says "private plan memory peaked 88%, scale P1v3 -> P2v3" never says
|
|
371
|
-
"Azure" or "App Service Plan", so a question asked in those words cannot
|
|
372
|
-
find it — measured on a real store, that memory was not in the top 40
|
|
373
|
-
candidates for "azure app service plan capacity increase". Write the
|
|
374
|
-
system / product / domain nouns (Azure App Service Plan, Freshdesk ticket,
|
|
375
|
-
MySQL connection pool, ShowConnect scoring) NEXT TO the identifiers.
|
|
376
|
-
Note this cuts against "pick the most specific topic" above: that rule is
|
|
377
|
-
about the \`topic\` TAG, not a licence to omit the category from the body.
|
|
378
|
-
Measured on an 11k-engram store: 94% of tagged memories are missing at
|
|
379
|
-
least one of their own topical terms from the body, and 66% of those
|
|
380
|
-
terms never appear in the text at all.
|
|
381
|
-
- **Tags are NOT a substitute for body text.** The embedding is built from
|
|
382
|
-
\`concept + content\` only, so it never sees tags. BM25 indexes them. The
|
|
383
|
-
cross-encoder rerank passage sees ONLY \`topic=\`/\`proj=\`/\`project=\` tags, and
|
|
384
|
-
only when \`AWM_RERANK_TAGS=1\` — which \`awm setup\`, the plugin and the Desktop
|
|
385
|
-
bundle all switch on, so assume it is on — capped at 80 characters. So a word
|
|
386
|
-
that exists only as a tag is invisible to the embedding entirely, and
|
|
387
|
-
invisible to the ranker unless it is one of those three prefixes inside that
|
|
388
|
-
budget. Tag it *and* write it.
|
|
389
|
-
- **Reserve canonical for stable invariants.** Decisions, requirements,
|
|
390
|
-
hard facts, cross-agent shared context. Working class (default) is correct
|
|
391
|
-
for findings, observations, and progress notes. The canonical floor is
|
|
392
|
-
0.7 salience — overusing it pollutes the canonical layer and the floor
|
|
393
|
-
loses meaning.
|
|
394
|
-
- **Include the why for feedback memories.** A rule without a reason can't
|
|
395
|
-
be applied to edge cases. "Don't mock the database" is brittle. "Don't
|
|
396
|
-
mock the database — last quarter mocked tests masked a broken migration"
|
|
397
|
-
is portable to new situations.
|
|
398
|
-
|
|
399
|
-
### Tagging rules (REQUIRED — AWM's prefix-tag retrieval boost depends on these)
|
|
400
|
-
|
|
401
|
-
Every \`memory_write\` should pass these structured fields. AWM stores each as a
|
|
402
|
-
prefix-tag like \`proj=\`, \`topic=\`, \`intent=\`, etc. and uses them for BM25
|
|
403
|
-
and entity-bridge boosts at recall time.
|
|
404
|
-
|
|
405
|
-
(“Required” below means *required for good recall* — the MCP schema accepts every
|
|
406
|
-
one of these as optional, so a write without them succeeds and simply retrieves
|
|
407
|
-
worse.)
|
|
408
|
-
|
|
409
|
-
| Field | Required? | Format | Example |
|
|
410
|
-
|---|---|---|---|
|
|
411
|
-
| \`project\` | **YES** | one short word matching the current project | \`"EquiHub"\`, \`"AWM"\`, \`"USEA-Agent"\` |
|
|
412
|
-
| \`topic\` | **YES** | one or more lowercase area words | \`"database-migration"\`, \`"benchmarks"\` |
|
|
413
|
-
| \`intent\` | **YES** | one of: \`decision\` / \`finding\` / \`todo\` / \`question\` / \`context\` | \`"finding"\` |
|
|
414
|
-
| \`confidence_level\` | **YES** | \`verified\` (tested) / \`observed\` (read in code) / \`assumed\` (reasoning) | \`"verified"\` |
|
|
415
|
-
| \`source\` | recommended | \`code-reading\` / \`debugging\` / \`discussion\` / \`research\` / \`testing\` / \`observation\` | \`"testing"\` |
|
|
416
|
-
| \`memory_class\` | when stable | \`canonical\` (source-of-truth, 0.7 floor, never staged) / \`working\` (default) / \`ephemeral\` | \`"canonical"\` |
|
|
417
|
-
| \`session_id\` | recommended | current conversation ID for entity-bridge boost | autogenerated |
|
|
418
|
-
| \`tags\` | when applicable | extra prefix-tags for IDs and dates | \`["ticket=10001", "date=2026-05-11"]\` |
|
|
419
|
-
|
|
420
|
-
**Always add identifier tags when present in the content:**
|
|
421
|
-
- \`ticket=<id>\` for Freshdesk tickets
|
|
422
|
-
- \`member=<id>\` for member IDs
|
|
423
|
-
- \`asset=<id>\` for a domain record id
|
|
424
|
-
- \`usef=<id>\` for USEF lookups
|
|
425
|
-
- \`date=YYYY-MM-DD\` for temporal anchoring (ISO format)
|
|
426
|
-
- \`person=<Name>\` for stakeholder quotes / decisions
|
|
427
|
-
- \`version=<X.Y.Z>\` for release-specific findings
|
|
428
|
-
|
|
429
|
-
### Entity index — exact-match recall for named things (default off)
|
|
430
|
-
Structured identifier tags (\`ticket=\`, \`person=\`, \`horse=\`, \`member=\`, bare 4+ digit
|
|
431
|
-
ids, etc.) feed a dedicated entity inverted index, separate from BM25/embedding scoring.
|
|
432
|
-
A query naming an entity ("ticket 10003", "Dana Whitfield") can reach the memory through
|
|
433
|
-
this index even when the wording doesn't lexically match — it's a deterministic exact
|
|
434
|
-
lookup, immune to vocabulary mismatch. Keep identifier tags exact and consistent for
|
|
435
|
-
this reason, not just for the BM25 boost described above.
|
|
436
|
-
|
|
437
|
-
Off by default; opt in with \`AWM_ENTITY_INDEX_FETCH=1\` (bounded by
|
|
438
|
-
\`AWM_ENTITY_INDEX_CAP\`, default 12). Matched entities get no score boost — they're
|
|
439
|
-
guaranteed a reranker audition instead, so the cross-encoder alone decides whether they
|
|
440
|
-
surface. Worth trialing on identifier-heavy workloads (ticket/event numbers, named
|
|
441
|
-
people/things you refer to by name often); not yet the default pending evaluation.
|
|
442
|
-
|
|
443
|
-
### Temporal validity — memories that expire or start in the future
|
|
444
|
-
\`memory_write\` accepts \`valid_from\` / \`valid_to\` (ISO dates). Use \`valid_to\` on
|
|
445
|
-
**operational** facts with a real shelf life — a deploy state, "waiting on X's reply",
|
|
446
|
-
a ticket status — so the memory expires instead of relying on you to remember it's
|
|
447
|
-
stale. Recall renders \`[valid until …]\`
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
- \`memory_class:
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
-
|
|
486
|
-
-
|
|
487
|
-
-
|
|
488
|
-
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
**
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
- \`
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
- \`
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
- \`
|
|
732
|
-
|
|
733
|
-
- \`
|
|
734
|
-
|
|
735
|
-
- \`
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
376
|
+
export const AWM_INSTRUCTION_CONTENT = `
|
|
377
|
+
## Memory (AWM) — MANDATORY
|
|
378
|
+
|
|
379
|
+
**AWM is THE memory system.** Use it via the \`agent-working-memory\` MCP server
|
|
380
|
+
(preferred) or HTTP at \`http://127.0.0.1:8400\` (fallback). The file-based
|
|
381
|
+
auto-memory at \`~/.claude/projects/.../memory/*.md\` is a LEGACY bootstrap path —
|
|
382
|
+
**do not write new memories to it.** All persistent knowledge goes through AWM.
|
|
383
|
+
|
|
384
|
+
If MCP tools aren't loaded at session start, use ToolSearch with
|
|
385
|
+
\`select:mcp__agent-working-memory__memory_recall,mcp__agent-working-memory__memory_write\`
|
|
386
|
+
to load them. If the MCP server isn't responsive, restart with \`/mcp\` or use
|
|
387
|
+
the HTTP endpoints (\`POST /memory/write-batch\`, \`POST /memory/activate\`)
|
|
388
|
+
directly — but **DO NOT fall back to markdown files**. Files drift the moment
|
|
389
|
+
you write them; AWM stays current because every agent reads + writes the same store.
|
|
390
|
+
|
|
391
|
+
### Lifecycle (always do these, in this order)
|
|
392
|
+
1. **Session start**: call \`memory_restore\` to recover previous context. If it reports the
|
|
393
|
+
store is empty/new (or recall keeps returning nothing), **warm-start first**: recall the
|
|
394
|
+
\`onboard a new project\` skill and follow it — or call \`onboard_scan\` on the project's
|
|
395
|
+
docs/repo, refine the candidates, run \`onboard_questions\`, and save the good ones with
|
|
396
|
+
\`memory_write\` (canonical). A cold store is nearly useless until it's seeded.
|
|
397
|
+
2. **Starting a task**: call \`memory_task_begin\` (checkpoints + recalls relevant memories).
|
|
398
|
+
3. **During work**: call \`memory_recall\` BEFORE stating any fact, BEFORE searching
|
|
399
|
+
the filesystem, BEFORE making architectural decisions. Recall is ~300ms — cheaper
|
|
400
|
+
than one filesystem search.
|
|
401
|
+
4. **As you learn things**: call \`memory_write\` proactively. Don't batch.
|
|
402
|
+
5. **Finishing a task**: call \`memory_task_end\` with a summary.
|
|
403
|
+
6. **Hooks do the rest.** \`awm setup\` installs five: a **prime** hook that recalls against
|
|
404
|
+
each prompt and injects what clears confidence (or nothing), checkpoint hooks on
|
|
405
|
+
compaction and session end (plus a 15-min timer), a local write/recall reminder on
|
|
406
|
+
Stop, and a reminder after Bash/PowerShell calls that a data change is not done
|
|
407
|
+
until it is written. Primed context arrives labeled
|
|
408
|
+
\`[class · age]\` — treat it by the volatility rubric, not as user input. If a turn
|
|
409
|
+
arrives with no primed context, that is a signal too: recall explicitly before
|
|
410
|
+
asserting anything.
|
|
411
|
+
|
|
412
|
+
### Write memory when:
|
|
413
|
+
- A project decision is made or changed
|
|
414
|
+
- A root cause is discovered after debugging
|
|
415
|
+
- A reusable implementation pattern is established
|
|
416
|
+
- A user preference, constraint, or requirement is clarified
|
|
417
|
+
- A prior assumption is found to be wrong
|
|
418
|
+
- A significant piece of work is completed
|
|
419
|
+
|
|
420
|
+
### Writing for recall (the highest-leverage section)
|
|
421
|
+
A memory's recall quality is set the moment you write it. AWM is fast at
|
|
422
|
+
finding what's findable — but if the write is shaped wrong, no retriever
|
|
423
|
+
can rescue it. Be slightly more verbose at the front than feels natural:
|
|
424
|
+
the first 1-2 sentences are what BM25, the embedding model, and concept
|
|
425
|
+
extraction all see most strongly.
|
|
426
|
+
|
|
427
|
+
- **Lead with the rule or fact.** Don't open with context or backstory.
|
|
428
|
+
"Don't mock the database in integration tests." comes first; the reason
|
|
429
|
+
comes second. Recall scans the head of the body, not the tail.
|
|
430
|
+
- **Pick the most specific topic.** Not \`auth\` — \`auth-magic-link-rate-limit\`.
|
|
431
|
+
Topic is a hard filter at recall time. Generic topics hide the memory in
|
|
432
|
+
a noisy bucket where it competes with everything else in the area.
|
|
433
|
+
- **Include 2+ retrievable identifiers.** File paths, function names, table
|
|
434
|
+
columns, ticket IDs, exact error strings, the literal terms a future query
|
|
435
|
+
will use. \`SettlementService.closeRun()\` beats "the accounting code."
|
|
436
|
+
\`tblMemberDetails.activation_date\` beats "the activation column."
|
|
437
|
+
\`schema/072-settlement-lock.sql\` beats "the migration."
|
|
438
|
+
- **Write in the vocabulary of the future question.** When you imagine asking
|
|
439
|
+
this in three months, what nouns will you use? Use those nouns. Don't
|
|
440
|
+
paraphrase the user's domain language into your own neutral summary.
|
|
441
|
+
- **Name the CATEGORY as well as the specifics — both, always.** Specifics
|
|
442
|
+
make a memory precise; category words make it *reachable*. A memory that
|
|
443
|
+
says "private plan memory peaked 88%, scale P1v3 -> P2v3" never says
|
|
444
|
+
"Azure" or "App Service Plan", so a question asked in those words cannot
|
|
445
|
+
find it — measured on a real store, that memory was not in the top 40
|
|
446
|
+
candidates for "azure app service plan capacity increase". Write the
|
|
447
|
+
system / product / domain nouns (Azure App Service Plan, Freshdesk ticket,
|
|
448
|
+
MySQL connection pool, ShowConnect scoring) NEXT TO the identifiers.
|
|
449
|
+
Note this cuts against "pick the most specific topic" above: that rule is
|
|
450
|
+
about the \`topic\` TAG, not a licence to omit the category from the body.
|
|
451
|
+
Measured on an 11k-engram store: 94% of tagged memories are missing at
|
|
452
|
+
least one of their own topical terms from the body, and 66% of those
|
|
453
|
+
terms never appear in the text at all.
|
|
454
|
+
- **Tags are NOT a substitute for body text.** The embedding is built from
|
|
455
|
+
\`concept + content\` only, so it never sees tags. BM25 indexes them. The
|
|
456
|
+
cross-encoder rerank passage sees ONLY \`topic=\`/\`proj=\`/\`project=\` tags, and
|
|
457
|
+
only when \`AWM_RERANK_TAGS=1\` — which \`awm setup\`, the plugin and the Desktop
|
|
458
|
+
bundle all switch on, so assume it is on — capped at 80 characters. So a word
|
|
459
|
+
that exists only as a tag is invisible to the embedding entirely, and
|
|
460
|
+
invisible to the ranker unless it is one of those three prefixes inside that
|
|
461
|
+
budget. Tag it *and* write it.
|
|
462
|
+
- **Reserve canonical for stable invariants.** Decisions, requirements,
|
|
463
|
+
hard facts, cross-agent shared context. Working class (default) is correct
|
|
464
|
+
for findings, observations, and progress notes. The canonical floor is
|
|
465
|
+
0.7 salience — overusing it pollutes the canonical layer and the floor
|
|
466
|
+
loses meaning.
|
|
467
|
+
- **Include the why for feedback memories.** A rule without a reason can't
|
|
468
|
+
be applied to edge cases. "Don't mock the database" is brittle. "Don't
|
|
469
|
+
mock the database — last quarter mocked tests masked a broken migration"
|
|
470
|
+
is portable to new situations.
|
|
471
|
+
|
|
472
|
+
### Tagging rules (REQUIRED — AWM's prefix-tag retrieval boost depends on these)
|
|
473
|
+
|
|
474
|
+
Every \`memory_write\` should pass these structured fields. AWM stores each as a
|
|
475
|
+
prefix-tag like \`proj=\`, \`topic=\`, \`intent=\`, etc. and uses them for BM25
|
|
476
|
+
and entity-bridge boosts at recall time.
|
|
477
|
+
|
|
478
|
+
(“Required” below means *required for good recall* — the MCP schema accepts every
|
|
479
|
+
one of these as optional, so a write without them succeeds and simply retrieves
|
|
480
|
+
worse.)
|
|
481
|
+
|
|
482
|
+
| Field | Required? | Format | Example |
|
|
483
|
+
|---|---|---|---|
|
|
484
|
+
| \`project\` | **YES** | one short word matching the current project | \`"EquiHub"\`, \`"AWM"\`, \`"USEA-Agent"\` |
|
|
485
|
+
| \`topic\` | **YES** | one or more lowercase area words | \`"database-migration"\`, \`"benchmarks"\` |
|
|
486
|
+
| \`intent\` | **YES** | one of: \`decision\` / \`finding\` / \`todo\` / \`question\` / \`context\` | \`"finding"\` |
|
|
487
|
+
| \`confidence_level\` | **YES** | \`verified\` (tested) / \`observed\` (read in code) / \`assumed\` (reasoning) | \`"verified"\` |
|
|
488
|
+
| \`source\` | recommended | \`code-reading\` / \`debugging\` / \`discussion\` / \`research\` / \`testing\` / \`observation\` | \`"testing"\` |
|
|
489
|
+
| \`memory_class\` | when stable | \`canonical\` (source-of-truth, 0.7 floor, never staged) / \`working\` (default) / \`ephemeral\` | \`"canonical"\` |
|
|
490
|
+
| \`session_id\` | recommended | current conversation ID for entity-bridge boost | autogenerated |
|
|
491
|
+
| \`tags\` | when applicable | extra prefix-tags for IDs and dates | \`["ticket=10001", "date=2026-05-11"]\` |
|
|
492
|
+
|
|
493
|
+
**Always add identifier tags when present in the content:**
|
|
494
|
+
- \`ticket=<id>\` for Freshdesk tickets
|
|
495
|
+
- \`member=<id>\` for member IDs
|
|
496
|
+
- \`asset=<id>\` for a domain record id
|
|
497
|
+
- \`usef=<id>\` for USEF lookups
|
|
498
|
+
- \`date=YYYY-MM-DD\` for temporal anchoring (ISO format)
|
|
499
|
+
- \`person=<Name>\` for stakeholder quotes / decisions
|
|
500
|
+
- \`version=<X.Y.Z>\` for release-specific findings
|
|
501
|
+
|
|
502
|
+
### Entity index — exact-match recall for named things (default off)
|
|
503
|
+
Structured identifier tags (\`ticket=\`, \`person=\`, \`horse=\`, \`member=\`, bare 4+ digit
|
|
504
|
+
ids, etc.) feed a dedicated entity inverted index, separate from BM25/embedding scoring.
|
|
505
|
+
A query naming an entity ("ticket 10003", "Dana Whitfield") can reach the memory through
|
|
506
|
+
this index even when the wording doesn't lexically match — it's a deterministic exact
|
|
507
|
+
lookup, immune to vocabulary mismatch. Keep identifier tags exact and consistent for
|
|
508
|
+
this reason, not just for the BM25 boost described above.
|
|
509
|
+
|
|
510
|
+
Off by default; opt in with \`AWM_ENTITY_INDEX_FETCH=1\` (bounded by
|
|
511
|
+
\`AWM_ENTITY_INDEX_CAP\`, default 12). Matched entities get no score boost — they're
|
|
512
|
+
guaranteed a reranker audition instead, so the cross-encoder alone decides whether they
|
|
513
|
+
surface. Worth trialing on identifier-heavy workloads (ticket/event numbers, named
|
|
514
|
+
people/things you refer to by name often); not yet the default pending evaluation.
|
|
515
|
+
|
|
516
|
+
### Temporal validity — memories that expire or start in the future
|
|
517
|
+
\`memory_write\` accepts \`valid_from\` / \`valid_to\` (ISO dates). Use \`valid_to\` on
|
|
518
|
+
**operational** facts with a real shelf life — a deploy state, "waiting on X's reply",
|
|
519
|
+
a ticket status — so the memory expires instead of relying on you to remember it's
|
|
520
|
+
stale. Recall renders \`[valid until …]\` while the window holds; once it has passed
|
|
521
|
+
the memory is down-ranked like a superseded one and labelled \`⚠ EXPIRED\`. Use
|
|
522
|
+
\`valid_from\` for a fact that becomes true on a known future date (a policy change, a
|
|
523
|
+
season that hasn't started yet). Don't set either for durable facts — most memories
|
|
524
|
+
don't need them.
|
|
525
|
+
|
|
526
|
+
### Memory classes (controls how strictly the salience filter gates the write)
|
|
527
|
+
- \`memory_class: canonical\` — source-of-truth memories. Floor 0.7 salience, never staged.
|
|
528
|
+
Use for: user-stated decisions, project requirements, verified architectural facts,
|
|
529
|
+
cross-agent shared context. **In a hive (multi-agent) setup, always use \`canonical\`
|
|
530
|
+
for writes that other agents must be able to recall** — the default \`working\` class
|
|
531
|
+
may get filtered.
|
|
532
|
+
- \`memory_class: working\` (default) — observations and findings. Salience-gated.
|
|
533
|
+
- \`memory_class: ephemeral\` — short-lived context that should decay quickly.
|
|
534
|
+
|
|
535
|
+
### Salience auto-promotion (defense in depth)
|
|
536
|
+
The salience filter automatically promotes certain content patterns even if you forget
|
|
537
|
+
to set \`memory_class\` explicitly:
|
|
538
|
+
- **User feedback** — content starting with a configured stakeholder name plus a feedback
|
|
539
|
+
verb ("<name> said…", "<name> directed…", "<name> decided…") auto-promotes to canonical,
|
|
540
|
+
so quoting the user verbatim preserves the decision. **This is OFF unless an operator
|
|
541
|
+
sets \`AWM_FEEDBACK_NAMES\`** (it ships empty); when in doubt, pass
|
|
542
|
+
\`memory_class: 'canonical'\` explicitly rather than relying on it.
|
|
543
|
+
- **Verified operational records** — content with an action verb (Submitted, Finalized,
|
|
544
|
+
Completed, Reconciled, Triaged, Posted, Resolved, Stamped, Pushed, Deployed, Migrated,
|
|
545
|
+
Imported, Exported, Backfilled) plus 2+ concrete identifiers (ISO date \`YYYY-MM-DD\`,
|
|
546
|
+
or contextual numeric IDs like "event 18969", "ticket #18330", "USEF 341980") gets
|
|
547
|
+
a 0.45 salience floor. So batch summaries with real IDs survive even when topic
|
|
548
|
+
terms repeat.
|
|
549
|
+
|
|
550
|
+
If neither pattern applies and you want a memory to definitely survive, set
|
|
551
|
+
\`memory_class: canonical\` explicitly. Don't rely on auto-promotion for important writes.
|
|
552
|
+
|
|
553
|
+
### Recall memory when:
|
|
554
|
+
- **BEFORE stating ANY fact about how a system works** — recall first; if AWM doesn't
|
|
555
|
+
have it, read the code. Never guess and present it as fact.
|
|
556
|
+
- **BEFORE searching the filesystem** — recall first; AWM is faster and has cross-session
|
|
557
|
+
knowledge that file search doesn't.
|
|
558
|
+
- Starting work on a new task or subsystem
|
|
559
|
+
- Re-entering code you haven't touched recently
|
|
560
|
+
- After a failed attempt — check if there's prior knowledge
|
|
561
|
+
- Before refactoring or making architectural changes
|
|
562
|
+
- When a topic comes up that you might have prior context on
|
|
563
|
+
|
|
564
|
+
Recall is fast (~300ms typical). Use it freely.
|
|
565
|
+
|
|
566
|
+
### Chain recalls — one hop per call
|
|
567
|
+
|
|
568
|
+
AWM returns what matches your query. It does **not** walk from that answer to the next
|
|
569
|
+
fact, and it is not meant to: it is active memory, holding what you need right now.
|
|
570
|
+
Following a chain to the next thing is **your** job — recall, read, recall again, each
|
|
571
|
+
result becoming the cue for the next query.
|
|
572
|
+
|
|
573
|
+
So when a question refers to something **by its role instead of its name**, you have a
|
|
574
|
+
chain, not a query:
|
|
575
|
+
|
|
576
|
+
> *"What is the codename of the project owned by my scheduler?"*
|
|
577
|
+
|
|
578
|
+
That is three lookups, not one:
|
|
579
|
+
|
|
580
|
+
1. recall \`who is my scheduler\` → a name
|
|
581
|
+
2. recall \`what project does <that name> own\` → a project
|
|
582
|
+
3. recall \`<that project> codename\` → the answer
|
|
583
|
+
|
|
584
|
+
**The failure mode is answering from the first recall.** A single query blending all
|
|
585
|
+
three terms returns the most *salient* related memory, not the correct one — measured,
|
|
586
|
+
that returns the main project's codename with complete confidence, and it is wrong.
|
|
587
|
+
Each hop on its own is an ordinary, reliable recall; the chain only breaks when you stop
|
|
588
|
+
asking.
|
|
589
|
+
|
|
590
|
+
Tells that you are looking at a chain: a possessive or relative clause naming an entity
|
|
591
|
+
by role rather than identity (\`my scheduler\`, \`the owner of X\`, \`whoever signed
|
|
592
|
+
off on Y\`), or a rule that operates on an attribute you have not looked up yet (\`the
|
|
593
|
+
release tag of the project we discussed\` needs that project's codename first). If
|
|
594
|
+
resolving the question requires a fact you would have to *derive*, recall the fact
|
|
595
|
+
instead.
|
|
596
|
+
|
|
597
|
+
### Recall strategy (when one query isn't enough)
|
|
598
|
+
AWM's adaptive retrieval handles most query variations natively — synonym
|
|
599
|
+
expansion, multi-channel scoring, embedding + BM25 + reranker agreement.
|
|
600
|
+
A single recall is usually enough.
|
|
601
|
+
|
|
602
|
+
When it isn't:
|
|
603
|
+
- **If the first recall returns nothing or returns the wrong things, reformulate.**
|
|
604
|
+
Try a second query with different phrasing — synonyms, more specific nouns,
|
|
605
|
+
the exact identifier from the code rather than the conceptual name. Two or
|
|
606
|
+
three recalls cost less than one filesystem search.
|
|
607
|
+
- **Use the words a domain expert would use, not generic English.** "Period
|
|
608
|
+
close lock" not "accounting feature"; "magic link rate limit" not "auth issue."
|
|
609
|
+
- **For broad exploration, pass \`mode: "exploratory"\`** — wider candidate
|
|
610
|
+
pool, lower precision floor. For specific lookups, leave mode unset (auto).
|
|
611
|
+
- **Don't ensemble more than 3 reformulations.** If three different phrasings
|
|
612
|
+
return nothing, the memory probably isn't there — read the code instead of
|
|
613
|
+
burning more recalls.
|
|
614
|
+
|
|
615
|
+
### Recall tuning — opt-in parameters
|
|
616
|
+
Default \`memory_recall\` is tuned for the common case. Four opt-in parameters change
|
|
617
|
+
the cost/quality tradeoff. Use them when the default doesn't match what you need.
|
|
618
|
+
|
|
619
|
+
- **\`granularity: 'compact'\`** — every result carries a 200-char \`summary\`
|
|
620
|
+
field with a query-aware snippet (the densest window of query terms in the
|
|
621
|
+
content). Use this when you expect to scan 5+ results to find one — saves
|
|
622
|
+
~70% of recall output tokens. The full content stays available in
|
|
623
|
+
\`engram.content\` if you want to drill into a specific result.
|
|
624
|
+
- **\`granularity: 'auto'\`** — confidence-adaptive. If the top result is a clear
|
|
625
|
+
winner, it gets a longer summary while the rest are compact. If confidence
|
|
626
|
+
is uniform across results, everything is compact. Use when you don't know
|
|
627
|
+
in advance whether one result will dominate.
|
|
628
|
+
- **\`require_confidence\`** — abstention. The default (0.05) is already a light
|
|
629
|
+
gate: when the top results don't stand out from the rest, recall returns
|
|
630
|
+
nothing and the reply says **\`RECALL ABSTAINED\`** with the count it withheld.
|
|
631
|
+
An empty result *without* that line is genuine absence. The gate is on the
|
|
632
|
+
SHAPE of the score distribution, not relevance — so raising it silences
|
|
633
|
+
specific questions with one clear winner first (measured: 0.25 abstained on
|
|
634
|
+
two identifier queries scoring 0.26–0.38 while passing a vague one at 0.92).
|
|
635
|
+
Leave it alone for ordinary recall; \`0.10\` is the ceiling that still helps for
|
|
636
|
+
push-style use where nobody asked. When abstention fires, reformulate once
|
|
637
|
+
with the exact identifier, then read the code — don't retry with a lower
|
|
638
|
+
threshold to force an answer.
|
|
639
|
+
- **\`workspace: "<name>"\`** — hive-mode recall across all agents in the
|
|
640
|
+
workspace. Use when other agents may have written canonical knowledge you
|
|
641
|
+
need. Default is agent-scoped (your own memories only). Can also be set
|
|
642
|
+
globally via the \`AWM_WORKSPACE\` env var. Memories live in the agent pool
|
|
643
|
+
they were written to; if a session lands in an unexpected pool, \`memory_whoami\`
|
|
644
|
+
says which one before you conclude the memory is missing.
|
|
645
|
+
|
|
646
|
+
### Keep memory fresh
|
|
647
|
+
- After recalling a memory, if you observe the real state is different → call
|
|
648
|
+
\`memory_supersede\` immediately with the corrected version.
|
|
649
|
+
- When a fact has CHANGED (a time, a price, a status, a decision) and you don't have the
|
|
650
|
+
old memory's id: \`memory_write\` with \`correction: true\`. AWM supersedes the closest
|
|
651
|
+
memory about the same thing and tells you which. Written as a plain observation, a
|
|
652
|
+
changed value merges into or ranks below the stale version — that is how "departs 8:40"
|
|
653
|
+
lost to "departs 10:15" in the MWA assessment.
|
|
654
|
+
- A recall line that says \`⚠ POSSIBLE CONFLICT\`, \`⚠ EXPIRED\` or \`⚠ SUPERSEDED\` is
|
|
655
|
+
telling you not to state that value from memory: read the source, then supersede or
|
|
656
|
+
correct the stale one.
|
|
657
|
+
- After using a recalled memory: call \`memory_feedback\` (useful/not-useful) **with the
|
|
658
|
+
\`recall_id\`** printed at the end of the recall output (\`[recall_id: …]\`). That id
|
|
659
|
+
joins the feedback to the recall that produced it, which is what lets AWM learn
|
|
660
|
+
which recalls were worth their tokens; feedback without it is a bare vote.
|
|
661
|
+
- If you discover a memory is factually wrong: \`memory_retract\` to remove it.
|
|
662
|
+
- **If you bypass AWM (file-memory, in-context notes, "I'll just remember"), the memory
|
|
663
|
+
drifts out of date. The system relies on you to keep it current. This is the #1
|
|
664
|
+
failure mode.**
|
|
665
|
+
|
|
666
|
+
### Cognition recipes — YOU do the thinking, AWM keeps the result
|
|
667
|
+
AWM contains no LLM. When memory needs real thinking — distilling a repeatable
|
|
668
|
+
procedure, reflecting on a failure — AWM hands YOU a versioned recipe (prompt +
|
|
669
|
+
strict output shape) and you run it as a SEPARATE focused pass, then write the
|
|
670
|
+
result back as an ordinary memory with provenance.
|
|
671
|
+
|
|
672
|
+
- \`memory_task_end\` responses include the recipe invitations. Honor the gates:
|
|
673
|
+
skill-derivation only after a genuinely procedural task (3+ tool calls or a
|
|
674
|
+
delegated sub-task); friction-lesson only after a failure/retry/wrong assumption.
|
|
675
|
+
- Run each recipe as its own focused pass — do NOT bundle it with other
|
|
676
|
+
reasoning; bundled passes reliably drop the output.
|
|
677
|
+
- Write back exactly per the recipe's contract: \`origin_class: 'recipe'\` +
|
|
678
|
+
\`recipe_id\` (e.g. \`skill-derivation@1\`), concept prefixed \`skill: \` or
|
|
679
|
+
\`lesson: \`. AWM validates the shape and rejects malformed or unknown-recipe
|
|
680
|
+
writes with the contract echoed back — fix and retry, don't drop the insight.
|
|
681
|
+
- Re-deriving the same skill name reinforces the existing memory instead of
|
|
682
|
+
duplicating it, so don't fear writing a skill you may have written before.
|
|
683
|
+
|
|
684
|
+
### Content fade — write-and-forget is safe
|
|
685
|
+
Un-recalled engrams gradually fade their content while preserving cue pathways
|
|
686
|
+
(concept + tags + embedding stay intact). This is Paper 1 — storage
|
|
687
|
+
degradation. Practical implications:
|
|
688
|
+
|
|
689
|
+
- **Don't manually purge memories** to "save space." The system already
|
|
690
|
+
compresses unused content. Old memories stay findable via cue match even
|
|
691
|
+
when their body has decayed.
|
|
692
|
+
- **Don't over-pin with \`memory_class: canonical\`** to fight fade. Canonical
|
|
693
|
+
only changes salience gating at write time, not fade behavior. Fade
|
|
694
|
+
affects un-recalled engrams of any class.
|
|
695
|
+
- **Recall keeps content alive.** Every recall touches the engram and resets
|
|
696
|
+
its fade clock. Frequently-recalled memories stay full-fidelity automatically.
|
|
697
|
+
- **Supersede is the right tool for stale facts.** When you observe a memory
|
|
698
|
+
is outdated, call \`memory_supersede\` — the new version inherits the old
|
|
699
|
+
one's coherent associations (counter-narrative replacement) so cue
|
|
700
|
+
pathways carry forward to the replacement.
|
|
701
|
+
|
|
702
|
+
### Example — good vs bad memory_write
|
|
703
|
+
|
|
704
|
+
**BAD** (no prefix tags, vague concept, can't be recalled by future queries):
|
|
705
|
+
\`\`\`
|
|
706
|
+
memory_write(
|
|
707
|
+
concept="found a bug",
|
|
708
|
+
content="The thing I was looking at was broken so I fixed it."
|
|
709
|
+
)
|
|
710
|
+
\`\`\`
|
|
711
|
+
|
|
712
|
+
**GOOD** (rich identifiers, structured metadata, prefix tags):
|
|
713
|
+
\`\`\`
|
|
714
|
+
memory_write(
|
|
715
|
+
concept="EquiHub period-close BLOCKED check missing server-side",
|
|
716
|
+
content="apps/web/app/(billing)/billing/settlement-lock/page.tsx had client-only BLOCKED enforcement. Fixed by adding server-side check in SettlementService.closeRun() per schema/072-settlement-lock.sql. Without server-side check a malicious request could bypass via direct API call.",
|
|
717
|
+
project="EquiHub",
|
|
718
|
+
topic="accounting",
|
|
719
|
+
intent="finding",
|
|
720
|
+
confidence_level="verified",
|
|
721
|
+
source="debugging",
|
|
722
|
+
memory_class="canonical",
|
|
723
|
+
tags=["ticket=10001", "person=Robert", "date=2026-05-11", "topic=period-close", "topic=security"]
|
|
724
|
+
)
|
|
725
|
+
\`\`\`
|
|
726
|
+
|
|
727
|
+
### Also:
|
|
728
|
+
- To track work items: memory_task_add, memory_task_update, memory_task_list, memory_task_next
|
|
729
|
+
- \`memory_whoami\` (MCP tool) / \`GET /whoami\` — identify the instance you're actually
|
|
730
|
+
talking to: agent id, workspace, mode, backend, store path, code provenance, sibling
|
|
731
|
+
agent spaces sharing the store. Call this FIRST whenever you're unsure which store,
|
|
732
|
+
which agent identity, or which running code you're dealing with — before reasoning
|
|
733
|
+
about AWM's own state from a stale memory or an assumed port number.
|
|
734
|
+
- AWM is shared across all agents in real time. When any agent writes or supersedes a
|
|
735
|
+
memory, every other agent can recall it immediately — but only within the same
|
|
736
|
+
workspace and agent scope.
|
|
737
|
+
|
|
738
|
+
### Output compression (token efficiency, output-only)
|
|
739
|
+
When a tool returns a LARGE STRUCTURED result you need to keep in context — a JSON
|
|
740
|
+
array of records, query rows, a log dump, an API response — pass it through
|
|
741
|
+
\`compress_output\` first. It re-encodes the data as TOON (a compact, lossless,
|
|
742
|
+
schema-aware tabular form of JSON), cutting ~50-65% of the tokens at no
|
|
743
|
+
comprehension cost. This is output-only: it never changes the data or your memories.
|
|
744
|
+
- Use it on big STRUCTURED outputs, not on prose. Prose is returned unchanged —
|
|
745
|
+
for trimming memory prose, use recall \`granularity: 'compact'\` instead.
|
|
746
|
+
- It returns a \`ref\`; call \`retrieve_original(ref)\` if you later need the exact
|
|
747
|
+
verbatim source (e.g. to hand it to another tool unchanged).
|
|
748
|
+
- Don't bother for small outputs — it only compresses when the saving is worthwhile
|
|
749
|
+
and falls back to plain JSON if TOON wouldn't reproduce the data exactly.
|
|
750
|
+
|
|
751
|
+
### Backend (SQLite vs PGlite)
|
|
752
|
+
AWM ships two storage backends. The installer picks SQLite by default; both
|
|
753
|
+
are functionally equivalent for cognitive workloads, but differ in operational
|
|
754
|
+
guarantees:
|
|
755
|
+
|
|
756
|
+
- **SQLite** (default) — embedded, **multi-process safe** via WAL mode. Best
|
|
757
|
+
for single-machine setups and MCP scenarios where multiple Claude Code
|
|
758
|
+
sessions may open the same database concurrently.
|
|
759
|
+
- **PGlite** — embedded Postgres (WASM) with pgvector. **Single-process only**
|
|
760
|
+
— two MCP processes against the same \`memory-pglite/\` directory will
|
|
761
|
+
abort the second. Pick via \`AWM_STORE_BACKEND=pglite\` and
|
|
762
|
+
\`AWM_DB_PATH=path/to/memory-pglite\`.
|
|
763
|
+
- **Auto-detect** — if \`AWM_DB_PATH\` points to a directory that already
|
|
764
|
+
exists, AWM detects PGlite; a file → SQLite. No explicit
|
|
765
|
+
\`AWM_STORE_BACKEND\` needed when an existing DB is present.
|
|
766
|
+
|
|
767
|
+
For the comparison table (recall quality parity, BM25 vs \`ts_rank_cd\`,
|
|
768
|
+
multi-process guarantees), see \`docs/pglite-feature-parity.md\`.
|
|
769
|
+
|
|
770
|
+
### Diagnostics / escape hatches (env vars, only if you know why)
|
|
771
|
+
Each optimization is gated by an env var so it can be disabled for A/B testing if a
|
|
772
|
+
regression appears in your workload. \`memory_whoami\` prints the active fingerprint.
|
|
773
|
+
|
|
774
|
+
Retrieval (set ON by \`awm setup\` since 0.14.6 — the measured-good configuration):
|
|
775
|
+
- \`AWM_RERANK2=1\` — second-stage rerank by the cross-encoder's own score.
|
|
776
|
+
- \`AWM_RERANK_WINDOW=query\` — 400-char rerank window on the densest query-term
|
|
777
|
+
region instead of the prefix (25% → 87.5% on long memories).
|
|
778
|
+
- \`AWM_RERANK_TAGS=1\` — tags fed into the rerank passage (+7.4pp s@1).
|
|
779
|
+
|
|
780
|
+
Hook sidecar (0.14.2):
|
|
781
|
+
- \`AWM_HOOK_PORT=8401\` / \`AWM_HOOK_PORT_RANGE=10\` — each MCP process binds the
|
|
782
|
+
first free port in the range, so several sessions can run at once. The shipped
|
|
783
|
+
hooks find their own session's sidecar by probing the range and matching
|
|
784
|
+
\`agentId\` on \`/health\`; \`memory_whoami\` reports the port actually bound.
|
|
785
|
+
|
|
786
|
+
Recall pipeline:
|
|
787
|
+
- \`AWM_DISABLE_POOL_FILTER=1\` — disables the candidate pool reduction
|
|
788
|
+
pre-filter in recall. Reverts to scoring all active candidates.
|
|
789
|
+
- \`AWM_ENTITY_INDEX_FETCH=1\` — see "Entity index" above (default off; measured
|
|
790
|
+
no effect on the real-store benchmark, 0.14.5).
|
|
791
|
+
- \`AWM_DISABLE_SLIM_CACHE=1\` — disables the in-memory slim cache.
|
|
792
|
+
Reverts to per-recall SQL fetch + Buffer→Float32Array conversion.
|
|
793
|
+
- \`AWM_FEEDBACK_BM25=1\` — re-enables Rocchio pseudo-relevance feedback
|
|
794
|
+
(phase 3.5), a third BM25 pass over the query plus five terms harvested from
|
|
795
|
+
the top 3 results. Default OFF since 2026-10-09: measured at 6.7–7.0% of every
|
|
796
|
+
warm recall for no measurable accuracy — 3 gold ranks moved in 950 queries
|
|
797
|
+
across three suites, two of them the wrong way. On the public corpus — the only
|
|
798
|
+
suite with recall headroom left — it is inert per query, not just in aggregate:
|
|
799
|
+
0 of 200 queries differ in rank, top-1 or score.
|
|
800
|
+
- \`AWM_DISABLE_EXPANSION_CACHE=1\` — disables the query expansion skip
|
|
801
|
+
heuristic + LRU cache. Forces every recall through flan-t5-small.
|
|
802
|
+
|
|
803
|
+
Write pipeline + lifecycle:
|
|
804
|
+
- \`AWM_SLOW_WRITE_MS=250\` — any write slower than this logs one stderr
|
|
805
|
+
line with a phase-time breakdown (embed/novelty/persist, event-loop lag,
|
|
806
|
+
embed-model cold-load ms). \`0\` disables. Useful for diagnosing why a session's
|
|
807
|
+
first write/recall feels slow.
|
|
808
|
+
- \`AWM_REINFORCE_MAX_CONTENT_LEN=1500\` — max chars an engram's content
|
|
809
|
+
can grow to via merge-on-reinforce (drop-oldest on overflow). Higher =
|
|
810
|
+
preserves more reinforced detail; lower = leaner recall output.
|
|
811
|
+
- \`AWM_REINFORCE_MERGE_CONTENT=0\` — disable content merge on reinforce.
|
|
812
|
+
Reverts to pre-0.8.5 behavior (discard new content, only bump confidence).
|
|
813
|
+
- \`AWM_NOVELTY_EMBED=0\` — disable the cosine channel in novelty
|
|
814
|
+
computation. BM25-only fallback. Reverts to pre-0.8.5 novelty.
|
|
815
|
+
- \`AWM_GRANULARITY_COMPACT_LEN=200\` — char budget for query-aware snippet
|
|
816
|
+
in \`granularity: 'compact'\` mode.
|
|
817
|
+
- \`AWM_GRANULARITY_FULL_LEN=1000\` — char budget for the top result in
|
|
818
|
+
\`granularity: 'auto'\` mode when there's a clear winner.
|
|
819
|
+
|
|
820
|
+
PGlite backend:
|
|
821
|
+
- \`AWM_PGLITE_BM25_M=1\` — multiplier on PGlite \`ts_rank_cd\` to calibrate
|
|
822
|
+
against SQLite FTS5 BM25 distribution. M=1 (default) is passthrough;
|
|
823
|
+
higher M boosts PGlite scores at the cost of recall-ranking precision
|
|
824
|
+
(see CHANGELOG 0.8.5 follow-up).
|
|
825
|
+
- \`AWM_IVFFLAT_PROBES=5\` — pgvector ivfflat probes per query. Higher =
|
|
826
|
+
more accurate, slower.
|
|
827
|
+
|
|
828
|
+
In production, leave these all unset. Use only when diagnosing a suspected
|
|
829
|
+
recall-quality regression.
|
|
748
830
|
`.trimStart();
|
|
749
831
|
//# sourceMappingURL=common.js.map
|