agent-working-memory 0.13.1 → 0.14.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 (91) hide show
  1. package/README.md +200 -238
  2. package/dist/adapters/common.d.ts +6 -0
  3. package/dist/adapters/common.d.ts.map +1 -1
  4. package/dist/adapters/common.js +457 -362
  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 +24 -8
  8. package/dist/api/routes.js.map +1 -1
  9. package/dist/core/alias-map.d.ts +16 -0
  10. package/dist/core/alias-map.d.ts.map +1 -0
  11. package/dist/core/alias-map.js +102 -0
  12. package/dist/core/alias-map.js.map +1 -0
  13. package/dist/core/embeddings.d.ts +17 -0
  14. package/dist/core/embeddings.d.ts.map +1 -1
  15. package/dist/core/embeddings.js +50 -1
  16. package/dist/core/embeddings.js.map +1 -1
  17. package/dist/core/recall-config.d.ts +52 -0
  18. package/dist/core/recall-config.d.ts.map +1 -0
  19. package/dist/core/recall-config.js +110 -0
  20. package/dist/core/recall-config.js.map +1 -0
  21. package/dist/core/rerank-window.d.ts +61 -0
  22. package/dist/core/rerank-window.d.ts.map +1 -0
  23. package/dist/core/rerank-window.js +153 -0
  24. package/dist/core/rerank-window.js.map +1 -0
  25. package/dist/core/rerank2.d.ts +62 -0
  26. package/dist/core/rerank2.d.ts.map +1 -0
  27. package/dist/core/rerank2.js +75 -0
  28. package/dist/core/rerank2.js.map +1 -0
  29. package/dist/core/retrieval-text.d.ts +55 -0
  30. package/dist/core/retrieval-text.d.ts.map +1 -0
  31. package/dist/core/retrieval-text.js +87 -0
  32. package/dist/core/retrieval-text.js.map +1 -0
  33. package/dist/core/temporal-query.d.ts +61 -0
  34. package/dist/core/temporal-query.d.ts.map +1 -0
  35. package/dist/core/temporal-query.js +168 -0
  36. package/dist/core/temporal-query.js.map +1 -0
  37. package/dist/core/token-budget.d.ts +75 -0
  38. package/dist/core/token-budget.d.ts.map +1 -0
  39. package/dist/core/token-budget.js +136 -0
  40. package/dist/core/token-budget.js.map +1 -0
  41. package/dist/core/whoami.d.ts +11 -0
  42. package/dist/core/whoami.d.ts.map +1 -1
  43. package/dist/core/whoami.js +10 -0
  44. package/dist/core/whoami.js.map +1 -1
  45. package/dist/core/write-pipeline.d.ts.map +1 -1
  46. package/dist/core/write-pipeline.js +6 -3
  47. package/dist/core/write-pipeline.js.map +1 -1
  48. package/dist/engine/activation.d.ts.map +1 -1
  49. package/dist/engine/activation.js +135 -32
  50. package/dist/engine/activation.js.map +1 -1
  51. package/dist/hooks/prime.d.ts +77 -0
  52. package/dist/hooks/prime.d.ts.map +1 -0
  53. package/dist/hooks/prime.js +92 -0
  54. package/dist/hooks/prime.js.map +1 -0
  55. package/dist/hooks/sidecar.d.ts.map +1 -1
  56. package/dist/hooks/sidecar.js +39 -0
  57. package/dist/hooks/sidecar.js.map +1 -1
  58. package/dist/mcp.js +134 -102
  59. package/dist/mcp.js.map +1 -1
  60. package/dist/storage/pglite.d.ts.map +1 -1
  61. package/dist/storage/pglite.js +10 -2
  62. package/dist/storage/pglite.js.map +1 -1
  63. package/dist/storage/postgres.d.ts.map +1 -1
  64. package/dist/storage/postgres.js +10 -2
  65. package/dist/storage/postgres.js.map +1 -1
  66. package/dist/storage/sqlite.d.ts.map +1 -1
  67. package/dist/storage/sqlite.js +12 -2
  68. package/dist/storage/sqlite.js.map +1 -1
  69. package/dist/types/engram.d.ts +7 -0
  70. package/dist/types/engram.d.ts.map +1 -1
  71. package/package.json +3 -2
  72. package/src/adapters/common.ts +666 -567
  73. package/src/api/routes.ts +1015 -999
  74. package/src/core/alias-map.ts +97 -0
  75. package/src/core/embeddings.ts +172 -115
  76. package/src/core/recall-config.ts +115 -0
  77. package/src/core/rerank-window.ts +158 -0
  78. package/src/core/rerank2.ts +82 -0
  79. package/src/core/retrieval-text.ts +82 -0
  80. package/src/core/temporal-query.ts +193 -0
  81. package/src/core/token-budget.ts +160 -0
  82. package/src/core/whoami.ts +110 -92
  83. package/src/core/write-pipeline.ts +6 -3
  84. package/src/engine/activation.ts +1568 -1468
  85. package/src/hooks/prime.ts +136 -0
  86. package/src/hooks/sidecar.ts +43 -0
  87. package/src/mcp.ts +1422 -1387
  88. package/src/storage/pglite.ts +10 -2
  89. package/src/storage/postgres.ts +10 -2
  90. package/src/storage/sqlite.ts +12 -2
  91. package/src/types/engram.ts +7 -0
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Temporal expressions in a recall query.
3
+ *
4
+ * THE PROBLEM
5
+ * -----------
6
+ * Nothing in the pipeline parsed dates out of query text, and `ActivationQuery`
7
+ * had no date parameter. So "azure app plan from last Thursday or Friday" spent
8
+ * "last", "Thursday" and "Friday" as ordinary BM25 tokens — diluting the subject
9
+ * terms and matching `date=` tags right across the corpus.
10
+ *
11
+ * Measured on the real store (101 probes, tests/realstore-eval):
12
+ * subject only ............ 59.4% success@1 <- control
13
+ * + "from last Friday" .... 56.4% (-3.0pp)
14
+ * + "on 2026-05-01" ....... 51.5% (-7.9pp)
15
+ * ORACLE, week-filtered ... 96.0% (+36.6pp)
16
+ *
17
+ * So the most selective thing the user said was a PENALTY, and a working
18
+ * temporal filter is worth more than anything else measured on this store.
19
+ *
20
+ * DESIGN NOTES
21
+ * ------------
22
+ * - `asOf` is required for relative phrases. Using the wall clock would make a
23
+ * fixture non-reproducible: "last week" would silently mean something new on
24
+ * every run. Callers pass the query time explicitly.
25
+ * - Matching is deliberately CONSERVATIVE. A greedy matcher that fired on
26
+ * ordinary words would reshape every recall in the store, so each pattern is
27
+ * anchored on an unambiguous cue ("last", "yesterday", an ISO date, a month
28
+ * name). When nothing matches, this returns `null` and callers must treat
29
+ * that as a strict no-op.
30
+ * - The window is a PREFERENCE for callers, never a filter. People misremember
31
+ * dates — "last Thursday or Friday" is itself an admission of uncertainty —
32
+ * so a memory outside the window must stay reachable on subject strength.
33
+ */
34
+ export interface TemporalMatch {
35
+ /** Inclusive window start (UTC ms). */
36
+ from: number;
37
+ /** Exclusive window end (UTC ms). */
38
+ to: number;
39
+ /** The literal phrases matched, for logging and for stripping. */
40
+ matched: string[];
41
+ /** The query with temporal phrases removed, for the lexical channel. */
42
+ stripped: string;
43
+ /** How the window was derived — useful when explaining a recall. */
44
+ kind: 'relative-day' | 'relative-week' | 'relative-month' | 'recent' | 'absolute-date' | 'absolute-month';
45
+ }
46
+ /**
47
+ * Parse a temporal expression out of `query`, anchored at `asOf`.
48
+ * Returns `null` when nothing matches — callers MUST treat that as a no-op.
49
+ *
50
+ * Windows are padded generously rather than tightly: the goal is to prefer a
51
+ * neighbourhood, not to pin an exact day the user probably misremembers.
52
+ */
53
+ export declare function parseTemporal(query: string, asOf: Date | number): TemporalMatch | null;
54
+ /** Whether temporal handling is enabled. Default OFF. */
55
+ export declare function temporalEnabled(): boolean;
56
+ /**
57
+ * Boost applied to a candidate whose creation time falls inside the window.
58
+ * A PREFERENCE, not a filter — see the design note above.
59
+ */
60
+ export declare function temporalBoost(): number;
61
+ //# sourceMappingURL=temporal-query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"temporal-query.d.ts","sourceRoot":"","sources":["../../src/core/temporal-query.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,MAAM,WAAW,aAAa;IAC5B,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,kEAAkE;IAClE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,IAAI,EAAE,cAAc,GAAG,eAAe,GAAG,gBAAgB,GAAG,QAAQ,GAAG,eAAe,GAAG,gBAAgB,CAAC;CAC3G;AASD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,aAAa,GAAG,IAAI,CAmGtF;AAkBD,yDAAyD;AACzD,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAGtC"}
@@ -0,0 +1,168 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Temporal expressions in a recall query.
5
+ *
6
+ * THE PROBLEM
7
+ * -----------
8
+ * Nothing in the pipeline parsed dates out of query text, and `ActivationQuery`
9
+ * had no date parameter. So "azure app plan from last Thursday or Friday" spent
10
+ * "last", "Thursday" and "Friday" as ordinary BM25 tokens — diluting the subject
11
+ * terms and matching `date=` tags right across the corpus.
12
+ *
13
+ * Measured on the real store (101 probes, tests/realstore-eval):
14
+ * subject only ............ 59.4% success@1 <- control
15
+ * + "from last Friday" .... 56.4% (-3.0pp)
16
+ * + "on 2026-05-01" ....... 51.5% (-7.9pp)
17
+ * ORACLE, week-filtered ... 96.0% (+36.6pp)
18
+ *
19
+ * So the most selective thing the user said was a PENALTY, and a working
20
+ * temporal filter is worth more than anything else measured on this store.
21
+ *
22
+ * DESIGN NOTES
23
+ * ------------
24
+ * - `asOf` is required for relative phrases. Using the wall clock would make a
25
+ * fixture non-reproducible: "last week" would silently mean something new on
26
+ * every run. Callers pass the query time explicitly.
27
+ * - Matching is deliberately CONSERVATIVE. A greedy matcher that fired on
28
+ * ordinary words would reshape every recall in the store, so each pattern is
29
+ * anchored on an unambiguous cue ("last", "yesterday", an ISO date, a month
30
+ * name). When nothing matches, this returns `null` and callers must treat
31
+ * that as a strict no-op.
32
+ * - The window is a PREFERENCE for callers, never a filter. People misremember
33
+ * dates — "last Thursday or Friday" is itself an admission of uncertainty —
34
+ * so a memory outside the window must stay reachable on subject strength.
35
+ */
36
+ const DAY = 86400000;
37
+ const WEEKDAYS = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
38
+ const MONTHS = ['january', 'february', 'march', 'april', 'may', 'june',
39
+ 'july', 'august', 'september', 'october', 'november', 'december'];
40
+ const utcDay = (ms) => Math.floor(ms / DAY) * DAY;
41
+ /**
42
+ * Parse a temporal expression out of `query`, anchored at `asOf`.
43
+ * Returns `null` when nothing matches — callers MUST treat that as a no-op.
44
+ *
45
+ * Windows are padded generously rather than tightly: the goal is to prefer a
46
+ * neighbourhood, not to pin an exact day the user probably misremembers.
47
+ */
48
+ export function parseTemporal(query, asOf) {
49
+ const anchorMs = typeof asOf === 'number' ? asOf : asOf.getTime();
50
+ if (!Number.isFinite(anchorMs))
51
+ return null;
52
+ const q = query.toLowerCase();
53
+ const today = utcDay(anchorMs);
54
+ const matched = [];
55
+ const build = (from, to, kind) => ({
56
+ from, to, matched,
57
+ stripped: stripPhrases(query, matched),
58
+ kind,
59
+ });
60
+ // ── absolute ISO date — ONLY with an explicit temporal cue ──
61
+ // A BARE date must not trigger this. Memories routinely carry a date as part
62
+ // of their SUBJECT ("Hive close-out 2026-04-24 PASS", "FIXED 2026-08-21 event
63
+ // River Glen"), where the date IS the fact, not a "when" filter. Treating
64
+ // those as temporal strips a strongly discriminative term and makes recall
65
+ // worse: measured, a bare-date rule fired on 98 of 1,316 (7.4%) real
66
+ // identifier queries and cost success@1 75.8% -> 75.0%. Same reason the month
67
+ // rule below requires an "in"/"during" cue.
68
+ const iso = q.match(/\b(on|from|since|before|after|around|during|dated)\s+(\d{4})-(\d{2})-(\d{2})\b/);
69
+ if (iso) {
70
+ matched.push(iso[0]);
71
+ const d = Date.UTC(+iso[2], +iso[3] - 1, +iso[4]);
72
+ // +/- 3 days: a remembered date is often a day or two off.
73
+ return build(d - 3 * DAY, d + 4 * DAY, 'absolute-date');
74
+ }
75
+ // ── "yesterday" / "today" ──
76
+ if (/\byesterday\b/.test(q)) {
77
+ matched.push('yesterday');
78
+ return build(today - DAY, today, 'relative-day');
79
+ }
80
+ if (/\btoday\b/.test(q)) {
81
+ matched.push('today');
82
+ return build(today, today + DAY, 'relative-day');
83
+ }
84
+ // ── "last <weekday>" / "on <weekday>" ──
85
+ const wd = q.match(/\b(?:last|this|on)\s+(sunday|monday|tuesday|wednesday|thursday|friday|saturday)\b/);
86
+ if (wd) {
87
+ matched.push(wd[0]);
88
+ // A second weekday ("Thursday or Friday") widens rather than confuses.
89
+ const second = q.match(/\bor\s+(sunday|monday|tuesday|wednesday|thursday|friday|saturday)\b/);
90
+ if (second)
91
+ matched.push(second[0]);
92
+ const targets = [WEEKDAYS.indexOf(wd[1])];
93
+ if (second)
94
+ targets.push(WEEKDAYS.indexOf(second[1]));
95
+ const anchorDow = new Date(today).getUTCDay();
96
+ const days = targets.map(t => {
97
+ let back = (anchorDow - t + 7) % 7;
98
+ if (back === 0)
99
+ back = 7; // "last Friday" on a Friday means the previous one
100
+ return today - back * DAY;
101
+ });
102
+ // +/- 1 day of slack around the named day(s).
103
+ return build(Math.min(...days) - DAY, Math.max(...days) + 2 * DAY, 'relative-day');
104
+ }
105
+ // ── "last week" / "this week" / "past week" ──
106
+ if (/\b(?:last|this|past)\s+week\b/.test(q)) {
107
+ matched.push(q.match(/\b(?:last|this|past)\s+week\b/)[0]);
108
+ return build(today - 14 * DAY, today + DAY, 'relative-week');
109
+ }
110
+ // ── "last month" / "this month" / "past month" ──
111
+ if (/\b(?:last|this|past)\s+month\b/.test(q)) {
112
+ matched.push(q.match(/\b(?:last|this|past)\s+month\b/)[0]);
113
+ return build(today - 45 * DAY, today + DAY, 'relative-month');
114
+ }
115
+ // ── "last N days/weeks/months" ──
116
+ const lastN = q.match(/\b(?:last|past)\s+(\d{1,3})\s+(day|week|month)s?\b/);
117
+ if (lastN) {
118
+ matched.push(lastN[0]);
119
+ const n = +lastN[1];
120
+ const mult = lastN[2] === 'day' ? DAY : lastN[2] === 'week' ? 7 * DAY : 30 * DAY;
121
+ return build(today - n * mult, today + DAY, 'relative-week');
122
+ }
123
+ // ── "recently" / "recent" / "the other day" / "a few days ago" ──
124
+ if (/\b(?:recently|the other day|a few days ago|just now)\b/.test(q)) {
125
+ matched.push(q.match(/\b(?:recently|the other day|a few days ago|just now)\b/)[0]);
126
+ return build(today - 14 * DAY, today + DAY, 'recent');
127
+ }
128
+ // ── "in <Month> [Year]" — requires the "in"/"during" cue so a project named
129
+ // "May" or a sentence containing "march" cannot trigger it accidentally.
130
+ const mon = q.match(/\b(?:in|during|back in)\s+(january|february|march|april|may|june|july|august|september|october|november|december)\b(?:\s+(\d{4}))?/);
131
+ if (mon) {
132
+ matched.push(mon[0]);
133
+ const mIdx = MONTHS.indexOf(mon[1]);
134
+ const anchorYear = new Date(today).getUTCFullYear();
135
+ const year = mon[2] ? +mon[2] : anchorYear;
136
+ const from = Date.UTC(year, mIdx, 1);
137
+ const to = Date.UTC(mIdx === 11 ? year + 1 : year, (mIdx + 1) % 12, 1);
138
+ return build(from, to, 'absolute-month');
139
+ }
140
+ return null;
141
+ }
142
+ /** Remove matched temporal phrases (and now-dangling connectives) from a query. */
143
+ function stripPhrases(query, phrases) {
144
+ let out = query;
145
+ // Longest first, so "on 2026-05-01" is removed before the bare date.
146
+ for (const p of [...phrases].sort((a, b) => b.length - a.length)) {
147
+ out = out.replace(new RegExp(escapeRe(p), 'gi'), ' ');
148
+ }
149
+ return out
150
+ .replace(/\b(?:from|on|in|during|back in|since)\s*$/i, ' ')
151
+ .replace(/\s+(?:from|on|in|during)\s+(?=\s|$)/gi, ' ')
152
+ .replace(/\s{2,}/g, ' ')
153
+ .trim();
154
+ }
155
+ const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
156
+ /** Whether temporal handling is enabled. Default OFF. */
157
+ export function temporalEnabled() {
158
+ return process.env.AWM_TEMPORAL === '1';
159
+ }
160
+ /**
161
+ * Boost applied to a candidate whose creation time falls inside the window.
162
+ * A PREFERENCE, not a filter — see the design note above.
163
+ */
164
+ export function temporalBoost() {
165
+ const v = Number(process.env.AWM_TEMPORAL_BOOST ?? 0.35);
166
+ return Number.isFinite(v) && v >= 0 ? v : 0.35;
167
+ }
168
+ //# sourceMappingURL=temporal-query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"temporal-query.js","sourceRoot":"","sources":["../../src/core/temporal-query.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Token budgeting for memory_recall (0.13.3).
3
+ *
4
+ * WHY THIS EXISTS
5
+ * ---------------
6
+ * `limit: N` is a COUNT, which is token-blind: a 5-result recall can cost 400
7
+ * tokens or 4,000 depending on how long the memories happen to be. AWM's own
8
+ * benchmark shows the problem — in real sessions AWM retrieval averaged 4,514
9
+ * tokens per call against 2,106 for plain file retrieval. AWM wins 9.8:1 in
10
+ * AGGREGATE (it needs far fewer calls) but loses 2.1:1 PER CALL. Per-call cost
11
+ * is the exposed flank, and a caller had no way to bound it.
12
+ *
13
+ * `max_tokens` lets the caller say "I have 800 tokens of context to spare"
14
+ * instead of guessing at a result count.
15
+ *
16
+ * PACKING STRATEGY
17
+ * ----------------
18
+ * This is a 0/1 knapsack (maximise score within a token budget), so we use the
19
+ * standard greedy density heuristic — order by score-per-token — with one
20
+ * deliberate exception: the top-scored result is always admitted first if it
21
+ * fits at all. Density packing alone can drop the single most relevant memory
22
+ * purely for being long, which is exactly the answer the caller wanted.
23
+ *
24
+ * Selection is by density; OUTPUT is restored to score order, because a reader
25
+ * scanning results expects the best one first.
26
+ *
27
+ * ESTIMATOR
28
+ * ---------
29
+ * Deliberately dependency-free: no tokenizer, no model load, no async. It
30
+ * takes max(words x 1.3, chars / 4) — the two standard English/code
31
+ * approximations — and takes the larger so the estimate errs toward
32
+ * OVER-counting. Over-counting keeps us inside the budget; under-counting
33
+ * would silently blow it, which is the failure that matters.
34
+ */
35
+ import type { ActivationResult } from '../types/engram.js';
36
+ /** Rough token count. Over-estimates by design — see the note above. */
37
+ export declare function estimateTokens(text: string): number;
38
+ export interface PackedRecall {
39
+ /** Formatted lines that fit the budget, in score order. */
40
+ lines: string[];
41
+ /** How many results were kept. */
42
+ kept: number;
43
+ /** How many results were available before budgeting. */
44
+ total: number;
45
+ /** Estimated tokens of the kept lines. */
46
+ tokens: number;
47
+ /** Estimated tokens of everything dropped (0 when nothing was dropped). */
48
+ withheldTokens: number;
49
+ }
50
+ /**
51
+ * Select and format results to fit `maxTokens`.
52
+ *
53
+ * When `maxTokens` is undefined the behaviour is unchanged from pre-0.13.3 —
54
+ * everything is returned — but the accounting fields are still populated, so
55
+ * callers get per-call token visibility without opting into budgeting.
56
+ */
57
+ export declare function packRecallByBudget(results: ActivationResult[], format: (r: ActivationResult, index: number) => string, maxTokens?: number,
58
+ /**
59
+ * Tokens the caller will spend on the SAME reply outside the result lines —
60
+ * the accounting footer, and any peer-decisions suffix. Reserved up front so
61
+ * the budget bounds the whole reply rather than just the part this function
62
+ * happens to build.
63
+ *
64
+ * Found by the end-to-end eval, not by the unit tests: budgets of 600/250/80
65
+ * came back as 601/256/95 because the footer spent tokens it never counted.
66
+ */
67
+ reservedTokens?: number): PackedRecall;
68
+ /**
69
+ * One-line accounting footer. Costs ~15-25 tokens to report, which is a good
70
+ * trade against a recall that can run to thousands — and it is what makes the
71
+ * per-call cost visible to both the human and the harness. Without this,
72
+ * AWM's token behaviour is only observable in an offline benchmark.
73
+ */
74
+ export declare function formatTokenFooter(p: PackedRecall, maxTokens?: number): string;
75
+ //# sourceMappingURL=token-budget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-budget.d.ts","sourceRoot":"","sources":["../../src/core/token-budget.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,wEAAwE;AACxE,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKnD;AAED,MAAM,WAAW,YAAY;IAC3B,2DAA2D;IAC3D,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,gBAAgB,EAAE,EAC3B,MAAM,EAAE,CAAC,CAAC,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,EACtD,SAAS,CAAC,EAAE,MAAM;AAClB;;;;;;;;GAQG;AACH,cAAc,SAAI,GACjB,YAAY,CA+Dd;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,YAAY,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAO7E"}
@@ -0,0 +1,136 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Token budgeting for memory_recall (0.13.3).
5
+ *
6
+ * WHY THIS EXISTS
7
+ * ---------------
8
+ * `limit: N` is a COUNT, which is token-blind: a 5-result recall can cost 400
9
+ * tokens or 4,000 depending on how long the memories happen to be. AWM's own
10
+ * benchmark shows the problem — in real sessions AWM retrieval averaged 4,514
11
+ * tokens per call against 2,106 for plain file retrieval. AWM wins 9.8:1 in
12
+ * AGGREGATE (it needs far fewer calls) but loses 2.1:1 PER CALL. Per-call cost
13
+ * is the exposed flank, and a caller had no way to bound it.
14
+ *
15
+ * `max_tokens` lets the caller say "I have 800 tokens of context to spare"
16
+ * instead of guessing at a result count.
17
+ *
18
+ * PACKING STRATEGY
19
+ * ----------------
20
+ * This is a 0/1 knapsack (maximise score within a token budget), so we use the
21
+ * standard greedy density heuristic — order by score-per-token — with one
22
+ * deliberate exception: the top-scored result is always admitted first if it
23
+ * fits at all. Density packing alone can drop the single most relevant memory
24
+ * purely for being long, which is exactly the answer the caller wanted.
25
+ *
26
+ * Selection is by density; OUTPUT is restored to score order, because a reader
27
+ * scanning results expects the best one first.
28
+ *
29
+ * ESTIMATOR
30
+ * ---------
31
+ * Deliberately dependency-free: no tokenizer, no model load, no async. It
32
+ * takes max(words x 1.3, chars / 4) — the two standard English/code
33
+ * approximations — and takes the larger so the estimate errs toward
34
+ * OVER-counting. Over-counting keeps us inside the budget; under-counting
35
+ * would silently blow it, which is the failure that matters.
36
+ */
37
+ /** Rough token count. Over-estimates by design — see the note above. */
38
+ export function estimateTokens(text) {
39
+ if (!text)
40
+ return 0;
41
+ const chars = text.length;
42
+ const words = text.split(/\s+/).filter(Boolean).length;
43
+ return Math.max(Math.ceil(words * 1.3), Math.ceil(chars / 4));
44
+ }
45
+ /**
46
+ * Select and format results to fit `maxTokens`.
47
+ *
48
+ * When `maxTokens` is undefined the behaviour is unchanged from pre-0.13.3 —
49
+ * everything is returned — but the accounting fields are still populated, so
50
+ * callers get per-call token visibility without opting into budgeting.
51
+ */
52
+ export function packRecallByBudget(results, format, maxTokens,
53
+ /**
54
+ * Tokens the caller will spend on the SAME reply outside the result lines —
55
+ * the accounting footer, and any peer-decisions suffix. Reserved up front so
56
+ * the budget bounds the whole reply rather than just the part this function
57
+ * happens to build.
58
+ *
59
+ * Found by the end-to-end eval, not by the unit tests: budgets of 600/250/80
60
+ * came back as 601/256/95 because the footer spent tokens it never counted.
61
+ */
62
+ reservedTokens = 0) {
63
+ const total = results.length;
64
+ if (total === 0)
65
+ return { lines: [], kept: 0, total: 0, tokens: 0, withheldTokens: 0 };
66
+ const effectiveBudget = maxTokens === undefined ? undefined
67
+ : Math.max(0, maxTokens - reservedTokens);
68
+ // Format once against the ORIGINAL index so displayed numbering matches the
69
+ // caller's mental model of "result 1 is the best match".
70
+ const measured = results.map((r, i) => {
71
+ const line = format(r, i);
72
+ return { line, tokens: estimateTokens(line), score: r.score, order: i };
73
+ });
74
+ const allTokens = measured.reduce((n, m) => n + m.tokens, 0);
75
+ // NOTE the asymmetry, which a unit test caught: `maxTokens` being absent or
76
+ // nonsense (<= 0) means "no budget — return everything", but an
77
+ // *effectiveBudget* of 0 means the reserve ate the entire budget, which must
78
+ // admit NOTHING. Collapsing those two cases returned the full result set on
79
+ // the tightest budgets — the exact opposite of what was asked for.
80
+ const unbudgeted = maxTokens === undefined || maxTokens <= 0;
81
+ if (unbudgeted || allTokens <= effectiveBudget) {
82
+ return {
83
+ lines: measured.map(m => m.line),
84
+ kept: total,
85
+ total,
86
+ tokens: allTokens,
87
+ withheldTokens: 0,
88
+ };
89
+ }
90
+ const chosen = [];
91
+ let spent = 0;
92
+ // 1. Top-scored result gets first refusal, so a long best-match is never
93
+ // dropped in favour of several short weak ones.
94
+ const top = measured[0];
95
+ if (top.tokens <= effectiveBudget) {
96
+ chosen.push(top);
97
+ spent += top.tokens;
98
+ }
99
+ // 2. Everything else by value density (score per token), greedily.
100
+ const rest = measured
101
+ .filter(m => m !== top)
102
+ .sort((a, b) => (b.score / Math.max(b.tokens, 1)) - (a.score / Math.max(a.tokens, 1)));
103
+ for (const m of rest) {
104
+ if (spent + m.tokens > effectiveBudget)
105
+ continue; // skip, don't stop: a later
106
+ chosen.push(m); // shorter result may still fit
107
+ spent += m.tokens;
108
+ }
109
+ // 3. Restore score order for display.
110
+ chosen.sort((a, b) => a.order - b.order);
111
+ return {
112
+ lines: chosen.map(m => m.line),
113
+ kept: chosen.length,
114
+ total,
115
+ tokens: spent,
116
+ withheldTokens: allTokens - spent,
117
+ };
118
+ }
119
+ /**
120
+ * One-line accounting footer. Costs ~15-25 tokens to report, which is a good
121
+ * trade against a recall that can run to thousands — and it is what makes the
122
+ * per-call cost visible to both the human and the harness. Without this,
123
+ * AWM's token behaviour is only observable in an offline benchmark.
124
+ */
125
+ export function formatTokenFooter(p, maxTokens) {
126
+ if (p.total === 0)
127
+ return '';
128
+ const parts = [`~${p.tokens} tok`];
129
+ parts.push(p.kept === p.total ? `${p.total} results` : `${p.kept}/${p.total} results`);
130
+ if (maxTokens !== undefined && maxTokens > 0)
131
+ parts.push(`budget ${maxTokens}`);
132
+ if (p.withheldTokens > 0)
133
+ parts.push(`~${p.withheldTokens} tok withheld`);
134
+ return `\n\n[awm: ${parts.join(' · ')}]`;
135
+ }
136
+ //# sourceMappingURL=token-budget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-budget.js","sourceRoot":"","sources":["../../src/core/token-budget.ts"],"names":[],"mappings":"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"}
@@ -12,6 +12,17 @@ export interface WhoamiInfo {
12
12
  http: number | null;
13
13
  hookSidecar: number | null;
14
14
  };
