agent-working-memory 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/README.md +8 -5
  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 -446
  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 +90 -13
  63. package/dist/core/reranker.js.map +1 -1
  64. package/dist/core/trust.d.ts +32 -0
  65. package/dist/core/trust.d.ts.map +1 -0
  66. package/dist/core/trust.js +66 -0
  67. package/dist/core/trust.js.map +1 -0
  68. package/dist/core/validity.d.ts +21 -0
  69. package/dist/core/validity.d.ts.map +1 -0
  70. package/dist/core/validity.js +41 -0
  71. package/dist/core/validity.js.map +1 -0
  72. package/dist/core/whoami.d.ts +3 -0
  73. package/dist/core/whoami.d.ts.map +1 -1
  74. package/dist/core/whoami.js +14 -0
  75. package/dist/core/whoami.js.map +1 -1
  76. package/dist/core/write-pipeline.d.ts +48 -0
  77. package/dist/core/write-pipeline.d.ts.map +1 -1
  78. package/dist/core/write-pipeline.js +80 -3
  79. package/dist/core/write-pipeline.js.map +1 -1
  80. package/dist/engine/activation.d.ts.map +1 -1
  81. package/dist/engine/activation.js +27 -4
  82. package/dist/engine/activation.js.map +1 -1
  83. package/dist/engine/consolidation.d.ts.map +1 -1
  84. package/dist/engine/consolidation.js +53 -4
  85. package/dist/engine/consolidation.js.map +1 -1
  86. package/dist/hooks/prime-query.d.ts +57 -0
  87. package/dist/hooks/prime-query.d.ts.map +1 -0
  88. package/dist/hooks/prime-query.js +388 -0
  89. package/dist/hooks/prime-query.js.map +1 -0
  90. package/dist/hooks/prime.d.ts +8 -0
  91. package/dist/hooks/prime.d.ts.map +1 -1
  92. package/dist/hooks/prime.js +21 -2
  93. package/dist/hooks/prime.js.map +1 -1
  94. package/dist/hooks/sidecar.d.ts +18 -0
  95. package/dist/hooks/sidecar.d.ts.map +1 -1
  96. package/dist/hooks/sidecar.js +58 -2
  97. package/dist/hooks/sidecar.js.map +1 -1
  98. package/dist/index.js +15 -3
  99. package/dist/index.js.map +1 -1
  100. package/dist/mcp.js +157 -104
  101. package/dist/mcp.js.map +1 -1
  102. package/dist/model-daemon.d.ts +8 -0
  103. package/dist/model-daemon.d.ts.map +1 -0
  104. package/dist/model-daemon.js +45 -0
  105. package/dist/model-daemon.js.map +1 -0
  106. package/dist/storage/factory.d.ts.map +1 -1
  107. package/dist/storage/factory.js +5 -0
  108. package/dist/storage/factory.js.map +1 -1
  109. package/dist/storage/pglite-schema.d.ts.map +1 -1
  110. package/dist/storage/pglite-schema.js +176 -170
  111. package/dist/storage/pglite-schema.js.map +1 -1
  112. package/dist/storage/pglite.d.ts +5 -0
  113. package/dist/storage/pglite.d.ts.map +1 -1
  114. package/dist/storage/pglite.js +165 -153
  115. package/dist/storage/pglite.js.map +1 -1
  116. package/dist/storage/postgres.d.ts +5 -0
  117. package/dist/storage/postgres.d.ts.map +1 -1
  118. package/dist/storage/postgres.js +165 -153
  119. package/dist/storage/postgres.js.map +1 -1
  120. package/dist/storage/sqlite.d.ts +22 -0
  121. package/dist/storage/sqlite.d.ts.map +1 -1
  122. package/dist/storage/sqlite.js +415 -363
  123. package/dist/storage/sqlite.js.map +1 -1
  124. package/dist/types/engram.d.ts +33 -0
  125. package/dist/types/engram.d.ts.map +1 -1
  126. package/dist/types/engram.js.map +1 -1
  127. package/package.json +1 -1
  128. package/plugin/.claude-plugin/plugin.json +1 -1
  129. package/plugin/README.md +1 -1
  130. package/plugin/bin/awm-mcp-launcher.cjs +2 -2
  131. package/plugin/skills/awm-memory/SKILL.md +11 -2
  132. package/src/adapters/common.ts +881 -802
  133. package/src/api/routes.ts +20 -0
  134. package/src/cli/migrate.ts +309 -307
  135. package/src/core/asked-value.ts +74 -0
  136. package/src/core/conflicts.ts +280 -0
  137. package/src/core/embeddings.ts +35 -10
  138. package/src/core/format-recall.ts +83 -25
  139. package/src/core/ml-worker.ts +458 -281
  140. package/src/core/model-daemon-client.ts +374 -0
  141. package/src/core/model-daemon-server.ts +344 -0
  142. package/src/core/model-daemon.ts +243 -0
  143. package/src/core/query-expander.ts +127 -126
  144. package/src/core/recall-config.ts +5 -0
  145. package/src/core/rerank-window.ts +164 -159
  146. package/src/core/rerank2.ts +143 -82
  147. package/src/core/reranker.ts +92 -12
  148. package/src/core/trust.ts +65 -0
  149. package/src/core/validity.ts +47 -0
  150. package/src/core/whoami.ts +136 -120
  151. package/src/core/write-pipeline.ts +119 -3
  152. package/src/engine/activation.ts +27 -4
  153. package/src/engine/consolidation.ts +950 -899
  154. package/src/hooks/prime-query.ts +375 -0
  155. package/src/hooks/prime.ts +25 -2
  156. package/src/hooks/sidecar.ts +57 -3
  157. package/src/index.ts +258 -248
  158. package/src/mcp.ts +1529 -1475
  159. package/src/model-daemon.ts +48 -0
  160. package/src/storage/factory.ts +25 -19
  161. package/src/storage/pglite-schema.ts +199 -193
  162. package/src/storage/pglite.ts +1513 -1498
  163. package/src/storage/postgres.ts +1613 -1598
  164. package/src/storage/sqlite.ts +2069 -2019
  165. package/src/types/engram.ts +30 -0
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Conflict surfacing among the memories ONE recall returns.
3
+ *
4
+ * AWM keeps contradictions on purpose (edge-case test 7 requires both sides of
5
+ * a contradiction in the top 5): settling them is the agent's job, with the
6
+ * source. What it did not do, until 2026-10-19, was SAY that two returned
7
+ * memories disagree, or which is newer. The MWA evaluation probe showed the
8
+ * cost: "departs 10:15" and "now departs 8:40" came back as two undated lines,
9
+ * the stale one first, and nothing on the page told the model it was looking
10
+ * at two versions of one fact. In the BL-40 assessment "newer facts lose to
11
+ * older sources" cost four checks.
12
+ *
13
+ * This is a detector over the returned set only — never over the store — so it
14
+ * costs a handful of string comparisons per recall. It is deliberately
15
+ * conservative and its output is a QUESTION ("possible conflict … confirm
16
+ * against the source"), not a verdict: a false mark costs the agent one check,
17
+ * a missed one costs a wrong answer stated with confidence.
18
+ *
19
+ * Two memories are flagged when
20
+ * 1. they are about the same thing — related by embedding cosine, or by word
21
+ * overlap when an embedding is missing — and
22
+ * 2. they carry a value of the same kind (a time, a date, a money amount, a
23
+ * bare number, a status word) and those values differ, and
24
+ * 3. neither supersedes the other and neither is already stale (superseded,
25
+ * expired, not yet valid) — those are marked elsewhere.
26
+ *
27
+ * Known limit, measured in tests/core/conflicts.test.ts: "template twins" —
28
+ * two genuinely distinct facts that share a template ("flight out 8:40",
29
+ * "flight back 10:15") — are flagged. That is the precision cost of (1)+(2)
30
+ * without a parser, and the reason the mark is phrased as a question.
31
+ */
32
+ export type SlotKind = 'time' | 'date' | 'money' | 'number' | 'status';
33
+ /** What this needs from a result — any ActivationResult qualifies. */
34
+ export interface ConflictCandidate {
35
+ engram: {
36
+ id: string;
37
+ concept: string;
38
+ content: string;
39
+ embedding?: number[] | null;
40
+ createdAt?: Date | string | null;
41
+ supersededBy?: string | null;
42
+ supersedes?: string | null;
43
+ validFrom?: string | null;
44
+ validTo?: string | null;
45
+ };
46
+ }
47
+ export interface ConflictMark {
48
+ /** The other memory in the pair. */
49
+ id: string;
50
+ /** True when THIS memory was written after the other one. */
51
+ newer: boolean;
52
+ /** The kind of value the two disagree on. */
53
+ slot: SlotKind;
54
+ }
55
+ /**
56
+ * The comparable values in a text, by kind. Text matched by an earlier,
57
+ * more specific kind is blanked before a later kind runs, so "3:30pm" is one
58
+ * time rather than a time and the numbers 3 and 30, and "$49" is money, not
59
+ * a number.
60
+ */
61
+ export declare function valueSlots(text: string): Map<SlotKind, Set<string>>;
62
+ /** True when the two memories look like they are about the same thing. */
63
+ export declare function related(a: ConflictCandidate, b: ConflictCandidate): boolean;
64
+ /** The first kind of value present in BOTH texts on which they disagree, or null. */
65
+ export declare function disagreement(a: ConflictCandidate, b: ConflictCandidate): SlotKind | null;
66
+ /**
67
+ * Pairwise over the returned set. Returns one list of marks per input, in
68
+ * input order; an empty list means no conflict for that memory.
69
+ */
70
+ export declare function detectConflicts(results: ConflictCandidate[], now?: number): ConflictMark[][];
71
+ /**
72
+ * Newest first among results that are two versions of one fact — FOR HARNESSES.
73
+ *
74
+ * MWA's case: "In October the club chose fantasy / December science fiction /
75
+ * March a mystery" for "what did the club most recently choose". The
76
+ * cross-encoder cannot tell them apart, and a sort by its score put the newest
77
+ * last. AWM itself does NOT apply this (0.18.1): a score-epsilon fallback to the
78
+ * blend measured -14pp identifier s@1 (rerank2.ts), and applying this helper
79
+ * inside the engine measured -18pp with s@5 unchanged, because on a real store
80
+ * the detector also marks related memories that differ in a ticket id or a
81
+ * date. The marks are a cue to check the source. A harness whose policy is
82
+ * "newer beats older" (MWA's is) can call this on the returned results with
83
+ * their `conflictsWith` marks. `marks` is parallel to `results`; the returned
84
+ * array is new and its marks still describe the same ids.
85
+ */
86
+ export declare function newestFirstAmongConflicts<T extends ConflictCandidate>(results: T[], marks: ConflictMark[][]): T[];
87
+ /** The id of the most recently written memory among those carrying a mark, or null. */
88
+ export declare function newestConflicting(results: ConflictCandidate[], marks: ConflictMark[][]): string | null;
89
+ //# sourceMappingURL=conflicts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conflicts.d.ts","sourceRoot":"","sources":["../../src/core/conflicts.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAKH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEvE,sEAAsE;AACtE,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE;QACN,EAAE,EAAE,MAAM,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QAC5B,SAAS,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;QACjC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACzB,CAAC;CACH;AAED,MAAM,WAAW,YAAY;IAC3B,oCAAoC;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,6DAA6D;IAC7D,KAAK,EAAE,OAAO,CAAC;IACf,6CAA6C;IAC7C,IAAI,EAAE,QAAQ,CAAC;CAChB;AA4BD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CA4CnE;AAqBD,0EAA0E;AAC1E,wBAAgB,OAAO,CAAC,CAAC,EAAE,iBAAiB,EAAE,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAM3E;AAED,qFAAqF;AACrF,wBAAgB,YAAY,CAAC,CAAC,EAAE,iBAAiB,EAAE,CAAC,EAAE,iBAAiB,GAAG,QAAQ,GAAG,IAAI,CAkBxF;AAoBD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,iBAAiB,EAAE,EAAE,GAAG,GAAE,MAAmB,GAAG,YAAY,EAAE,EAAE,CAoBxG;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,iBAAiB,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,GAAG,CAAC,EAAE,CAmBjH;AAED,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,GAAG,MAAM,GAAG,IAAI,CAQtG"}
@@ -0,0 +1,267 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Conflict surfacing among the memories ONE recall returns.
5
+ *
6
+ * AWM keeps contradictions on purpose (edge-case test 7 requires both sides of
7
+ * a contradiction in the top 5): settling them is the agent's job, with the
8
+ * source. What it did not do, until 2026-10-19, was SAY that two returned
9
+ * memories disagree, or which is newer. The MWA evaluation probe showed the
10
+ * cost: "departs 10:15" and "now departs 8:40" came back as two undated lines,
11
+ * the stale one first, and nothing on the page told the model it was looking
12
+ * at two versions of one fact. In the BL-40 assessment "newer facts lose to
13
+ * older sources" cost four checks.
14
+ *
15
+ * This is a detector over the returned set only — never over the store — so it
16
+ * costs a handful of string comparisons per recall. It is deliberately
17
+ * conservative and its output is a QUESTION ("possible conflict … confirm
18
+ * against the source"), not a verdict: a false mark costs the agent one check,
19
+ * a missed one costs a wrong answer stated with confidence.
20
+ *
21
+ * Two memories are flagged when
22
+ * 1. they are about the same thing — related by embedding cosine, or by word
23
+ * overlap when an embedding is missing — and
24
+ * 2. they carry a value of the same kind (a time, a date, a money amount, a
25
+ * bare number, a status word) and those values differ, and
26
+ * 3. neither supersedes the other and neither is already stale (superseded,
27
+ * expired, not yet valid) — those are marked elsewhere.
28
+ *
29
+ * Known limit, measured in tests/core/conflicts.test.ts: "template twins" —
30
+ * two genuinely distinct facts that share a template ("flight out 8:40",
31
+ * "flight back 10:15") — are flagged. That is the precision cost of (1)+(2)
32
+ * without a parser, and the reason the mark is phrased as a question.
33
+ */
34
+ import { cosineSimilarity } from './embeddings.js';
35
+ import { validityState } from './validity.js';
36
+ const MONTHS = 'jan|feb|mar|apr|may|jun|jul|aug|sep|sept|oct|nov|dec';
37
+ const STATUS_WORDS = new Set([
38
+ 'moved', 'cancelled', 'canceled', 'renewed', 'rejected', 'approved', 'closed', 'open', 'opened',
39
+ 'paused', 'resumed', 'delayed', 'postponed', 'confirmed', 'unpaid', 'paid', 'overdue', 'done',
40
+ 'blocked', 'shipped', 'deployed', 'reverted', 'merged', 'unmerged', 'live', 'offline', 'down', 'up',
41
+ ]);
42
+ const STOP = new Set([
43
+ 'the', 'and', 'for', 'with', 'that', 'this', 'from', 'per', 'now', 'was', 'were', 'are', 'has',
44
+ 'have', 'had', 'into', 'onto', 'than', 'then', 'not', 'but', 'its', 'his', 'her', 'their', 'our',
45
+ 'according', 'page', 'said', 'says', 'still', 'also', 'any', 'all', 'one', 'two', 'about',
46
+ ]);
47
+ /** Normalise a clock time to HH:MM (24h). Returns null when it is not one. */
48
+ function clock(h, m, ampm) {
49
+ let hh = Number(h);
50
+ const mm = m ? Number(m) : 0;
51
+ if (!Number.isFinite(hh) || hh > 23 || mm > 59)
52
+ return null;
53
+ if (ampm) {
54
+ const p = ampm.toLowerCase();
55
+ if (hh > 12)
56
+ return null;
57
+ if (p === 'pm' && hh < 12)
58
+ hh += 12;
59
+ if (p === 'am' && hh === 12)
60
+ hh = 0;
61
+ }
62
+ return `${String(hh).padStart(2, '0')}:${String(mm).padStart(2, '0')}`;
63
+ }
64
+ /**
65
+ * The comparable values in a text, by kind. Text matched by an earlier,
66
+ * more specific kind is blanked before a later kind runs, so "3:30pm" is one
67
+ * time rather than a time and the numbers 3 and 30, and "$49" is money, not
68
+ * a number.
69
+ */
70
+ export function valueSlots(text) {
71
+ const slots = new Map();
72
+ const add = (k, v) => {
73
+ if (!slots.has(k))
74
+ slots.set(k, new Set());
75
+ slots.get(k).add(v);
76
+ };
77
+ let t = ` ${text} `;
78
+ const take = (re, fn) => {
79
+ t = t.replace(re, (...args) => {
80
+ const m = args;
81
+ fn(m);
82
+ return ' ';
83
+ });
84
+ };
85
+ // Times: 8:40 AM, 3:30pm, 14:00, 7pm.
86
+ take(/\b(\d{1,2}):(\d{2})\s*(am|pm)\b/gi, m => { const c = clock(m[1], m[2], m[3]); if (c)
87
+ add('time', c); });
88
+ take(/\b(\d{1,2})\s*(am|pm)\b/gi, m => { const c = clock(m[1], undefined, m[2]); if (c)
89
+ add('time', c); });
90
+ take(/\b([01]?\d|2[0-3]):([0-5]\d)\b/g, m => { const c = clock(m[1], m[2], undefined); if (c)
91
+ add('time', c); });
92
+ // Dates: 2026-01-15, Nov 14, 14 Nov, November 14th.
93
+ take(/\b(\d{4})-(\d{2})-(\d{2})\b/g, m => add('date', `${m[1]}-${m[2]}-${m[3]}`));
94
+ take(new RegExp(`\\b(${MONTHS})[a-z]*\\.?\\s+(\\d{1,2})(?:st|nd|rd|th)?\\b`, 'gi'), m => add('date', `${m[1].slice(0, 3).toLowerCase()}-${Number(m[2])}`));
95
+ take(new RegExp(`\\b(\\d{1,2})(?:st|nd|rd|th)?\\s+(${MONTHS})[a-z]*\\b`, 'gi'), m => add('date', `${m[2].slice(0, 3).toLowerCase()}-${Number(m[1])}`));
96
+ // A bare month after a preposition ("in October", "by March", "since
97
+ // November") is a date too — coarser, but "in October the club chose
98
+ // fantasy" against "in March … a mystery" is exactly the two-versions case
99
+ // (MWA, 0.18.1). Full names only and a preposition required: an unanchored
100
+ // match took the modal verb "may" and every passing "March" as a date, and
101
+ // on the real store that flagged enough false conflicts to show in the
102
+ // benchmark. Runs after the month-day forms, which have already been
103
+ // blanked, so "Nov 14" is not also counted as "nov".
104
+ take(/\b(?:in|by|until|till|from|since|during|before|after|early|late|mid|next|last|this|of|through)\s+(january|february|march|april|may|june|july|august|september|october|november|december)\b/gi, m => add('date', m[1].slice(0, 3).toLowerCase()));
105
+ // Money: $1.45, $2,400, $49/month.
106
+ take(/\$\s?(\d[\d,]*(?:\.\d+)?)/g, m => add('money', m[1].replace(/,/g, '')));
107
+ // Bare numbers (ids, counts, flight numbers): shared ones do no harm, differing ones may.
108
+ take(/\b\d+(?:\.\d+)?\b/g, m => add('number', m[0]));
109
+ // Status vocabulary.
110
+ for (const w of t.toLowerCase().match(/[a-z]+/g) ?? []) {
111
+ if (STATUS_WORDS.has(w))
112
+ add('status', w);
113
+ }
114
+ return slots;
115
+ }
116
+ /** Content words for the overlap fallback: lower-case, 3+ letters, no stopwords, no digits. */
117
+ function words(c) {
118
+ const out = new Set();
119
+ for (const w of `${c.engram.concept} ${c.engram.content}`.toLowerCase().match(/[a-z][a-z']{2,}/g) ?? []) {
120
+ if (!STOP.has(w))
121
+ out.add(w);
122
+ }
123
+ return out;
124
+ }
125
+ function jaccard(a, b) {
126
+ if (a.size === 0 || b.size === 0)
127
+ return 0;
128
+ let inter = 0;
129
+ for (const w of a)
130
+ if (b.has(w))
131
+ inter++;
132
+ return inter / (a.size + b.size - inter);
133
+ }
134
+ const COSINE_RELATED = 0.80;
135
+ const OVERLAP_RELATED = 0.30;
136
+ /** True when the two memories look like they are about the same thing. */
137
+ export function related(a, b) {
138
+ const ea = a.engram.embedding, eb = b.engram.embedding;
139
+ if (ea && eb && ea.length === eb.length && ea.length > 0) {
140
+ if (cosineSimilarity(ea, eb) >= COSINE_RELATED)
141
+ return true;
142
+ }
143
+ return jaccard(words(a), words(b)) >= OVERLAP_RELATED;
144
+ }
145
+ /** The first kind of value present in BOTH texts on which they disagree, or null. */
146
+ export function disagreement(a, b) {
147
+ const sa = valueSlots(a.engram.content), sb = valueSlots(b.engram.content);
148
+ for (const kind of ['time', 'date', 'money', 'status', 'number']) {
149
+ let va = sa.get(kind), vb = sb.get(kind);
150
+ if (!va || !vb)
151
+ continue;
152
+ // Dates come at three grains (2026-01-15, nov-14, nov). When either side
153
+ // has a month-only value, compare both at month grain: "Nov 14" and
154
+ // "in November" agree; "in October" and "in March" do not.
155
+ if (kind === 'date' && ([...va, ...vb].some(v => /^[a-z]{3}$/.test(v)))) {
156
+ va = new Set([...va].map(monthOf));
157
+ vb = new Set([...vb].map(monthOf));
158
+ }
159
+ // Disagree when neither set contains the other: a memory that mentions
160
+ // two times, one of which the other repeats, is an elaboration, not a conflict.
161
+ const aInB = [...va].every(v => vb.has(v));
162
+ const bInA = [...vb].every(v => va.has(v));
163
+ if (!aInB && !bInA)
164
+ return kind;
165
+ }
166
+ return null;
167
+ }
168
+ const MONTH_NAMES = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];
169
+ /** The month token of any date value this file produces. */
170
+ function monthOf(v) {
171
+ const iso = /^(\d{4})-(\d{2})-\d{2}$/.exec(v);
172
+ if (iso)
173
+ return MONTH_NAMES[Number(iso[2]) - 1] ?? v;
174
+ return v.slice(0, 3);
175
+ }
176
+ function createdMs(c) {
177
+ const v = c.engram.createdAt;
178
+ if (!v)
179
+ return NaN;
180
+ return v instanceof Date ? v.getTime() : Date.parse(v);
181
+ }
182
+ function stale(c, now) {
183
+ return !!c.engram.supersededBy || validityState(c.engram, now) !== 'current';
184
+ }
185
+ /**
186
+ * Pairwise over the returned set. Returns one list of marks per input, in
187
+ * input order; an empty list means no conflict for that memory.
188
+ */
189
+ export function detectConflicts(results, now = Date.now()) {
190
+ const marks = results.map(() => []);
191
+ for (let i = 0; i < results.length; i++) {
192
+ const a = results[i];
193
+ if (stale(a, now))
194
+ continue;
195
+ for (let j = i + 1; j < results.length; j++) {
196
+ const b = results[j];
197
+ if (stale(b, now))
198
+ continue;
199
+ if (a.engram.supersedes === b.engram.id || b.engram.supersedes === a.engram.id)
200
+ continue;
201
+ if (!related(a, b))
202
+ continue;
203
+ const slot = disagreement(a, b);
204
+ if (!slot)
205
+ continue;
206
+ const ta = createdMs(a), tb = createdMs(b);
207
+ const aNewer = Number.isFinite(ta) && Number.isFinite(tb) ? ta > tb : false;
208
+ const bNewer = Number.isFinite(ta) && Number.isFinite(tb) ? tb > ta : false;
209
+ marks[i].push({ id: b.engram.id, newer: aNewer, slot });
210
+ marks[j].push({ id: a.engram.id, newer: bNewer, slot });
211
+ }
212
+ }
213
+ return marks;
214
+ }
215
+ /**
216
+ * Newest first among results that are two versions of one fact — FOR HARNESSES.
217
+ *
218
+ * MWA's case: "In October the club chose fantasy / December science fiction /
219
+ * March a mystery" for "what did the club most recently choose". The
220
+ * cross-encoder cannot tell them apart, and a sort by its score put the newest
221
+ * last. AWM itself does NOT apply this (0.18.1): a score-epsilon fallback to the
222
+ * blend measured -14pp identifier s@1 (rerank2.ts), and applying this helper
223
+ * inside the engine measured -18pp with s@5 unchanged, because on a real store
224
+ * the detector also marks related memories that differ in a ticket id or a
225
+ * date. The marks are a cue to check the source. A harness whose policy is
226
+ * "newer beats older" (MWA's is) can call this on the returned results with
227
+ * their `conflictsWith` marks. `marks` is parallel to `results`; the returned
228
+ * array is new and its marks still describe the same ids.
229
+ */
230
+ export function newestFirstAmongConflicts(results, marks) {
231
+ const out = results.slice();
232
+ const m = marks.slice();
233
+ for (let guard = 0; guard < out.length * out.length; guard++) {
234
+ let moved = false;
235
+ for (let j = 1; j < out.length && !moved; j++) {
236
+ for (let i = 0; i < j; i++) {
237
+ const mk = m[j].find(x => x.id === out[i].engram.id);
238
+ if (mk && mk.newer) {
239
+ const [r] = out.splice(j, 1);
240
+ const [mm] = m.splice(j, 1);
241
+ out.splice(i, 0, r);
242
+ m.splice(i, 0, mm);
243
+ moved = true;
244
+ break;
245
+ }
246
+ }
247
+ }
248
+ if (!moved)
249
+ break;
250
+ }
251
+ return out;
252
+ }
253
+ /** The id of the most recently written memory among those carrying a mark, or null. */
254
+ export function newestConflicting(results, marks) {
255
+ let best = null, bestMs = -Infinity;
256
+ results.forEach((r, i) => {
257
+ if (marks[i].length === 0)
258
+ return;
259
+ const ms = createdMs(r);
260
+ if (Number.isFinite(ms) && ms > bestMs) {
261
+ bestMs = ms;
262
+ best = r.engram.id;
263
+ }
264
+ });
265
+ return best;
266
+ }
267
+ //# sourceMappingURL=conflicts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conflicts.js","sourceRoot":"","sources":["../../src/core/conflicts.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,15 @@
15
15
  * Set AWM_EMBED_MODEL=Xenova/all-MiniLM-L6-v2 for backward compatibility.
16
16
  */
17
17
  import { type FeatureExtractionPipeline } from '@huggingface/transformers';
18
+ import type { Lane } from './model-daemon.js';
19
+ /** The texts-per-pass setting, for the model daemon's background chunking. */
20
+ export declare const EMBED_BATCH_SIZE: number;
21
+ /** Exported with its batch size as a parameter so tests can compare settings in one process. */
22
+ export declare function inProcessEmbed(args: {
23
+ texts: string[];
24
+ pooling: 'cls' | 'mean';
25
+ dimensions: number;
26
+ }, batch?: number): Promise<number[][]>;
18
27
  /**
19
28
  * Get or initialize the embedding pipeline (singleton).
20
29
  * Kept for backwards compat — returns the in-process pipeline only.
@@ -30,9 +39,12 @@ export declare function embed(text: string): Promise<number[]>;
30
39
  /**
31
40
  * Generate embeddings for multiple texts in a batch.
32
41
  * More efficient than calling embed() in a loop — the worker batches the
33
- * tokenization + forward pass.
42
+ * tokenization + forward pass. `lane: 'background'` (consolidation backfill) lets the
43
+ * shared model daemon run interactive requests ahead of it.
34
44
  */
35
- export declare function embedBatch(texts: string[]): Promise<number[][]>;
45
+ export declare function embedBatch(texts: string[], opts?: {
46
+ lane?: Lane;
47
+ }): Promise<number[][]>;
36
48
  /** Get the current embedding model ID (for version tracking in stored embeddings) */
37
49
  export declare function getModelId(): string;
38
50
  /** Observed embedding-dimension mismatches. Surfaced by /health and memory_whoami. */
@@ -1 +1 @@
1
- {"version":3,"file":"embeddings.d.ts","sourceRoot":"","sources":["../../src/core/embeddings.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAY,KAAK,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AA8CrF;;;;;GAKG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,yBAAyB,CAAC,CAEtE;AAED;;;GAGG;AACH,wBAAsB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAG3D;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAGrE;AAED,qFAAqF;AACrF,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAeD,sFAAsF;AACtF,wBAAgB,eAAe,IAAI;IACjC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAClD,CAMA;AAED,6CAA6C;AAC7C,wBAAgB,sBAAsB,IAAI,IAAI,CAI7C;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CA0BjE;AAED,uCAAuC;AACvC,eAAO,MAAM,oBAAoB,QAAa,CAAC"}
1
+ {"version":3,"file":"embeddings.d.ts","sourceRoot":"","sources":["../../src/core/embeddings.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAY,KAAK,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAErF,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAsB9C,8EAA8E;AAC9E,eAAO,MAAM,gBAAgB,QAAc,CAAC;AAqB5C,gGAAgG;AAChG,wBAAsB,cAAc,CAAC,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,KAAK,GAAG,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,SAAc,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAerJ;AAQD;;;;;GAKG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,yBAAyB,CAAC,CAEtE;AAED;;;GAGG;AACH,wBAAsB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAG3D;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,IAAI,CAAA;CAAO,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAGjG;AAED,qFAAqF;AACrF,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAeD,sFAAsF;AACtF,wBAAgB,eAAe,IAAI;IACjC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAClD,CAMA;AAED,6CAA6C;AAC7C,wBAAgB,sBAAsB,IAAI,IAAI,CAI7C;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CA0BjE;AAED,uCAAuC;AACvC,eAAO,MAAM,oBAAoB,QAAa,CAAC"}
@@ -17,12 +17,27 @@
17
17
  * Set AWM_EMBED_MODEL=Xenova/all-MiniLM-L6-v2 for backward compatibility.
18
18
  */
19
19
  import { pipeline } from '@huggingface/transformers';
20
- import { dispatchEmbed, registerInProcessHandlers } from './ml-worker.js';
20
+ import { dispatchEmbed, registerInferenceConfig, registerInProcessHandlers } from './ml-worker.js';
21
21
  import { noteModelLoad } from './write-telemetry.js';
22
22
  import { ensureModelCacheDir } from './model-cache.js';
23
23
  const MODEL_ID = process.env.AWM_EMBED_MODEL ?? 'Xenova/bge-small-en-v1.5';
24
24
  const DIMENSIONS = parseInt(process.env.AWM_EMBED_DIMS ?? '384', 10);
25
25
  const POOLING = (process.env.AWM_EMBED_POOLING ?? 'mean');
26
+ /**
27
+ * Texts per forward pass (default 8; 0 = all at once). onnxruntime-node's CPU arena grows
28
+ * to the largest tensor it has run and never shrinks, and attention is quadratic in the
29
+ * padded length: a 100-memory /memory/write-batch or a 32-engram consolidation backfill
30
+ * at 512 tokens set a high-water mark the process then kept for life. Chunking bounds it.
31
+ * A text's vector does not depend on the others in its batch (padding is masked).
32
+ * See reranker.ts for the measurement.
33
+ */
34
+ const EMBED_BATCH = (() => {
35
+ // An explicit 0 disables chunking; anything else invalid falls back to 8 (see reranker.ts).
36
+ const v = Number(process.env.AWM_EMBED_BATCH?.trim() || NaN);
37
+ return Number.isInteger(v) && v >= 0 ? v : 8;
38
+ })();
39
+ /** The texts-per-pass setting, for the model daemon's background chunking. */
40
+ export const EMBED_BATCH_SIZE = EMBED_BATCH;
26
41
  // --- In-process fallback (used by tests and crash recovery) ---
27
42
  let inProcessInstance = null;
28
43
  let inProcessInitPromise = null;
@@ -41,21 +56,27 @@ async function loadInProcess() {
41
56
  });
42
57
  return inProcessInitPromise;
43
58
  }
44
- async function inProcessEmbed(args) {
59
+ /** Exported with its batch size as a parameter so tests can compare settings in one process. */
60
+ export async function inProcessEmbed(args, batch = EMBED_BATCH) {
45
61
  const { texts, pooling, dimensions } = args;
46
62
  if (texts.length === 0)
47
63
  return [];
48
64
  const embedder = await loadInProcess();
49
- const result = await embedder(texts, { pooling, normalize: true });
50
- const data = result.data;
51
65
  const vectors = [];
52
- for (let i = 0; i < texts.length; i++) {
53
- vectors.push(Array.from(data.slice(i * dimensions, (i + 1) * dimensions)));
66
+ const size = batch > 0 ? batch : texts.length;
67
+ for (let b = 0; b < texts.length; b += size) {
68
+ const chunk = texts.slice(b, b + size);
69
+ const result = await embedder(chunk, { pooling, normalize: true });
70
+ const data = result.data;
71
+ for (let i = 0; i < chunk.length; i++) {
72
+ vectors.push(Array.from(data.slice(i * dimensions, (i + 1) * dimensions)));
73
+ }
54
74
  }
55
75
  return vectors;
56
76
  }
57
77
  // Register the in-process handler with the pool (used in test mode and as fallback)
58
- registerInProcessHandlers({ embed: inProcessEmbed });
78
+ registerInProcessHandlers({ embed: (args) => inProcessEmbed(args) });
79
+ registerInferenceConfig({ embedModel: MODEL_ID, embedDims: DIMENSIONS, embedPooling: POOLING, embedBatch: EMBED_BATCH });
59
80
  // --- Public API ---
60
81
  /**
61
82
  * Get or initialize the embedding pipeline (singleton).
@@ -77,12 +98,13 @@ export async function embed(text) {
77
98
  /**
78
99
  * Generate embeddings for multiple texts in a batch.
79
100
  * More efficient than calling embed() in a loop — the worker batches the
80
- * tokenization + forward pass.
101
+ * tokenization + forward pass. `lane: 'background'` (consolidation backfill) lets the
102
+ * shared model daemon run interactive requests ahead of it.
81
103
  */
82
- export async function embedBatch(texts) {
104
+ export async function embedBatch(texts, opts = {}) {
83
105
  if (texts.length === 0)
84
106
  return [];
85
- return dispatchEmbed({ texts, pooling: POOLING, dimensions: DIMENSIONS });
107
+ return dispatchEmbed({ texts, pooling: POOLING, dimensions: DIMENSIONS }, opts.lane);
86
108
  }
87
109
  /** Get the current embedding model ID (for version tracking in stored embeddings) */
88
110
  export function getModelId() {
@@ -1 +1 @@
1
- {"version":3,"file":"embeddings.js","sourceRoot":"","sources":["../../src/core/embeddings.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"embeddings.js","sourceRoot":"","sources":["../../src/core/embeddings.ts"],"names":[],"mappings":"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"}
@@ -12,5 +12,25 @@ import type { ActivationResult } from '../types/engram.js';
12
12
  * bodies can be long, and a consumer scanning for `[id: ...]` shouldn't have
13
13
  * to read past a paragraph of content to find it.
14
14
  */
15
- export declare function formatRecallResultLine(r: ActivationResult, index: number): string;
15
+ /**
16
+ * True when Phase 5.5 has already trimmed this engram's body.
17
+ *
18
+ * The fade marker is written INTO the content (`consolidation.ts` appends
19
+ * `'… [faded]'` after slicing to FADE_KEEP_CHARS), which means it is only
20
+ * visible to a reader who reaches the end of the body. Two things hide it
21
+ * there: `granularity: 'compact'`/`'auto'`, where the displayed body is a
22
+ * query-aware 200-char window that will rarely include the tail, and the
23
+ * plain length of a full body. So the marker is detected on the CONTENT and
24
+ * rendered in the header, next to `[valid until …]`, where a partial answer
25
+ * cannot be mistaken for a complete one.
26
+ *
27
+ * Matched anywhere rather than at the end because merge-on-reinforce
28
+ * (`AWM_REINFORCE_MERGE_CONTENT`) can append text after a fade, which would
29
+ * leave the marker mid-body. A false positive labels a full memory partial,
30
+ * which is the harmless direction.
31
+ */
32
+ export declare function isFaded(content: string): boolean;
33
+ export declare function formatRecallResultLine(r: ActivationResult, index: number, now?: number): string;
34
+ /** YYYY-MM-DD from a Date or ISO string; empty when absent or unparseable. */
35
+ export declare function writtenDate(createdAt: Date | string | null | undefined): string;
16
36
  //# sourceMappingURL=format-recall.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"format-recall.d.ts","sourceRoot":"","sources":["../../src/core/format-recall.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CASjF"}
1
+ {"version":3,"file":"format-recall.d.ts","sourceRoot":"","sources":["../../src/core/format-recall.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAG3D;;;;;;;;;;;;GAYG;AACH;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEhD;AAED,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,MAAM,CAsC3G;AAED,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAI/E"}
@@ -1,3 +1,4 @@
1
+ import { validityState } from './validity.js';
1
2
  /**
2
3
  * Formats one memory_recall result line for the MCP text response.
3
4
  *
@@ -11,14 +12,68 @@
11
12
  * bodies can be long, and a consumer scanning for `[id: ...]` shouldn't have
12
13
  * to read past a paragraph of content to find it.
13
14
  */
14
- export function formatRecallResultLine(r, index) {
15
+ /**
16
+ * True when Phase 5.5 has already trimmed this engram's body.
17
+ *
18
+ * The fade marker is written INTO the content (`consolidation.ts` appends
19
+ * `'… [faded]'` after slicing to FADE_KEEP_CHARS), which means it is only
20
+ * visible to a reader who reaches the end of the body. Two things hide it
21
+ * there: `granularity: 'compact'`/`'auto'`, where the displayed body is a
22
+ * query-aware 200-char window that will rarely include the tail, and the
23
+ * plain length of a full body. So the marker is detected on the CONTENT and
24
+ * rendered in the header, next to `[valid until …]`, where a partial answer
25
+ * cannot be mistaken for a complete one.
26
+ *
27
+ * Matched anywhere rather than at the end because merge-on-reinforce
28
+ * (`AWM_REINFORCE_MERGE_CONTENT`) can append text after a fade, which would
29
+ * leave the marker mid-body. A false positive labels a full memory partial,
30
+ * which is the harmless direction.
31
+ */
32
+ export function isFaded(content) {
33
+ return content.includes('[faded]');
34
+ }
35
+ export function formatRecallResultLine(r, index, now = Date.now()) {
15
36
  const body = r.summary ?? r.engram.content;
37
+ // Fade retains 7.1% of answer-bearing identifiers while leaving findability
38
+ // intact (docs/eval-gap-2026-10-11.md §8.1) — 37 of 38 faded engrams still
39
+ // surface by cue. That combination is the worst possible shape: the result
40
+ // ranks, reads as an answer, and no longer contains one. The label does not
41
+ // fix the retrieval, it stops the silent part.
42
+ const faded = isFaded(r.engram.content)
43
+ // Fade overwrites the content (consolidation.ts Phase 5.5); no tool can bring the
44
+ // rest back, so the label says what to do instead.
45
+ ? ' [faded — body trimmed, the rest is gone; confirm against the source or a newer memory]'
46
+ : '';
16
47
  const chain = r.engram.supersededBy
17
48
  ? ` ⚠ SUPERSEDED by ${r.engram.supersededBy} — treat as historical; recall/fetch the successor before relying on this.`
18
49
  : '';
19
- const validity = r.engram.validTo
20
- ? ` [valid until ${r.engram.validTo}]`
21
- : '';
22
- return `${index + 1}. **${r.engram.concept}** (${r.score.toFixed(3)}) [id: ${r.engram.id}]${validity}: ${body}${chain}`;
50
+ // Temporal validity (D8). Same decision as ranking (activation.ts Phase 8c,
51
+ // via core/validity.ts): a memory past its valid_to is down-ranked AND says
52
+ // so here — "[valid until 2026-01-15]" on a line read in March was the label
53
+ // that let 482 expired memories in the live store pass as current.
54
+ const vstate = validityState(r.engram, now);
55
+ const validity = vstate === 'expired'
56
+ ? ` ⚠ EXPIRED ${r.engram.validTo} — treat as historical; confirm the current state before relying on this.`
57
+ : vstate === 'not-yet-valid'
58
+ ? ` [not yet valid — from ${r.engram.validFrom}]`
59
+ : r.engram.validTo
60
+ ? ` [valid until ${r.engram.validTo}]`
61
+ : '';
62
+ // The written date on every line (2026-10-19). Two versions of a fact used
63
+ // to arrive with nothing to say which came first; the prime hook printed an
64
+ // age, this format printed nothing.
65
+ const written = writtenDate(r.engram.createdAt);
66
+ const dated = written ? ` [written ${written}]` : '';
67
+ // Conflict mark (src/core/conflicts.ts): phrased as a question, because the
68
+ // detector cannot tell a correction from two facts that share a template.
69
+ const conflicts = (r.conflictsWith ?? []).map(c => ` ⚠ POSSIBLE CONFLICT with ${c.id} on a ${c.slot} (this one is ${c.newer ? 'newer' : 'older'}; confirm against the source before stating a value)`).join('');
70
+ return `${index + 1}. **${r.engram.concept}** (${r.score.toFixed(3)}) [id: ${r.engram.id}]${dated}${validity}${faded}: ${body}${chain}${conflicts}`;
71
+ }
72
+ /** YYYY-MM-DD from a Date or ISO string; empty when absent or unparseable. */
73
+ export function writtenDate(createdAt) {
74
+ if (!createdAt)
75
+ return '';
76
+ const ms = createdAt instanceof Date ? createdAt.getTime() : Date.parse(createdAt);
77
+ return Number.isFinite(ms) ? new Date(ms).toISOString().slice(0, 10) : '';
23
78
  }
24
79
  //# sourceMappingURL=format-recall.js.map