agent-working-memory 0.16.1 → 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.
Files changed (170) hide show
  1. package/README.md +321 -318
  2. package/dist/adapters/common.d.ts +34 -0
  3. package/dist/adapters/common.d.ts.map +1 -1
  4. package/dist/adapters/common.js +528 -444
  5. package/dist/adapters/common.js.map +1 -1
  6. package/dist/api/routes.d.ts.map +1 -1
  7. package/dist/api/routes.js +19 -0
  8. package/dist/api/routes.js.map +1 -1
  9. package/dist/cli/migrate.d.ts.map +1 -1
  10. package/dist/cli/migrate.js +31 -29
  11. package/dist/cli/migrate.js.map +1 -1
  12. package/dist/core/asked-value.d.ts +37 -0
  13. package/dist/core/asked-value.d.ts.map +1 -0
  14. package/dist/core/asked-value.js +77 -0
  15. package/dist/core/asked-value.js.map +1 -0
  16. package/dist/core/conflicts.d.ts +89 -0
  17. package/dist/core/conflicts.d.ts.map +1 -0
  18. package/dist/core/conflicts.js +267 -0
  19. package/dist/core/conflicts.js.map +1 -0
  20. package/dist/core/embeddings.d.ts +14 -2
  21. package/dist/core/embeddings.d.ts.map +1 -1
  22. package/dist/core/embeddings.js +32 -10
  23. package/dist/core/embeddings.js.map +1 -1
  24. package/dist/core/format-recall.d.ts +21 -1
  25. package/dist/core/format-recall.d.ts.map +1 -1
  26. package/dist/core/format-recall.js +60 -5
  27. package/dist/core/format-recall.js.map +1 -1
  28. package/dist/core/ml-worker.d.ts +24 -1
  29. package/dist/core/ml-worker.d.ts.map +1 -1
  30. package/dist/core/ml-worker.js +165 -4
  31. package/dist/core/ml-worker.js.map +1 -1
  32. package/dist/core/model-daemon-client.d.ts +77 -0
  33. package/dist/core/model-daemon-client.d.ts.map +1 -0
  34. package/dist/core/model-daemon-client.js +383 -0
  35. package/dist/core/model-daemon-client.js.map +1 -0
  36. package/dist/core/model-daemon-server.d.ts +71 -0
  37. package/dist/core/model-daemon-server.d.ts.map +1 -0
  38. package/dist/core/model-daemon-server.js +363 -0
  39. package/dist/core/model-daemon-server.js.map +1 -0
  40. package/dist/core/model-daemon.d.ts +137 -0
  41. package/dist/core/model-daemon.d.ts.map +1 -0
  42. package/dist/core/model-daemon.js +231 -0
  43. package/dist/core/model-daemon.js.map +1 -0
  44. package/dist/core/query-expander.d.ts +5 -0
  45. package/dist/core/query-expander.d.ts.map +1 -1
  46. package/dist/core/query-expander.js +3 -2
  47. package/dist/core/query-expander.js.map +1 -1
  48. package/dist/core/recall-config.d.ts +1 -1
  49. package/dist/core/recall-config.d.ts.map +1 -1
  50. package/dist/core/recall-config.js +5 -0
  51. package/dist/core/recall-config.js.map +1 -1
  52. package/dist/core/rerank-window.d.ts +7 -2
  53. package/dist/core/rerank-window.d.ts.map +1 -1
  54. package/dist/core/rerank-window.js +9 -4
  55. package/dist/core/rerank-window.js.map +1 -1
  56. package/dist/core/rerank2.d.ts +52 -2
  57. package/dist/core/rerank2.d.ts.map +1 -1
  58. package/dist/core/rerank2.js +60 -3
  59. package/dist/core/rerank2.js.map +1 -1
  60. package/dist/core/reranker.d.ts +19 -0
  61. package/dist/core/reranker.d.ts.map +1 -1
  62. package/dist/core/reranker.js +76 -12
  63. package/dist/core/reranker.js.map +1 -1
  64. package/dist/core/salience.d.ts +2 -0
  65. package/dist/core/salience.d.ts.map +1 -1
  66. package/dist/core/salience.js +45 -18
  67. package/dist/core/salience.js.map +1 -1
  68. package/dist/core/trust.d.ts +32 -0
  69. package/dist/core/trust.d.ts.map +1 -0
  70. package/dist/core/trust.js +66 -0
  71. package/dist/core/trust.js.map +1 -0
  72. package/dist/core/validity.d.ts +21 -0
  73. package/dist/core/validity.d.ts.map +1 -0
  74. package/dist/core/validity.js +41 -0
  75. package/dist/core/validity.js.map +1 -0
  76. package/dist/core/whoami.d.ts +3 -0
  77. package/dist/core/whoami.d.ts.map +1 -1
  78. package/dist/core/whoami.js +14 -0
  79. package/dist/core/whoami.js.map +1 -1
  80. package/dist/core/write-pipeline.d.ts +48 -0
  81. package/dist/core/write-pipeline.d.ts.map +1 -1
  82. package/dist/core/write-pipeline.js +104 -4
  83. package/dist/core/write-pipeline.js.map +1 -1
  84. package/dist/engine/activation.d.ts.map +1 -1
  85. package/dist/engine/activation.js +27 -4
  86. package/dist/engine/activation.js.map +1 -1
  87. package/dist/engine/consolidation.d.ts.map +1 -1
  88. package/dist/engine/consolidation.js +67 -5
  89. package/dist/engine/consolidation.js.map +1 -1
  90. package/dist/hooks/prime-query.d.ts +57 -0
  91. package/dist/hooks/prime-query.d.ts.map +1 -0
  92. package/dist/hooks/prime-query.js +388 -0
  93. package/dist/hooks/prime-query.js.map +1 -0
  94. package/dist/hooks/prime.d.ts +8 -0
  95. package/dist/hooks/prime.d.ts.map +1 -1
  96. package/dist/hooks/prime.js +21 -2
  97. package/dist/hooks/prime.js.map +1 -1
  98. package/dist/hooks/sidecar.d.ts +7 -0
  99. package/dist/hooks/sidecar.d.ts.map +1 -1
  100. package/dist/hooks/sidecar.js +10 -2
  101. package/dist/hooks/sidecar.js.map +1 -1
  102. package/dist/index.js +11 -3
  103. package/dist/index.js.map +1 -1
  104. package/dist/mcp.js +148 -103
  105. package/dist/mcp.js.map +1 -1
  106. package/dist/model-daemon.d.ts +8 -0
  107. package/dist/model-daemon.d.ts.map +1 -0
  108. package/dist/model-daemon.js +45 -0
  109. package/dist/model-daemon.js.map +1 -0
  110. package/dist/storage/factory.d.ts.map +1 -1
  111. package/dist/storage/factory.js +5 -0
  112. package/dist/storage/factory.js.map +1 -1
  113. package/dist/storage/pglite-schema.d.ts.map +1 -1
  114. package/dist/storage/pglite-schema.js +176 -170
  115. package/dist/storage/pglite-schema.js.map +1 -1
  116. package/dist/storage/pglite.d.ts +5 -0
  117. package/dist/storage/pglite.d.ts.map +1 -1
  118. package/dist/storage/pglite.js +165 -153
  119. package/dist/storage/pglite.js.map +1 -1
  120. package/dist/storage/postgres.d.ts +5 -0
  121. package/dist/storage/postgres.d.ts.map +1 -1
  122. package/dist/storage/postgres.js +165 -153
  123. package/dist/storage/postgres.js.map +1 -1
  124. package/dist/storage/sqlite.d.ts +22 -0
  125. package/dist/storage/sqlite.d.ts.map +1 -1
  126. package/dist/storage/sqlite.js +400 -363
  127. package/dist/storage/sqlite.js.map +1 -1
  128. package/dist/types/engram.d.ts +33 -0
  129. package/dist/types/engram.d.ts.map +1 -1
  130. package/dist/types/engram.js.map +1 -1
  131. package/package.json +106 -106
  132. package/plugin/.claude-plugin/plugin.json +1 -1
  133. package/plugin/README.md +1 -1
  134. package/plugin/bin/awm-mcp-launcher.cjs +2 -2
  135. package/plugin/skills/awm-memory/SKILL.md +16 -5
  136. package/src/adapters/common.ts +881 -800
  137. package/src/api/routes.ts +20 -0
  138. package/src/cli/migrate.ts +309 -307
  139. package/src/core/asked-value.ts +74 -0
  140. package/src/core/conflicts.ts +280 -0
  141. package/src/core/embeddings.ts +35 -10
  142. package/src/core/format-recall.ts +83 -25
  143. package/src/core/ml-worker.ts +458 -281
  144. package/src/core/model-daemon-client.ts +374 -0
  145. package/src/core/model-daemon-server.ts +344 -0
  146. package/src/core/model-daemon.ts +243 -0
  147. package/src/core/query-expander.ts +127 -126
  148. package/src/core/recall-config.ts +5 -0
  149. package/src/core/rerank-window.ts +164 -159
  150. package/src/core/rerank2.ts +143 -82
  151. package/src/core/reranker.ts +78 -11
  152. package/src/core/salience.ts +45 -18
  153. package/src/core/trust.ts +65 -0
  154. package/src/core/validity.ts +47 -0
  155. package/src/core/whoami.ts +136 -120
  156. package/src/core/write-pipeline.ts +143 -4
  157. package/src/engine/activation.ts +27 -4
  158. package/src/engine/consolidation.ts +950 -887
  159. package/src/hooks/prime-query.ts +375 -0
  160. package/src/hooks/prime.ts +25 -2
  161. package/src/hooks/sidecar.ts +13 -3
  162. package/src/index.ts +255 -248
  163. package/src/mcp.ts +1521 -1475
  164. package/src/model-daemon.ts +48 -0
  165. package/src/storage/factory.ts +25 -19
  166. package/src/storage/pglite-schema.ts +199 -193
  167. package/src/storage/pglite.ts +1513 -1498
  168. package/src/storage/postgres.ts +1613 -1598
  169. package/src/storage/sqlite.ts +2056 -2019
  170. package/src/types/engram.ts +30 -0
