@remnic/core 9.3.659 → 9.3.661

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 (234) hide show
  1. package/dist/access-cli.js +22 -22
  2. package/dist/access-http.d.ts +5 -5
  3. package/dist/access-http.js +15 -14
  4. package/dist/access-mcp.d.ts +5 -5
  5. package/dist/access-mcp.js +14 -13
  6. package/dist/{access-service-D_nbpexW.d.ts → access-service-D0SLB4MH.d.ts} +2 -2
  7. package/dist/access-service.d.ts +5 -5
  8. package/dist/access-service.js +13 -12
  9. package/dist/action-confidence.d.ts +1 -1
  10. package/dist/active-memory-bridge.d.ts +1 -1
  11. package/dist/active-recall.d.ts +1 -1
  12. package/dist/behavior-learner.d.ts +1 -1
  13. package/dist/behavior-signals.d.ts +1 -1
  14. package/dist/bootstrap.d.ts +3 -3
  15. package/dist/briefing.d.ts +1 -1
  16. package/dist/briefing.js +4 -3
  17. package/dist/buffer-surprise-report.d.ts +1 -1
  18. package/dist/buffer.d.ts +1 -1
  19. package/dist/calibration.d.ts +1 -1
  20. package/dist/causal-behavior.d.ts +1 -1
  21. package/dist/causal-consolidation.d.ts +1 -1
  22. package/dist/causal-consolidation.js +5 -4
  23. package/dist/causal-consolidation.js.map +1 -1
  24. package/dist/{chunk-QRSKPI62.js → chunk-34NSUPWS.js} +110 -25
  25. package/dist/chunk-34NSUPWS.js.map +1 -0
  26. package/dist/{chunk-SCPFRKIT.js → chunk-42JKGUFJ.js} +6 -2
  27. package/dist/{chunk-SCPFRKIT.js.map → chunk-42JKGUFJ.js.map} +1 -1
  28. package/dist/{chunk-FWIROLS6.js → chunk-44VFF3BB.js} +18 -16
  29. package/dist/chunk-44VFF3BB.js.map +1 -0
  30. package/dist/{chunk-5PFIMBJJ.js → chunk-4SYURHI6.js} +12 -12
  31. package/dist/{chunk-2VCTTEJM.js → chunk-5G2DNO54.js} +3 -3
  32. package/dist/{chunk-3R6OP33G.js → chunk-7F7LC6HW.js} +3 -3
  33. package/dist/{chunk-ZCMO46YY.js → chunk-A7EF2XRO.js} +2 -2
  34. package/dist/{chunk-BNUAOLDK.js → chunk-ANJOULTP.js} +2 -2
  35. package/dist/{chunk-CPPS65WS.js → chunk-AWJ2FHCF.js} +84 -17
  36. package/dist/chunk-AWJ2FHCF.js.map +1 -0
  37. package/dist/{chunk-4PLOQDBB.js → chunk-AX5O25EF.js} +7 -5
  38. package/dist/chunk-AX5O25EF.js.map +1 -0
  39. package/dist/{chunk-QFKRE7AU.js → chunk-BP5O3GYD.js} +4 -4
  40. package/dist/{chunk-BKRIAXTU.js → chunk-D2EFNQMY.js} +2 -2
  41. package/dist/{chunk-6M4LYWA2.js → chunk-D44FQVCU.js} +5 -5
  42. package/dist/{chunk-SSSXWIBP.js → chunk-EMSC4P66.js} +5 -5
  43. package/dist/{chunk-MBZAESQ3.js → chunk-F6O7IOS3.js} +2 -2
  44. package/dist/{chunk-6G5JEN55.js → chunk-FZC2WSDB.js} +2 -2
  45. package/dist/{chunk-KI6QM5AV.js → chunk-GY3V3SUI.js} +2 -2
  46. package/dist/{chunk-EKQMQQ3U.js → chunk-LFZUFZQR.js} +10 -2
  47. package/dist/chunk-LFZUFZQR.js.map +1 -0
  48. package/dist/{chunk-7VWDC7AD.js → chunk-MHYRRV43.js} +122 -29
  49. package/dist/chunk-MHYRRV43.js.map +1 -0
  50. package/dist/{chunk-VJYFXDCZ.js → chunk-NMPEJV5M.js} +3 -3
  51. package/dist/{chunk-7KSPKZIQ.js → chunk-PXVFMQLD.js} +3 -3
  52. package/dist/{chunk-FIS5RT6K.js → chunk-QXHBWFR3.js} +2 -2
  53. package/dist/{chunk-G2VVBWFU.js → chunk-RQGR3ETH.js} +2 -2
  54. package/dist/{chunk-GGL7R2L2.js → chunk-RQRKQJYM.js} +4 -4
  55. package/dist/{chunk-JI3LQFJH.js → chunk-TBLGI2LT.js} +2 -2
  56. package/dist/{chunk-RVT6U6PV.js → chunk-TWAJICBN.js} +2 -2
  57. package/dist/{chunk-46RXRASB.js → chunk-TYIXG4VR.js} +3 -3
  58. package/dist/{chunk-5PLUC5OB.js → chunk-WSQG37DV.js} +2 -2
  59. package/dist/{chunk-M3VYPE2H.js → chunk-YNQ6DFSV.js} +1 -1
  60. package/dist/chunk-YNQ6DFSV.js.map +1 -0
  61. package/dist/{chunk-GRYAECRV.js → chunk-ZJH723NM.js} +2 -2
  62. package/dist/{cli-aYxSuPvP.d.ts → cli-C6twwe84.d.ts} +3 -3
  63. package/dist/cli.d.ts +5 -5
  64. package/dist/cli.js +24 -23
  65. package/dist/compounding/engine.d.ts +1 -1
  66. package/dist/compounding/engine.js +4 -3
  67. package/dist/compounding/preference-consolidator.d.ts +1 -1
  68. package/dist/compression-optimizer.d.ts +1 -1
  69. package/dist/config.d.ts +1 -1
  70. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  71. package/dist/connectors/codex-materialize-runner.js +4 -3
  72. package/dist/connectors/codex-materialize.d.ts +1 -1
  73. package/dist/connectors/index.d.ts +1 -1
  74. package/dist/connectors/index.js +4 -3
  75. package/dist/consolidation-provenance-check.d.ts +1 -1
  76. package/dist/consolidation-undo.d.ts +1 -1
  77. package/dist/contradiction/index.d.ts +1 -1
  78. package/dist/conversation-index/backend.d.ts +1 -1
  79. package/dist/conversation-index/chunker.d.ts +1 -1
  80. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  81. package/dist/conversation-index/indexer.d.ts +1 -1
  82. package/dist/conversation-index/search.d.ts +1 -1
  83. package/dist/day-summary.d.ts +1 -1
  84. package/dist/delinearize.d.ts +1 -1
  85. package/dist/direct-answer-wiring.d.ts +1 -1
  86. package/dist/direct-answer.d.ts +1 -1
  87. package/dist/embedding-fallback.d.ts +1 -1
  88. package/dist/enrichment/index.d.ts +1 -1
  89. package/dist/entity-retrieval.d.ts +1 -1
  90. package/dist/entity-retrieval.js +4 -3
  91. package/dist/entity-schema.d.ts +1 -1
  92. package/dist/explicit-capture.d.ts +3 -3
  93. package/dist/extraction-judge-telemetry.d.ts +1 -1
  94. package/dist/extraction-judge-training.d.ts +1 -1
  95. package/dist/extraction-judge.d.ts +1 -1
  96. package/dist/extraction.d.ts +1 -1
  97. package/dist/fallback-llm.d.ts +1 -1
  98. package/dist/identity-continuity.d.ts +1 -1
  99. package/dist/importance.d.ts +1 -1
  100. package/dist/index.d.ts +8 -8
  101. package/dist/index.js +30 -30
  102. package/dist/intent.d.ts +1 -1
  103. package/dist/lcm/engine.d.ts +1 -1
  104. package/dist/lcm/index.d.ts +1 -1
  105. package/dist/lcm/tools.d.ts +1 -1
  106. package/dist/lifecycle.d.ts +1 -1
  107. package/dist/live-connectors-runner.d.ts +1 -1
  108. package/dist/local-llm.d.ts +1 -1
  109. package/dist/maintenance/memory-governance.d.ts +1 -1
  110. package/dist/maintenance/memory-governance.js +4 -3
  111. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +4 -3
  112. package/dist/maintenance/rebuild-memory-projection.js +5 -4
  113. package/dist/mcp-memory-inspector-app.d.ts +5 -5
  114. package/dist/memory-action-policy.d.ts +1 -1
  115. package/dist/memory-cache.d.ts +44 -2
  116. package/dist/memory-cache.js +6 -1
  117. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  118. package/dist/memory-projection-store.d.ts +1 -1
  119. package/dist/memory-provenance.d.ts +1 -1
  120. package/dist/memory-worth-outcomes.d.ts +1 -1
  121. package/dist/models-json.d.ts +1 -1
  122. package/dist/namespaces/migrate.d.ts +1 -1
  123. package/dist/namespaces/migrate.js +12 -11
  124. package/dist/namespaces/principal.d.ts +1 -1
  125. package/dist/namespaces/search.d.ts +1 -1
  126. package/dist/namespaces/search.js +9 -8
  127. package/dist/namespaces/storage.d.ts +1 -1
  128. package/dist/namespaces/storage.js +4 -3
  129. package/dist/native-knowledge.d.ts +1 -1
  130. package/dist/operator-toolkit.d.ts +1 -1
  131. package/dist/operator-toolkit.js +14 -13
  132. package/dist/{orchestrator-D1wcmPNj.d.ts → orchestrator-Cg1UkvmO.d.ts} +2 -2
  133. package/dist/orchestrator.d.ts +3 -3
  134. package/dist/orchestrator.js +20 -20
  135. package/dist/patterns-cli.d.ts +1 -1
  136. package/dist/policy-runtime.d.ts +1 -1
  137. package/dist/qmd-recall-cache.d.ts +5 -2
  138. package/dist/qmd-recall-cache.js +3 -1
  139. package/dist/qmd.d.ts +17 -1
  140. package/dist/qmd.js +4 -3
  141. package/dist/recall-disclosure-escalation.d.ts +1 -1
  142. package/dist/recall-explain-renderer.d.ts +2 -1
  143. package/dist/recall-planner-llm.d.ts +1 -1
  144. package/dist/recall-state.d.ts +17 -1
  145. package/dist/recall-state.js +1 -1
  146. package/dist/recall-tag-filter.d.ts +1 -1
  147. package/dist/recall-xray-cli.d.ts +1 -1
  148. package/dist/recall-xray-renderer.d.ts +1 -1
  149. package/dist/recall-xray.d.ts +1 -1
  150. package/dist/resolve-auth-token.d.ts +1 -1
  151. package/dist/retrieval-agents.d.ts +1 -1
  152. package/dist/retrieval-tiers.d.ts +1 -1
  153. package/dist/routing/engine.d.ts +1 -1
  154. package/dist/routing/store.d.ts +1 -1
  155. package/dist/schemas.d.ts +24 -24
  156. package/dist/search/embed-helper.d.ts +1 -1
  157. package/dist/search/factory.d.ts +1 -1
  158. package/dist/search/factory.js +8 -7
  159. package/dist/search/index.d.ts +1 -1
  160. package/dist/search/index.js +8 -7
  161. package/dist/search/lancedb-backend.d.ts +1 -1
  162. package/dist/search/lancedb-backend.js +2 -2
  163. package/dist/search/meilisearch-backend.d.ts +1 -1
  164. package/dist/search/meilisearch-backend.js +2 -2
  165. package/dist/search/noop-backend.d.ts +1 -1
  166. package/dist/search/orama-backend.d.ts +1 -1
  167. package/dist/search/orama-backend.js +2 -2
  168. package/dist/search/port.d.ts +21 -2
  169. package/dist/search/port.js +1 -1
  170. package/dist/search/remote-backend.d.ts +1 -1
  171. package/dist/{semantic-consolidation-MWOdNtSE.d.ts → semantic-consolidation-BICZvQ3C.d.ts} +1 -1
  172. package/dist/semantic-consolidation.d.ts +2 -2
  173. package/dist/semantic-consolidation.js +5 -4
  174. package/dist/semantic-rule-promotion.js +4 -3
  175. package/dist/semantic-rule-verifier.d.ts +1 -1
  176. package/dist/semantic-rule-verifier.js +4 -3
  177. package/dist/session-observer-bands.d.ts +1 -1
  178. package/dist/session-observer-state.d.ts +1 -1
  179. package/dist/shared-context/manager.d.ts +1 -1
  180. package/dist/signal.d.ts +1 -1
  181. package/dist/storage.d.ts +1 -1
  182. package/dist/storage.js +3 -2
  183. package/dist/summarizer.d.ts +1 -1
  184. package/dist/summary-snapshot.d.ts +1 -1
  185. package/dist/temporal-supersession.d.ts +1 -1
  186. package/dist/temporal-validity.d.ts +1 -1
  187. package/dist/threading.d.ts +1 -1
  188. package/dist/tier-migration.d.ts +1 -1
  189. package/dist/tier-routing.d.ts +1 -1
  190. package/dist/topics.d.ts +1 -1
  191. package/dist/transcript.d.ts +1 -1
  192. package/dist/{types-CgcCpUrf.d.ts → types-D96bCB3C.d.ts} +1 -1
  193. package/dist/types.d.ts +1 -1
  194. package/dist/utility-runtime.d.ts +1 -1
  195. package/dist/verified-recall.js +4 -3
  196. package/package.json +1 -1
  197. package/src/memory-cache.test.ts +30 -0
  198. package/src/memory-cache.ts +129 -16
  199. package/src/namespaces/search.ts +16 -0
  200. package/src/orchestrator.ts +144 -3
  201. package/src/qmd-recall-cache.ts +6 -0
  202. package/src/qmd.ts +131 -18
  203. package/src/recall-state.ts +47 -21
  204. package/src/search/port.ts +25 -0
  205. package/src/storage.ts +27 -3
  206. package/dist/chunk-4PLOQDBB.js.map +0 -1
  207. package/dist/chunk-7VWDC7AD.js.map +0 -1
  208. package/dist/chunk-CPPS65WS.js.map +0 -1
  209. package/dist/chunk-EKQMQQ3U.js.map +0 -1
  210. package/dist/chunk-FWIROLS6.js.map +0 -1
  211. package/dist/chunk-M3VYPE2H.js.map +0 -1
  212. package/dist/chunk-QRSKPI62.js.map +0 -1
  213. /package/dist/{chunk-5PFIMBJJ.js.map → chunk-4SYURHI6.js.map} +0 -0
  214. /package/dist/{chunk-2VCTTEJM.js.map → chunk-5G2DNO54.js.map} +0 -0
  215. /package/dist/{chunk-3R6OP33G.js.map → chunk-7F7LC6HW.js.map} +0 -0
  216. /package/dist/{chunk-ZCMO46YY.js.map → chunk-A7EF2XRO.js.map} +0 -0
  217. /package/dist/{chunk-BNUAOLDK.js.map → chunk-ANJOULTP.js.map} +0 -0
  218. /package/dist/{chunk-QFKRE7AU.js.map → chunk-BP5O3GYD.js.map} +0 -0
  219. /package/dist/{chunk-BKRIAXTU.js.map → chunk-D2EFNQMY.js.map} +0 -0
  220. /package/dist/{chunk-6M4LYWA2.js.map → chunk-D44FQVCU.js.map} +0 -0
  221. /package/dist/{chunk-SSSXWIBP.js.map → chunk-EMSC4P66.js.map} +0 -0
  222. /package/dist/{chunk-MBZAESQ3.js.map → chunk-F6O7IOS3.js.map} +0 -0
  223. /package/dist/{chunk-6G5JEN55.js.map → chunk-FZC2WSDB.js.map} +0 -0
  224. /package/dist/{chunk-KI6QM5AV.js.map → chunk-GY3V3SUI.js.map} +0 -0
  225. /package/dist/{chunk-VJYFXDCZ.js.map → chunk-NMPEJV5M.js.map} +0 -0
  226. /package/dist/{chunk-7KSPKZIQ.js.map → chunk-PXVFMQLD.js.map} +0 -0
  227. /package/dist/{chunk-FIS5RT6K.js.map → chunk-QXHBWFR3.js.map} +0 -0
  228. /package/dist/{chunk-G2VVBWFU.js.map → chunk-RQGR3ETH.js.map} +0 -0
  229. /package/dist/{chunk-GGL7R2L2.js.map → chunk-RQRKQJYM.js.map} +0 -0
  230. /package/dist/{chunk-JI3LQFJH.js.map → chunk-TBLGI2LT.js.map} +0 -0
  231. /package/dist/{chunk-RVT6U6PV.js.map → chunk-TWAJICBN.js.map} +0 -0
  232. /package/dist/{chunk-46RXRASB.js.map → chunk-TYIXG4VR.js.map} +0 -0
  233. /package/dist/{chunk-5PLUC5OB.js.map → chunk-WSQG37DV.js.map} +0 -0
  234. /package/dist/{chunk-GRYAECRV.js.map → chunk-ZJH723NM.js.map} +0 -0