15
+ /**
16
+ * Effective recall configuration. `whoami` is the "what am I actually
17
+ * running" tool, and the version alone does not answer that: a build can be
18
+ * current while the recall flags that change its behaviour are unset, or set
19
+ * to something unintended. GET /health already reports this, but HTTP is off
20
+ * by default, so an MCP-only session had no way to see it.
21
+ */
22
+ recall: {
23
+ fingerprint: string;
24
+ flags: Record<string, string>;
25
+ };
15
26
  siblingAgents: string[];
16
27
  }
17
28
  /** Store subset whoami needs — every backend implements listAgentIds. */
@@ -1 +1 @@
1
- {"version":3,"file":"whoami.d.ts","sourceRoot":"","sources":["../../src/core/whoami.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,IAAI,EAAE,YAAY,GAAG,MAAM,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAC3D,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,yEAAyE;AACzE,MAAM,WAAW,WAAW;IAC1B,YAAY,IAAI,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAC9C;AASD,wBAAsB,WAAW,CAC/B,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,KAAK,GAAG,MAAM,GACtB,OAAO,CAAC,UAAU,CAAC,CAyBrB;AAED,0DAA0D;AAC1D,wBAAgB,YAAY,CAAC,CAAC,EAAE,UAAU,GAAG,MAAM,CAYlD"}
1
+ {"version":3,"file":"whoami.d.ts","sourceRoot":"","sources":["../../src/core/whoami.ts"],"names":[],"mappings":"AAuBA,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,IAAI,EAAE,YAAY,GAAG,MAAM,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAC3D;;;;;;OAMG;IACH,MAAM,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;IAC/D,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,yEAAyE;AACzE,MAAM,WAAW,WAAW;IAC1B,YAAY,IAAI,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAC9C;AASD,wBAAsB,WAAW,CAC/B,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,KAAK,GAAG,MAAM,GACtB,OAAO,CAAC,UAAU,CAAC,CA0BrB;AAED,0DAA0D;AAC1D,wBAAgB,YAAY,CAAC,CAAC,EAAE,UAAU,GAAG,MAAM,CAmBlD"}
@@ -16,6 +16,8 @@ import { dirname, join, resolve } from 'node:path';
16
16
  import { fileURLToPath } from 'node:url';
17
17
  import { VERSION } from '../version.js';
18
18
  import { getConfiguredBackend, getConfiguredPath } from '../storage/factory.js';
19
+ import { activeRecallConfig, recallConfigFingerprint } from './recall-config.js';
20
+ import { embeddingHealth } from './embeddings.js';
19
21
  function packageRoot() {
20
22
  // src/core/whoami.ts (dev) and dist/core/whoami.js (build) both sit two
21
23
  // levels below the package root.
@@ -46,6 +48,7 @@ export async function buildWhoami(store, agentId, surface) {
46
48
  http: Number.isFinite(httpPort) && httpPort > 0 ? httpPort : null,
47
49
  hookSidecar: Number.isFinite(hookPort) && hookPort > 0 ? hookPort : null,
48
50
  },
51
+ recall: { fingerprint: recallConfigFingerprint(), flags: activeRecallConfig() },
49
52
  siblingAgents: siblings,
50
53
  };
51
54
  }