@@ -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,448 +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 …]\` on results carrying this field. Use
448
- \`valid_from\` for a fact that becomes true on a known future date (a policy change, a
449
- season that hasn't started yet). Don't set either for durable facts — most memories
450
- don't need them.
451
-
452
- ### Memory classes (controls how strictly the salience filter gates the write)
453
- - \`memory_class: canonical\` — source-of-truth memories. Floor 0.7 salience, never staged.
454
- Use for: user-stated decisions, project requirements, verified architectural facts,
455
- cross-agent shared context. **In a hive (multi-agent) setup, always use \`canonical\`
456
- for writes that other agents must be able to recall** — the default \`working\` class
457
- may get filtered.
458
- - \`memory_class: working\` (default) — observations and findings. Salience-gated.
459
- - \`memory_class: ephemeral\` — short-lived context that should decay quickly.
460
-
461
- ### Salience auto-promotion (defense in depth)
462
- The salience filter automatically promotes certain content patterns even if you forget
463
- to set \`memory_class\` explicitly:
464
- - **User feedback** — content starting with "Robert said…", "Katherine directed…",
465
- "Nancy decided…" etc. auto-promotes to canonical. So quoting the user verbatim
466
- always preserves the decision.
467
- - **Verified operational records** — content with an action verb (Submitted, Finalized,
468
- Completed, Reconciled, Triaged, Posted, Resolved, Stamped, Pushed, Deployed, Migrated,
469
- Imported, Exported, Backfilled) plus 2+ concrete identifiers (ISO date \`YYYY-MM-DD\`,
470
- or contextual numeric IDs like "event 18969", "ticket #18330", "USEF 341980") gets
471
- a 0.45 salience floor. So batch summaries with real IDs survive even when topic
472
- terms repeat.
473
-
474
- If neither pattern applies and you want a memory to definitely survive, set
475
- \`memory_class: canonical\` explicitly. Don't rely on auto-promotion for important writes.
476
-
477
- ### Recall memory when:
478
- - **BEFORE stating ANY fact about how a system works** — recall first; if AWM doesn't
479
- have it, read the code. Never guess and present it as fact.
480
- - **BEFORE searching the filesystem** — recall first; AWM is faster and has cross-session
481
- knowledge that file search doesn't.
482
- - Starting work on a new task or subsystem
483
- - Re-entering code you haven't touched recently
484
- - After a failed attempt — check if there's prior knowledge
485
- - Before refactoring or making architectural changes
486
- - When a topic comes up that you might have prior context on
487
-
488
- Recall is fast (~300ms typical). Use it freely.
489
-
490
- ### Chain recalls — one hop per call
491
-
492
- AWM returns what matches your query. It does **not** walk from that answer to the next
493
- fact, and it is not meant to: it is active memory, holding what you need right now.
494
- Following a chain to the next thing is **your** job — recall, read, recall again, each
495
- result becoming the cue for the next query.
496
-
497
- So when a question refers to something **by its role instead of its name**, you have a
498
- chain, not a query:
499
-
500
- > *"What is the codename of the project owned by my scheduler?"*
501
-
502
- That is three lookups, not one:
503
-
504
- 1. recall \`who is my scheduler\` → a name
505
- 2. recall \`what project does <that name> own\` → a project
506
- 3. recall \`<that project> codename\` → the answer
507
-
508
- **The failure mode is answering from the first recall.** A single query blending all
509
- three terms returns the most *salient* related memory, not the correct one — measured,
510
- that returns the main project's codename with complete confidence, and it is wrong.
511
- Each hop on its own is an ordinary, reliable recall; the chain only breaks when you stop
512
- asking.
513
-
514
- Tells that you are looking at a chain: a possessive or relative clause naming an entity
515
- by role rather than identity (\`my scheduler\`, \`the owner of X\`, \`whoever signed
516
- off on Y\`), or a rule that operates on an attribute you have not looked up yet (\`the
517
- release tag of the project we discussed\` needs that project's codename first). If
518
- resolving the question requires a fact you would have to *derive*, recall the fact
519
- instead.
520
-
521
- ### Recall strategy (when one query isn't enough)
522
- AWM's adaptive retrieval handles most query variations natively — synonym
523
- expansion, multi-channel scoring, embedding + BM25 + reranker agreement.
524
- A single recall is usually enough.
525
-
526
- When it isn't:
527
- - **If the first recall returns nothing or returns the wrong things, reformulate.**
528
- Try a second query with different phrasing — synonyms, more specific nouns,
529
- the exact identifier from the code rather than the conceptual name. Two or
530
- three recalls cost less than one filesystem search.
531
- - **Use the words a domain expert would use, not generic English.** "Period
532
- close lock" not "accounting feature"; "magic link rate limit" not "auth issue."
533
- - **For broad exploration, pass \`mode: "exploratory"\`** — wider candidate
534
- pool, lower precision floor. For specific lookups, leave mode unset (auto).
535
- - **Don't ensemble more than 3 reformulations.** If three different phrasings
536
- return nothing, the memory probably isn't there — read the code instead of
537
- burning more recalls.
538
-
539
- ### Recall tuning — opt-in parameters
540
- Default \`memory_recall\` is tuned for the common case. Four opt-in parameters change
541
- the cost/quality tradeoff. Use them when the default doesn't match what you need.
542
-
543
- - **\`granularity: 'compact'\`** — every result carries a 200-char \`summary\`
544
- field with a query-aware snippet (the densest window of query terms in the
545
- content). Use this when you expect to scan 5+ results to find one — saves
546
- ~70% of recall output tokens. The full content stays available in
547
- \`engram.content\` if you want to drill into a specific result.
548
- - **\`granularity: 'auto'\`** — confidence-adaptive. If the top result is a clear
549
- winner, it gets a longer summary while the rest are compact. If confidence
550
- is uniform across results, everything is compact. Use when you don't know
551
- in advance whether one result will dominate.
552
- - **\`require_confidence\`** — abstention. The default (0.05) is already a light
553
- gate: when the top results don't stand out from the rest, recall returns
554
- nothing and the reply says **\`RECALL ABSTAINED\`** with the count it withheld.
555
- An empty result *without* that line is genuine absence. The gate is on the
556
- SHAPE of the score distribution, not relevance — so raising it silences
557
- specific questions with one clear winner first (measured: 0.25 abstained on
558
- two identifier queries scoring 0.26–0.38 while passing a vague one at 0.92).
559
- Leave it alone for ordinary recall; \`0.10\` is the ceiling that still helps for
560
- push-style use where nobody asked. When abstention fires, reformulate once
561
- with the exact identifier, then read the code — don't retry with a lower
562
- threshold to force an answer.
563
- - **\`workspace: "<name>"\`** — hive-mode recall across all agents in the
564
- workspace. Use when other agents may have written canonical knowledge you
565
- need. Default is agent-scoped (your own memories only). Can also be set
566
- globally via the \`AWM_WORKSPACE\` env var. Memories live in the agent pool
567
- they were written to; if a session lands in an unexpected pool, \`memory_whoami\`
568
- says which one before you conclude the memory is missing.
569
-
570
- ### Keep memory fresh
571
- - After recalling a memory, if you observe the real state is different → call
572
- \`memory_supersede\` immediately with the corrected version.
573
- - After using a recalled memory: call \`memory_feedback\` (useful/not-useful) **with the
574
- \`recall_id\`** printed at the end of the recall output (\`[recall_id: …]\`). That id
575
- joins the feedback to the recall that produced it, which is what lets AWM learn
576
- which recalls were worth their tokens; feedback without it is a bare vote.
577
- - If you discover a memory is factually wrong: \`memory_retract\` to remove it.
578
- - **If you bypass AWM (file-memory, in-context notes, "I'll just remember"), the memory
579
- drifts out of date. The system relies on you to keep it current. This is the #1
580
- failure mode.**
581
-
582
- ### Cognition recipes — YOU do the thinking, AWM keeps the result
583
- AWM contains no LLM. When memory needs real thinking — distilling a repeatable
584
- procedure, reflecting on a failure — AWM hands YOU a versioned recipe (prompt +
585
- strict output shape) and you run it as a SEPARATE focused pass, then write the
586
- result back as an ordinary memory with provenance.
587
-
588
- - \`memory_task_end\` responses include the recipe invitations. Honor the gates:
589
- skill-derivation only after a genuinely procedural task (3+ tool calls or a
590
- delegated sub-task); friction-lesson only after a failure/retry/wrong assumption.
591
- - Run each recipe as its own focused pass — do NOT bundle it with other
592
- reasoning; bundled passes reliably drop the output.
593
- - Write back exactly per the recipe's contract: \`origin_class: 'recipe'\` +
594
- \`recipe_id\` (e.g. \`skill-derivation@1\`), concept prefixed \`skill: \` or
595
- \`lesson: \`. AWM validates the shape and rejects malformed or unknown-recipe
596
- writes with the contract echoed back — fix and retry, don't drop the insight.
597
- - Re-deriving the same skill name reinforces the existing memory instead of
598
- duplicating it, so don't fear writing a skill you may have written before.
599
-
600
- ### Content fade — write-and-forget is safe
601
- Un-recalled engrams gradually fade their content while preserving cue pathways
602
- (concept + tags + embedding stay intact). This is Paper 1 — storage
603
- degradation. Practical implications:
604
-
605
- - **Don't manually purge memories** to "save space." The system already
606
- compresses unused content. Old memories stay findable via cue match even
607
- when their body has decayed.
608
- - **Don't over-pin with \`memory_class: canonical\`** to fight fade. Canonical
609
- only changes salience gating at write time, not fade behavior. Fade
610
- affects un-recalled engrams of any class.
611
- - **Recall keeps content alive.** Every recall touches the engram and resets
612
- its fade clock. Frequently-recalled memories stay full-fidelity automatically.
613
- - **Supersede is the right tool for stale facts.** When you observe a memory
614
- is outdated, call \`memory_supersede\` — the new version inherits the old
615
- one's coherent associations (counter-narrative replacement) so cue
616
- pathways carry forward to the replacement.
617
-
618
- ### Example — good vs bad memory_write
619
-
620
- **BAD** (no prefix tags, vague concept, can't be recalled by future queries):
621
- \`\`\`
622
- memory_write(
623
- concept="found a bug",
624
- content="The thing I was looking at was broken so I fixed it."
625
- )
626
- \`\`\`
627
-
628
- **GOOD** (rich identifiers, structured metadata, prefix tags):
629
- \`\`\`
630
- memory_write(
631
- concept="EquiHub period-close BLOCKED check missing server-side",
632
- 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.",
633
- project="EquiHub",
634
- topic="accounting",
635
- intent="finding",
636
- confidence_level="verified",
637
- source="debugging",
638
- memory_class="canonical",
639
- tags=["ticket=10001", "person=Robert", "date=2026-05-11", "topic=period-close", "topic=security"]
640
- )
641
- \`\`\`
642
-
643
- ### Also:
644
- - To track work items: memory_task_add, memory_task_update, memory_task_list, memory_task_next
645
- - \`memory_whoami\` (MCP tool) / \`GET /whoami\` — identify the instance you're actually
646
- talking to: agent id, workspace, mode, backend, store path, code provenance, sibling
647
- agent spaces sharing the store. Call this FIRST whenever you're unsure which store,
648
- which agent identity, or which running code you're dealing with — before reasoning
649
- about AWM's own state from a stale memory or an assumed port number.
650
- - AWM is shared across all agents in real time. When any agent writes or supersedes a
651
- memory, every other agent can recall it immediately — but only within the same
652
- workspace and agent scope.
653
-
654
- ### Output compression (token efficiency, output-only)
655
- When a tool returns a LARGE STRUCTURED result you need to keep in context — a JSON
656
- array of records, query rows, a log dump, an API response — pass it through
657
- \`compress_output\` first. It re-encodes the data as TOON (a compact, lossless,
658
- schema-aware tabular form of JSON), cutting ~50-65% of the tokens at no
659
- comprehension cost. This is output-only: it never changes the data or your memories.
660
- - Use it on big STRUCTURED outputs, not on prose. Prose is returned unchanged —
661
- for trimming memory prose, use recall \`granularity: 'compact'\` instead.
662
- - It returns a \`ref\`; call \`retrieve_original(ref)\` if you later need the exact
663
- verbatim source (e.g. to hand it to another tool unchanged).
664
- - Don't bother for small outputs — it only compresses when the saving is worthwhile
665
- and falls back to plain JSON if TOON wouldn't reproduce the data exactly.
666
-
667
- ### Backend (SQLite vs PGlite)
668
- AWM ships two storage backends. The installer picks SQLite by default; both
669
- are functionally equivalent for cognitive workloads, but differ in operational
670
- guarantees:
671
-
672
- - **SQLite** (default) — embedded, **multi-process safe** via WAL mode. Best
673
- for single-machine setups and MCP scenarios where multiple Claude Code
674
- sessions may open the same database concurrently.
675
- - **PGlite** — embedded Postgres (WASM) with pgvector. **Single-process only**
676
- — two MCP processes against the same \`memory-pglite/\` directory will
677
- abort the second. Pick via \`AWM_STORE_BACKEND=pglite\` and
678
- \`AWM_DB_PATH=path/to/memory-pglite\`.
679
- - **Auto-detect** — if \`AWM_DB_PATH\` points to a directory that already
680
- exists, AWM detects PGlite; a file → SQLite. No explicit
681
- \`AWM_STORE_BACKEND\` needed when an existing DB is present.
682
-
683
- For the comparison table (recall quality parity, BM25 vs \`ts_rank_cd\`,
684
- multi-process guarantees), see \`docs/pglite-feature-parity.md\`.
685
-
686
- ### Diagnostics / escape hatches (env vars, only if you know why)
687
- Each optimization is gated by an env var so it can be disabled for A/B testing if a
688
- regression appears in your workload. \`memory_whoami\` prints the active fingerprint.
689
-
690
- Retrieval (set ON by \`awm setup\` since 0.14.6 — the measured-good configuration):
691
- - \`AWM_RERANK2=1\` — second-stage rerank by the cross-encoder's own score.
692
- - \`AWM_RERANK_WINDOW=query\` — 400-char rerank window on the densest query-term
693
- region instead of the prefix (25% → 87.5% on long memories).
694
- - \`AWM_RERANK_TAGS=1\` — tags fed into the rerank passage (+7.4pp s@1).
695
-
696
- Hook sidecar (0.14.2):
697
- - \`AWM_HOOK_PORT=8401\` / \`AWM_HOOK_PORT_RANGE=10\` — each MCP process binds the
698
- first free port in the range, so several sessions can run at once. The shipped
699
- hooks find their own session's sidecar by probing the range and matching
700
- \`agentId\` on \`/health\`; \`memory_whoami\` reports the port actually bound.
701
-
702
- Recall pipeline:
703
- - \`AWM_DISABLE_POOL_FILTER=1\` — disables the candidate pool reduction
704
- pre-filter in recall. Reverts to scoring all active candidates.
705
- - \`AWM_ENTITY_INDEX_FETCH=1\` — see "Entity index" above (default off; measured
706
- no effect on the real-store benchmark, 0.14.5).
707
- - \`AWM_DISABLE_SLIM_CACHE=1\` — disables the in-memory slim cache.
708
- Reverts to per-recall SQL fetch + Buffer→Float32Array conversion.
709
- - \`AWM_FEEDBACK_BM25=1\` — re-enables Rocchio pseudo-relevance feedback
710
- (phase 3.5), a third BM25 pass over the query plus five terms harvested from
711
- the top 3 results. Default OFF since 2026-10-09: measured at 6.7–7.0% of every
712
- warm recall for no measurable accuracy — 3 gold ranks moved in 950 queries
713
- across three suites, two of them the wrong way. On the public corpus — the only
714
- suite with recall headroom left — it is inert per query, not just in aggregate:
715
- 0 of 200 queries differ in rank, top-1 or score.
716
- - \`AWM_DISABLE_EXPANSION_CACHE=1\` — disables the query expansion skip
717
- heuristic + LRU cache. Forces every recall through flan-t5-small.
718
-
719
- Write pipeline + lifecycle:
720
- - \`AWM_SLOW_WRITE_MS=250\` — any write slower than this logs one stderr
721
- line with a phase-time breakdown (embed/novelty/persist, event-loop lag,
722
- embed-model cold-load ms). \`0\` disables. Useful for diagnosing why a session's
723
- first write/recall feels slow.
724
- - \`AWM_REINFORCE_MAX_CONTENT_LEN=1500\` — max chars an engram's content
725
- can grow to via merge-on-reinforce (drop-oldest on overflow). Higher =
726
- preserves more reinforced detail; lower = leaner recall output.
727
- - \`AWM_REINFORCE_MERGE_CONTENT=0\` — disable content merge on reinforce.
728
- Reverts to pre-0.8.5 behavior (discard new content, only bump confidence).
729
- - \`AWM_NOVELTY_EMBED=0\` — disable the cosine channel in novelty
730
- computation. BM25-only fallback. Reverts to pre-0.8.5 novelty.
731
- - \`AWM_GRANULARITY_COMPACT_LEN=200\` — char budget for query-aware snippet
732
- in \`granularity: 'compact'\` mode.
733
- - \`AWM_GRANULARITY_FULL_LEN=1000\` — char budget for the top result in
734
- \`granularity: 'auto'\` mode when there's a clear winner.
735
-
736
- PGlite backend:
737
- - \`AWM_PGLITE_BM25_M=1\` — multiplier on PGlite \`ts_rank_cd\` to calibrate
738
- against SQLite FTS5 BM25 distribution. M=1 (default) is passthrough;
739
- higher M boosts PGlite scores at the cost of recall-ranking precision
740
- (see CHANGELOG 0.8.5 follow-up).
741
- - \`AWM_IVFFLAT_PROBES=5\` — pgvector ivfflat probes per query. Higher =
742
- more accurate, slower.
743
-
744
- In production, leave these all unset. Use only when diagnosing a suspected
745
- recall-quality regression.
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.
746
830
  `.trimStart();
747
831
  //# sourceMappingURL=common.js.map