@@ -3,6 +3,7 @@ import path from "node:path";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import { log } from "./logger.js";
5
5
  import { writeFileAtomically } from "./maintenance/atomic-file.js";
6
+ import type { SearchDegradation } from "./search/port.js";
6
7
  import type {
7
8
  IdentityInjectionMode,
8
9
  RecallPlanMode,
@@ -59,6 +60,14 @@ export interface LastRecallSnapshot {
59
60
  * graph-path `recallExplain` operation.
60
61
  */
61
62
  tierExplain?: RecallTierExplain;
63
+ /**
64
+ * Backend degradations observed while serving this recall (issue #1536).
65
+ * Present only when a search backend reported unavailable/loading/timeout
66
+ * during the recall — distinguishing "no matches" from "backend could not
67
+ * answer" (CLAUDE.md rule 34). Deliberately independent of `tierExplain`,
68
+ * which is gated behind `recallDirectAnswerEnabled`.
69
+ */
70
+ backendDegradations?: SearchDegradation[];
62
71
  }
63
72
 
64
73
  export interface GraphRecallExpandedEntry {
@@ -136,6 +145,31 @@ type StateFileWriter = (filePath: string, content: string) => Promise<void>;
136
145
  * qmd-recall-cache.ts). The payload is pure JSON-shaped data, so
137
146
  * structuredClone is both safe and complete here.
138
147
  */
148
+ /**
149
+ * Stale-snapshot identity guard shared by the annotate methods (issue #518):
150
+ * a writeNonce match wins outright; otherwise traceId (when expected) and
151
+ * recordedAt are compared. Extracted so every annotation surface applies
152
+ * identical guards (CLAUDE.md rule 22).
153
+ */
154
+ function snapshotMatchesExpectedIdentity(
155
+ current: LastRecallSnapshot,
156
+ expected?: { writeNonce?: string; traceId?: string; recordedAt?: string },
157
+ ): boolean {
158
+ if (!expected) return true;
159
+ if (typeof expected.writeNonce === "string" && expected.writeNonce.length > 0) {
160
+ return current.writeNonce === expected.writeNonce;
161
+ }
162
+ const hasExpectedTraceId =
163
+ typeof expected.traceId === "string" && expected.traceId.length > 0;
164
+ if (hasExpectedTraceId) {
165
+ return current.traceId === expected.traceId;
166
+ }
167
+ if (expected.recordedAt !== undefined) {
168
+ return current.recordedAt === expected.recordedAt;
169
+ }
170
+ return true;
171
+ }
172
+
139
173
  function cloneTierExplain(
140
174
  tierExplain: RecallTierExplain | undefined,
141
175
  ): RecallTierExplain | undefined {
@@ -271,6 +305,13 @@ export class LastRecallStore {
271
305
  * can render which retrieval tier served the query.
272
306
  */
273
307
  tierExplain?: RecallTierExplain;
308
+ /**
309
+ * Backend degradations observed while serving this recall (issue #1536).
310
+ * Passed at record time so the published snapshot is born annotated —
311
+ * a post-record annotation would leave a window where readers see the
312
+ * snapshot without them (codex review on #1544).
313
+ */
314
+ backendDegradations?: SearchDegradation[];
274
315
  }): Promise<void> {
275
316
  const now = new Date().toISOString();
276
317
  const queryHash = createHash("sha256").update(opts.query).digest("hex");
@@ -302,6 +343,10 @@ export class LastRecallStore {
302
343
  identityInjectedChars: opts.identityInjection?.injectedChars,
303
344
  identityInjectionTruncated: opts.identityInjection?.truncated,
304
345
  tierExplain: opts.tierExplain,
346
+ backendDegradations:
347
+ opts.backendDegradations && opts.backendDegradations.length > 0
348
+ ? opts.backendDegradations
349
+ : undefined,
305
350
  };
306
351
  // `cloneLastRecallSnapshot` handles `null` but that never applies
307
352
  // at this call site — the non-null assertion keeps the type
@@ -355,27 +400,7 @@ export class LastRecallStore {
355
400
  ): Promise<void> {
356
401
  const current = this.state[sessionKey];
357
402
  if (!current) return;
358
- if (expected) {
359
- if (
360
- typeof expected.writeNonce === "string" &&
361
- expected.writeNonce.length > 0
362
- ) {
363
- if (current.writeNonce !== expected.writeNonce) return;
364
- } else {
365
- const hasExpectedTraceId =
366
- typeof expected.traceId === "string" && expected.traceId.length > 0;
367
- const traceIdMatches =
368
- hasExpectedTraceId && current.traceId === expected.traceId;
369
- const recordedAtMatches =
370
- expected.recordedAt !== undefined &&
371
- current.recordedAt === expected.recordedAt;
372
- if (hasExpectedTraceId) {
373
- if (!traceIdMatches) return;
374
- } else if (expected.recordedAt !== undefined && !recordedAtMatches) {
375
- return;
376
- }
377
- }
378
- }
403
+ if (!snapshotMatchesExpectedIdentity(current, expected)) return;
379
404
  this.state[sessionKey] = {
380
405
  ...current,
381
406
  tierExplain: cloneTierExplain(tierExplain),
@@ -387,6 +412,7 @@ export class LastRecallStore {
387
412
  }
388
413
  }
389
414
 
415
+
390
416
  private flushState(): Promise<void> {
391
417
  const run = this.stateWriteChain.catch(() => undefined).then(async () => {
392
418
  await mkdir(path.dirname(this.statePath), { recursive: true });
@@ -12,8 +12,33 @@ export interface SearchQueryOptions {
12
12
  structuredSearches?: Array<{ type: "lex" | "vec" | "hyde"; query: string }>;
13
13
  }
14
14
 
15
+ /**
16
+ * A search that returned empty (or partial) results because the backend was
17
+ * unavailable, still loading, or timed out — cases that are otherwise
18
+ * indistinguishable from a genuine "no matches" (issue #1536, CLAUDE.md
19
+ * rule 34).
20
+ */
21
+ export interface SearchDegradation {
22
+ backend: "qmd";
23
+ code:
24
+ | "backend_unavailable"
25
+ | "daemon_timeout"
26
+ | "daemon_loading"
27
+ | "subprocess_error"
28
+ | "deadline_exceeded";
29
+ detail?: string;
30
+ }
31
+
15
32
  export interface SearchExecutionOptions {
16
33
  signal?: AbortSignal;
34
+ /**
35
+ * Observer invoked when the backend degrades during this call (#1536).
36
+ * Callers that need to distinguish empty-because-degraded from
37
+ * empty-because-no-matches (recall x-ray, fallback decisions) pass a
38
+ * collector here. Observer failures are swallowed by the notifier —
39
+ * observability must never break search.
40
+ */
41
+ onDegradation?: (degradation: SearchDegradation) => void;
17
42
  }
18
43
 
19
44
  export function resolveEnsureCollectionArgs(
package/src/storage.ts CHANGED
@@ -5,7 +5,7 @@ import path from "node:path";
5
5
  import { log } from "./logger.js";
6
6
  import { isErrnoCode } from "./utils/errno.js";
7
7
  import { RECALL_FALLBACK_DIRS } from "./utils/category-dir.js";
8
- import { getCachedEntities, invalidateCachedEntities, setCachedEntities } from "./memory-cache.js";
8
+ import { getCachedEntities, invalidateAllForDir, setCachedEntities } from "./memory-cache.js";
9
9
  import { rotateMarkdownFileToArchive } from "./hygiene.js";
10
10
  import { sanitizeMemoryContent } from "./sanitize.js";
11
11
  import { createVersion as createPageVersion, type VersioningConfig, type VersionTrigger } from "./page-versioning.js";
@@ -2335,7 +2335,10 @@ export class StorageManager {
2335
2335
  setSecureStoreKey(key: Buffer | null, encryptOnWrite = true): void {
2336
2336
  this._secureStoreKey = key;
2337
2337
  this._secureStoreEncryptOnWrite = encryptOnWrite;
2338
- invalidateCachedEntities(this.baseDir);
2338
+ // Route through the invalidation chokepoint (issue #1535): a key change
2339
+ // (or store lock) must evict every cache layer that may hold content
2340
+ // decrypted under the previous key, not just the entity layer.
2341
+ invalidateAllForDir(this.baseDir);
2339
2342
  this.invalidateKnowledgeIndexCache();
2340
2343
  }
2341
2344
 
@@ -2511,6 +2514,13 @@ export class StorageManager {
2511
2514
 
2512
2515
  private bumpMemoryStatusVersion(): void {
2513
2516
  this.bumpSharedVersion("memory-status", StorageManager.memoryStatusVersionByDir);
2517
+ // Invalidation chokepoint (issue #1535): status/entity mutations funnel
2518
+ // through this bump — several of them (supersedeMemory, archiveMemories,
2519
+ // writeEntity, cleanExpiredCommitments) do NOT call
2520
+ // invalidateAllMemoriesCache(), so the chokepoint must fire here as well.
2521
+ // Version bumps only invalidate the version-checked layers lazily; the
2522
+ // TTL-based QMD caches need this eager clear.
2523
+ invalidateAllForDir(this.baseDir);
2514
2524
  }
2515
2525
 
2516
2526
  getMemoryStatusVersion(): number {
@@ -2943,8 +2953,9 @@ export class StorageManager {
2943
2953
  }
2944
2954
 
2945
2955
  private async invalidateAfterOfflineSyncMutation(filePath: string): Promise<void> {
2956
+ // invalidateAllMemoriesCache() routes through the invalidateAllForDir()
2957
+ // chokepoint, which also covers the entity cache (issue #1535).
2946
2958
  this.invalidateAllMemoriesCache();
2947
- invalidateCachedEntities(this.baseDir);
2948
2959
  this.invalidateKnowledgeIndexCache();
2949
2960
  this.factHashIndexAuthoritative = false;
2950
2961
  await unlink(this.factHashIndexReadyPath).catch((error: unknown) => {
@@ -4002,6 +4013,13 @@ export class StorageManager {
4002
4013
  * inside cold/, archiveMemory, etc.). */
4003
4014
  private invalidateAllMemoriesCache(): void {
4004
4015
  StorageManager.allMemoriesInFlight.delete(this.baseDir);
4016
+ // Invalidation chokepoint (issue #1535): eagerly evict EVERY process-level
4017
+ // cache layer that can serve memory-derived content for this dir — hot,
4018
+ // archive, entity, derived episode/rule views, and both QMD result caches
4019
+ // (qmdSearchCache and qmdRecallCache). Before this call was added, the QMD
4020
+ // recall cache was never invalidated on mutations, so recall served
4021
+ // pre-edit bundles for the remainder of its fresh/stale TTL window.
4022
+ invalidateAllForDir(this.baseDir);
4005
4023
  }
4006
4024
 
4007
4025
  /**
@@ -4020,6 +4038,12 @@ export class StorageManager {
4020
4038
  const coldRoot = path.join(this.baseDir, "cold");
4021
4039
  StorageManager.coldMemoriesCache.delete(coldRoot);
4022
4040
  this.bumpColdWriteVersion();
4041
+ // Invalidation chokepoint (issue #1535): cold-tier mutations can reach
4042
+ // this funnel without invalidateAllMemoriesCache() (e.g. the cold-only
4043
+ // branch of invalidateMemoryCachesForTiers used by maintenance/purge), so
4044
+ // the chokepoint must fire here too — cold memories are recallable and a
4045
+ // cold delete must not leave stale QMD recall bundles behind.
4046
+ invalidateAllForDir(this.baseDir);
4023
4047
  }
4024
4048
 
4025
4049
  /** Return the current cold-write version counter for this storage root.