@@ -58,6 +61,13 @@ export function formatWhoami(w) {
58
61
  `Store: ${w.storePath}`,
59
62
  `Code: ${w.codePath} (pid ${w.pid})`,
60
63
  `Ports: http=${w.ports.http ?? 'off'} hookSidecar=${w.ports.hookSidecar ?? 'off'}`,
64
+ `Recall config: ${w.recall.fingerprint}`,
65
+ ...(embeddingHealth().dimensionMismatches > 0
66
+ ? [`\u26a0 EMBEDDING INTEGRITY: ${embeddingHealth().dimensionMismatches} dimension mismatches ` +
67
+ `(expected ${embeddingHealth().expectedDimensions}d). Affected memories score 0 on the ` +
68
+ `vector channel — the corpus is probably half-migrated. Re-embed the whole store or ` +
69
+ `revert AWM_EMBED_MODEL/AWM_EMBED_DIMS.`]
70
+ : []),
61
71
  w.siblingAgents.length
62
72
  ? `Sibling agent spaces in this store: ${w.siblingAgents.join(', ')} — recall is scoped to '${w.agentId}'; other spaces need workspace recall, \`awm export --agent <id>\`, or a session configured for that agent.`
63
73
  : 'Sibling agent spaces in this store: none',
@@ -1 +1 @@
1
- {"version":3,"file":"whoami.js","sourceRoot":"","sources":["../../src/core/whoami.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"whoami.js","sourceRoot":"","sources":["../../src/core/whoami.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"write-pipeline.d.ts","sourceRoot":"","sources":["../../src/core/write-pipeline.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAE,YAAY,IAAI,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAIL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,eAAe,CAAC;AAOvB,kFAAkF;AAClF,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,uFAAuF;AACvF,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAC/C,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAY9C,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,CAAC;AAE/D,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,gFAAgF;IAChF,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,OAAO,oBAAoB,EAAE,eAAe,EAAE,CAAC;IAC5D;;qEAEiE;IACjE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,yFAAyF;IACzF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,WAAW,CAAC;IACpB;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,uGAAuG;IACvG,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;IAChC,uDAAuD;IACvD,aAAa,EAAE,aAAa,CAAC;IAC7B,kEAAkE;IAClE,SAAS,CAAC,EAAE;QACV,kBAAkB,EAAE,MAAM,CAAC;QAC3B,aAAa,EAAE,MAAM,CAAC;QACtB,mBAAmB,EAAE,MAAM,CAAC;KAC7B,CAAC;IACF,iEAAiE;IACjE,WAAW,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9B;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,WAAW,CAAC;IACnB,gBAAgB,EAAE,gBAAgB,CAAC;CACpC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,oBAAoB,EAC7B,KAAK,EAAE,UAAU,GAChB,OAAO,CAAC,WAAW,CAAC,CA4KtB"}
1
+ {"version":3,"file":"write-pipeline.d.ts","sourceRoot":"","sources":["../../src/core/write-pipeline.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAE,YAAY,IAAI,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAIL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,eAAe,CAAC;AAQvB,kFAAkF;AAClF,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,uFAAuF;AACvF,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAC/C,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAY9C,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,CAAC;AAE/D,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,gFAAgF;IAChF,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,OAAO,oBAAoB,EAAE,eAAe,EAAE,CAAC;IAC5D;;qEAEiE;IACjE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,yFAAyF;IACzF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,WAAW,CAAC;IACpB;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,uGAAuG;IACvG,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;IAChC,uDAAuD;IACvD,aAAa,EAAE,aAAa,CAAC;IAC7B,kEAAkE;IAClE,SAAS,CAAC,EAAE;QACV,kBAAkB,EAAE,MAAM,CAAC;QAC3B,aAAa,EAAE,MAAM,CAAC;QACtB,mBAAmB,EAAE,MAAM,CAAC;KAC7B,CAAC;IACF,iEAAiE;IACjE,WAAW,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9B;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,WAAW,CAAC;IACnB,gBAAgB,EAAE,gBAAgB,CAAC;CACpC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,oBAAoB,EAC7B,KAAK,EAAE,UAAU,GAChB,OAAO,CAAC,WAAW,CAAC,CA4KtB"}
@@ -40,6 +40,7 @@ import { extractMetaTags } from './auto-tagger.js';
40
40
  import { extractEntitiesFromTags } from './entity-extract.js';
41
41
  import { reportWrite } from './write-telemetry.js';
42
42
  import { DEFAULT_AGENT_CONFIG } from '../types/agent.js';
43
+ import { buildRetrievalText } from './retrieval-text.js';
43
44
  /** Confidence floor below which a matched engram is treated as "decaying out". */
44
45
  export const HEALTHY_CONFIDENCE_FLOOR = 0.3;
45
46
  /** Confidence delta applied on reinforcement. Bounded by REINFORCE_CONFIDENCE_CEIL. */
@@ -106,7 +107,7 @@ export async function performWrite(engines, input) {
106
107
  let prewriteEmbedding = null;
107
108
  if (process.env.AWM_NOVELTY_EMBED !== '0') {
108
109
  try {
109
- prewriteEmbedding = await embed(`${input.concept} ${input.content}`);
110
+ prewriteEmbedding = await embed(buildRetrievalText(input.concept, input.content, input.tags));
110
111
  }
111
112
  catch { /* fall through — async embed will retry later */ }
112
113
  }
@@ -318,7 +319,9 @@ newConceptHint = '') {
318
319
  // beyond 512 tokens but the topic anchor (early content) drives
319
320
  // the vector for retrieval purposes.
320
321
  const conceptForEmbed = newConceptHint || matched.concept;
321
- const newVec = await embed(`${conceptForEmbed} ${mergedContent}`);
322
+ // The reinforced engram's OWN tags — `input` is not in scope here, and
323
+ // the stored tags are the right vocabulary for the stored memory anyway.
324
+ const newVec = await embed(buildRetrievalText(conceptForEmbed, mergedContent, matched.tags));
322
325
  await store.updateEmbedding(matched.id, newVec);
323
326
  }
324
327
  catch { /* merge is best-effort — confidence bump already landed */ }
@@ -446,7 +449,7 @@ prewriteEmbedding = null) {
446
449
  const alreadyEmbedded = prewriteEmbedding != null && prewriteEmbedding.length > 0;
447
450
  const shouldEmbed = (!isStructural || input.embed === true) && !alreadyEmbedded;
448
451
  if (shouldEmbed) {
449
- embed(`${input.concept} ${input.content}`)
452
+ embed(buildRetrievalText(input.concept, input.content, input.tags))
450
453
  .then(async (vec) => {
451
454
  try {
452
455
  await store.updateEmbedding(engram.id, vec);