@remnic/core 9.7.8 → 9.7.10

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 (406) hide show
  1. package/dist/access-admin-ops-surface.d.ts +6 -6
  2. package/dist/access-admin-ops-surface.js +32 -30
  3. package/dist/access-audit.js +2 -2
  4. package/dist/access-boundary.d.ts +6 -6
  5. package/dist/access-boundary.js +33 -31
  6. package/dist/access-cli.js +92 -90
  7. package/dist/access-cli.js.map +1 -1
  8. package/dist/access-http.d.ts +6 -6
  9. package/dist/access-http.js +40 -38
  10. package/dist/access-identity-continuity-surface.d.ts +5 -5
  11. package/dist/access-identity-continuity-surface.js +32 -30
  12. package/dist/access-lcm-surface.d.ts +6 -6
  13. package/dist/access-lcm-surface.js +32 -30
  14. package/dist/access-mcp.d.ts +6 -6
  15. package/dist/access-mcp.js +37 -35
  16. package/dist/access-observe-write-surface.d.ts +6 -6
  17. package/dist/access-observe-write-surface.js +32 -30
  18. package/dist/access-operations-batch.js +35 -33
  19. package/dist/access-operations.d.ts +9 -9
  20. package/dist/access-operations.js +36 -34
  21. package/dist/access-recall-surface.d.ts +6 -6
  22. package/dist/access-recall-surface.js +32 -30
  23. package/dist/access-schema.d.ts +64 -64
  24. package/dist/{access-service-CxDZXTKt.d.ts → access-service-DSuzikHQ.d.ts} +4 -4
  25. package/dist/access-service.d.ts +6 -6
  26. package/dist/access-service.js +32 -30
  27. package/dist/access-surface-catalog.d.ts +6 -6
  28. package/dist/action-confidence.d.ts +1 -1
  29. package/dist/active-memory-bridge.d.ts +1 -1
  30. package/dist/active-recall.d.ts +1 -1
  31. package/dist/active-recall.js +1 -1
  32. package/dist/adapters/index.js +4 -4
  33. package/dist/adapters/registry.js +2 -2
  34. package/dist/{auto-sync-676N6N6Y.js → auto-sync-AAMTVZMK.js} +5 -5
  35. package/dist/behavior-learner.d.ts +1 -1
  36. package/dist/behavior-signals.d.ts +1 -1
  37. package/dist/bootstrap.d.ts +5 -5
  38. package/dist/briefing.d.ts +2 -2
  39. package/dist/briefing.js +15 -13
  40. package/dist/buffer-surprise-report.d.ts +1 -1
  41. package/dist/buffer.d.ts +2 -2
  42. package/dist/bulk-import/index.d.ts +3 -3
  43. package/dist/calibration.d.ts +1 -1
  44. package/dist/calibration.js +2 -2
  45. package/dist/capabilities.d.ts +1 -1
  46. package/dist/{capsule-merge-GK5E647P.js → capsule-merge-Z2HUO6IK.js} +10 -2
  47. package/dist/capsule-merge-Z2HUO6IK.js.map +1 -0
  48. package/dist/{catalog-DzU9HowE.d.ts → catalog-BNwjACa5.d.ts} +1 -1
  49. package/dist/causal-behavior.d.ts +1 -1
  50. package/dist/causal-consolidation.d.ts +1 -1
  51. package/dist/causal-consolidation.js +16 -14
  52. package/dist/causal-consolidation.js.map +1 -1
  53. package/dist/causal-trajectory-graph.d.ts +1 -1
  54. package/dist/{chunk-HHAA35A3.js → chunk-2VN4JAX7.js} +548 -161
  55. package/dist/chunk-2VN4JAX7.js.map +1 -0
  56. package/dist/{chunk-CNWC274T.js → chunk-3OQ4IZJN.js} +28 -28
  57. package/dist/{chunk-JO5AT6QT.js → chunk-3ZVFLMFQ.js} +1 -1
  58. package/dist/chunk-3ZVFLMFQ.js.map +1 -0
  59. package/dist/{chunk-7XH7VJN4.js → chunk-5S7Z5SHA.js} +2 -2
  60. package/dist/{chunk-WKE4VXSZ.js → chunk-673NPXE3.js} +2 -2
  61. package/dist/{chunk-Q6JPMCPO.js → chunk-7CKS4AXY.js} +63 -58
  62. package/dist/chunk-7CKS4AXY.js.map +1 -0
  63. package/dist/{chunk-UHGHTOR5.js → chunk-7EX4YOKH.js} +7 -7
  64. package/dist/{chunk-TVVEYCNW.js → chunk-7K5Q6COX.js} +4 -4
  65. package/dist/{chunk-EJCVMPIM.js → chunk-7LMEY5KZ.js} +15 -1
  66. package/dist/chunk-7LMEY5KZ.js.map +1 -0
  67. package/dist/{chunk-3F4N4WDH.js → chunk-AHLPZ2SF.js} +2 -2
  68. package/dist/{chunk-6C35R7P4.js → chunk-DJCKXI4M.js} +2 -2
  69. package/dist/{chunk-AD2KK5MT.js → chunk-DLWYAN6A.js} +9 -3
  70. package/dist/chunk-DLWYAN6A.js.map +1 -0
  71. package/dist/{chunk-TVRPRFUB.js → chunk-EQF5763Z.js} +9 -7
  72. package/dist/chunk-EQF5763Z.js.map +1 -0
  73. package/dist/{chunk-FYKIEOG6.js → chunk-FNJX2O6G.js} +3 -3
  74. package/dist/{chunk-DQPPRXFO.js → chunk-HQ7QZJM5.js} +2 -2
  75. package/dist/{chunk-RKNJBZ55.js → chunk-JBPKEARU.js} +4 -4
  76. package/dist/{chunk-KIPLG7S7.js → chunk-JKLJ6A7W.js} +2 -2
  77. package/dist/{chunk-O3BZ6PFY.js → chunk-JZM5QUTE.js} +2 -2
  78. package/dist/{chunk-KTOH7EJE.js → chunk-KXLRMM35.js} +312 -48
  79. package/dist/chunk-KXLRMM35.js.map +1 -0
  80. package/dist/{chunk-ZUFJ7ZTR.js → chunk-L4H6MQ7I.js} +6 -2
  81. package/dist/chunk-L4H6MQ7I.js.map +1 -0
  82. package/dist/{chunk-SYNYI4ZM.js → chunk-L5HJQC36.js} +2 -2
  83. package/dist/{chunk-VXMWDXIJ.js → chunk-LC7OODWZ.js} +2 -2
  84. package/dist/{chunk-ZW6OKHVX.js → chunk-LQKITFGB.js} +2 -2
  85. package/dist/{chunk-TZJVTH3F.js → chunk-MXRMNKSY.js} +14 -14
  86. package/dist/{chunk-GL3SMLMZ.js → chunk-N3LWQJ6Y.js} +150 -119
  87. package/dist/chunk-N3LWQJ6Y.js.map +1 -0
  88. package/dist/{chunk-M5QKGHCR.js → chunk-OC35KQIN.js} +6 -2
  89. package/dist/chunk-OC35KQIN.js.map +1 -0
  90. package/dist/chunk-OKUEZOGW.js +27 -0
  91. package/dist/chunk-OKUEZOGW.js.map +1 -0
  92. package/dist/{chunk-PKGWJFNR.js → chunk-OVMS3DCB.js} +2 -2
  93. package/dist/chunk-PDJTHRAW.js +36 -0
  94. package/dist/chunk-PDJTHRAW.js.map +1 -0
  95. package/dist/{chunk-LRGSLV7M.js → chunk-PYHOAXTI.js} +4 -4
  96. package/dist/{chunk-NMMKRVUF.js → chunk-QVALQE4P.js} +6 -6
  97. package/dist/{chunk-FXGC5UUH.js → chunk-RT7IP3D3.js} +2 -2
  98. package/dist/{chunk-AH3M6F4X.js → chunk-SBFDL2EQ.js} +2 -2
  99. package/dist/{chunk-UYZUJ4EB.js → chunk-SECMZACR.js} +2 -2
  100. package/dist/{chunk-HXDKC3BI.js → chunk-TGBJ2OMK.js} +2 -2
  101. package/dist/{chunk-DE6ENYLG.js → chunk-TXLGHDHS.js} +2 -2
  102. package/dist/{chunk-74MJH7I3.js → chunk-U4KHQTJ6.js} +12 -3
  103. package/dist/chunk-U4KHQTJ6.js.map +1 -0
  104. package/dist/{chunk-CRFTLUWT.js → chunk-UTDA43TB.js} +2 -2
  105. package/dist/{chunk-GKCCHHZK.js → chunk-UXKMMNJD.js} +4 -4
  106. package/dist/{chunk-57INMZ6F.js → chunk-UZRIKUFS.js} +2 -2
  107. package/dist/{chunk-2FSHROYK.js → chunk-V53GJUSL.js} +8 -8
  108. package/dist/{chunk-LKT755LP.js → chunk-VA4WSW54.js} +2 -2
  109. package/dist/{chunk-ZHKD4EZM.js → chunk-VCEH5UYS.js} +3 -3
  110. package/dist/{chunk-CHVU4RE5.js → chunk-VDLMAJXS.js} +3 -3
  111. package/dist/{chunk-XFTPUXCE.js → chunk-VRAD2FGO.js} +10 -10
  112. package/dist/{chunk-VF4XKTX3.js → chunk-VZ5OJE4X.js} +10 -2
  113. package/dist/chunk-VZ5OJE4X.js.map +1 -0
  114. package/dist/{chunk-UVIQHSS7.js → chunk-WUDBMLC6.js} +9 -3
  115. package/dist/chunk-WUDBMLC6.js.map +1 -0
  116. package/dist/{chunk-AWJ2FHCF.js → chunk-X47LWMKA.js} +49 -21
  117. package/dist/chunk-X47LWMKA.js.map +1 -0
  118. package/dist/{chunk-VMGMY2AI.js → chunk-XF4KOVDK.js} +8 -8
  119. package/dist/{chunk-VMGMY2AI.js.map → chunk-XF4KOVDK.js.map} +1 -1
  120. package/dist/{chunk-T7VNN3ZM.js → chunk-XVLEGMJI.js} +2 -2
  121. package/dist/{chunk-R2SJDIPO.js → chunk-YLTG6BO4.js} +9 -7
  122. package/dist/chunk-YLTG6BO4.js.map +1 -0
  123. package/dist/{chunk-QDCJNRKK.js → chunk-YXOAQAH2.js} +3 -3
  124. package/dist/{cli-BqyGVyOe.d.ts → cli-DACyuK8-.d.ts} +5 -5
  125. package/dist/cli.d.ts +8 -8
  126. package/dist/cli.js +66 -64
  127. package/dist/compounding/engine.d.ts +2 -2
  128. package/dist/compounding/engine.js +15 -13
  129. package/dist/compounding/preference-consolidator.d.ts +1 -1
  130. package/dist/compression-optimizer.d.ts +1 -1
  131. package/dist/config.d.ts +1 -1
  132. package/dist/config.js +1 -1
  133. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  134. package/dist/connectors/codex-materialize-runner.js +15 -13
  135. package/dist/connectors/codex-materialize.d.ts +1 -1
  136. package/dist/connectors/index.d.ts +1 -1
  137. package/dist/connectors/index.js +15 -13
  138. package/dist/consolidation-provenance-check.d.ts +2 -2
  139. package/dist/consolidation-provenance-check.js +6 -2
  140. package/dist/consolidation-undo.d.ts +2 -2
  141. package/dist/consolidation-undo.js +8 -1
  142. package/dist/consolidation-undo.js.map +1 -1
  143. package/dist/contradiction/index.d.ts +2 -2
  144. package/dist/conversation-index/backend.d.ts +1 -1
  145. package/dist/conversation-index/chunker.d.ts +1 -1
  146. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  147. package/dist/conversation-index/indexer.d.ts +1 -1
  148. package/dist/conversation-index/search.d.ts +1 -1
  149. package/dist/day-summary.d.ts +1 -1
  150. package/dist/delinearize.d.ts +1 -1
  151. package/dist/direct-answer-wiring.d.ts +1 -1
  152. package/dist/direct-answer.d.ts +1 -1
  153. package/dist/embedding-fallback.d.ts +1 -1
  154. package/dist/enrichment/index.d.ts +1 -1
  155. package/dist/entity-retrieval.d.ts +2 -2
  156. package/dist/entity-retrieval.js +15 -13
  157. package/dist/entity-schema.d.ts +1 -1
  158. package/dist/explicit-capture.d.ts +5 -5
  159. package/dist/extraction-faithfulness.d.ts +1 -1
  160. package/dist/extraction-faithfulness.js +3 -3
  161. package/dist/extraction-judge-telemetry.d.ts +1 -1
  162. package/dist/extraction-judge-training.d.ts +1 -1
  163. package/dist/extraction-judge.d.ts +1 -1
  164. package/dist/extraction-judge.js +3 -3
  165. package/dist/extraction.d.ts +1 -1
  166. package/dist/extraction.js +3 -3
  167. package/dist/fallback-llm.d.ts +1 -1
  168. package/dist/fallback-llm.js +2 -2
  169. package/dist/{forget-KHF7A44H.js → forget-CQCLB5LB.js} +2 -2
  170. package/dist/graph-dashboard-diff.d.ts +1 -1
  171. package/dist/graph-dashboard-key.d.ts +1 -1
  172. package/dist/graph-dashboard-parser.d.ts +1 -1
  173. package/dist/graph-edge-reinforcement.d.ts +1 -1
  174. package/dist/graph-snapshot.d.ts +1 -1
  175. package/dist/graph.d.ts +1 -1
  176. package/dist/identity-continuity.d.ts +1 -1
  177. package/dist/importance.d.ts +1 -1
  178. package/dist/importers/index.d.ts +1 -1
  179. package/dist/in-flight-reads.d.ts +19 -0
  180. package/dist/in-flight-reads.js +16 -0
  181. package/dist/in-flight-reads.js.map +1 -0
  182. package/dist/index.d.ts +13 -13
  183. package/dist/index.js +116 -114
  184. package/dist/intent.d.ts +1 -1
  185. package/dist/lcm/engine.d.ts +1 -1
  186. package/dist/lcm/engine.js +4 -4
  187. package/dist/lcm/index.d.ts +1 -1
  188. package/dist/lcm/index.js +13 -13
  189. package/dist/lcm/tools.d.ts +1 -1
  190. package/dist/lifecycle.d.ts +1 -1
  191. package/dist/live-connectors-runner.d.ts +1 -1
  192. package/dist/local-llm.d.ts +1 -1
  193. package/dist/local-model-endpoint.d.ts +1 -1
  194. package/dist/maintenance/memory-governance.d.ts +1 -1
  195. package/dist/maintenance/memory-governance.js +15 -13
  196. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +15 -13
  197. package/dist/maintenance/rebuild-memory-projection.js +16 -14
  198. package/dist/mcp-memory-inspector-app.d.ts +6 -6
  199. package/dist/memory-action-policy.d.ts +1 -1
  200. package/dist/memory-cache.d.ts +24 -13
  201. package/dist/memory-cache.js +3 -1
  202. package/dist/memory-corpus-version.d.ts +19 -0
  203. package/dist/memory-corpus-version.js +13 -0
  204. package/dist/memory-corpus-version.js.map +1 -0
  205. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  206. package/dist/memory-projection-store.d.ts +1 -1
  207. package/dist/memory-provenance.d.ts +1 -1
  208. package/dist/memory-worth-outcomes.d.ts +2 -2
  209. package/dist/models-json.d.ts +1 -1
  210. package/dist/namespaces/migrate.d.ts +3 -3
  211. package/dist/namespaces/migrate.js +22 -20
  212. package/dist/namespaces/principal.d.ts +1 -1
  213. package/dist/namespaces/search.d.ts +1 -1
  214. package/dist/namespaces/search.js +7 -7
  215. package/dist/namespaces/storage.d.ts +3 -3
  216. package/dist/namespaces/storage.js +15 -13
  217. package/dist/native-knowledge.d.ts +1 -1
  218. package/dist/operator-toolkit.d.ts +2 -2
  219. package/dist/operator-toolkit.js +27 -25
  220. package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
  221. package/dist/orchestration/maintenance.d.ts +2 -2
  222. package/dist/orchestration/maintenance.js +17 -15
  223. package/dist/{orchestrator-37Cx7yyw.d.ts → orchestrator-D83AvrPf.d.ts} +4 -4
  224. package/dist/orchestrator.d.ts +5 -5
  225. package/dist/orchestrator.js +92 -90
  226. package/dist/page-versioning.js +5 -1
  227. package/dist/patterns-cli.d.ts +1 -1
  228. package/dist/{pipeline-C6onljHc.d.ts → pipeline-BSqGpYl1.d.ts} +1 -1
  229. package/dist/policy-runtime.d.ts +1 -1
  230. package/dist/provenance.d.ts +1 -1
  231. package/dist/{qmd-J08G7MEm.d.ts → qmd-CutnpRVe.d.ts} +1 -1
  232. package/dist/qmd-preflight.d.ts +2 -2
  233. package/dist/qmd-recall-cache.d.ts +1 -1
  234. package/dist/qmd.d.ts +2 -2
  235. package/dist/qmd.js +2 -2
  236. package/dist/recall-disclosure-escalation.d.ts +1 -1
  237. package/dist/recall-explain-renderer.d.ts +1 -1
  238. package/dist/recall-explain-renderer.js +3 -3
  239. package/dist/recall-planner-llm.d.ts +1 -1
  240. package/dist/recall-planner-llm.js +2 -2
  241. package/dist/recall-state.d.ts +1 -1
  242. package/dist/recall-tag-filter.d.ts +1 -1
  243. package/dist/recall-timings.d.ts +1 -1
  244. package/dist/recall-xray-cli.d.ts +1 -1
  245. package/dist/recall-xray-cli.js +4 -4
  246. package/dist/recall-xray-renderer.d.ts +1 -1
  247. package/dist/recall-xray-renderer.js +3 -3
  248. package/dist/recall-xray.d.ts +1 -1
  249. package/dist/recall-xray.js +2 -2
  250. package/dist/replay/normalizers/chatgpt.d.ts +1 -1
  251. package/dist/replay/normalizers/claude.d.ts +1 -1
  252. package/dist/replay/normalizers/openclaw.d.ts +1 -1
  253. package/dist/replay/normalizers/shared.d.ts +1 -1
  254. package/dist/replay/runner.d.ts +1 -1
  255. package/dist/replay/types.d.ts +1 -1
  256. package/dist/resolve-auth-token.d.ts +1 -1
  257. package/dist/resume-bundles.js +2 -2
  258. package/dist/retrieval-agents.d.ts +2 -2
  259. package/dist/retrieval-agents.js +2 -2
  260. package/dist/retrieval-tiers.d.ts +1 -1
  261. package/dist/routing/engine.d.ts +1 -1
  262. package/dist/routing/store.d.ts +1 -1
  263. package/dist/schemas.d.ts +52 -52
  264. package/dist/search/embed-helper.d.ts +1 -1
  265. package/dist/search/factory.d.ts +1 -1
  266. package/dist/search/factory.js +6 -6
  267. package/dist/search/index.d.ts +1 -1
  268. package/dist/search/index.js +12 -12
  269. package/dist/search/lancedb-backend.d.ts +1 -1
  270. package/dist/search/meilisearch-backend.d.ts +1 -1
  271. package/dist/search/noop-backend.d.ts +1 -1
  272. package/dist/search/orama-backend.d.ts +1 -1
  273. package/dist/search/port.d.ts +1 -1
  274. package/dist/search/remote-backend.d.ts +1 -1
  275. package/dist/{semantic-consolidation-BFtoVosl.d.ts → semantic-consolidation-btIVRC4l.d.ts} +1 -1
  276. package/dist/semantic-consolidation.d.ts +2 -2
  277. package/dist/semantic-consolidation.js +16 -14
  278. package/dist/semantic-rule-promotion.js +15 -13
  279. package/dist/semantic-rule-verifier.d.ts +5 -1
  280. package/dist/semantic-rule-verifier.js +15 -13
  281. package/dist/session-observer-bands.d.ts +1 -1
  282. package/dist/session-observer-state.d.ts +1 -1
  283. package/dist/shared-context/manager.d.ts +9 -9
  284. package/dist/signal.d.ts +1 -1
  285. package/dist/{storage-CXjwBr69.d.ts → storage-BVv_Mqa0.d.ts} +133 -3
  286. package/dist/storage.d.ts +2 -2
  287. package/dist/storage.js +14 -12
  288. package/dist/summarizer.d.ts +1 -1
  289. package/dist/summarizer.js +3 -3
  290. package/dist/summary-snapshot.d.ts +1 -1
  291. package/dist/temporal-index-compat.d.ts +1 -0
  292. package/dist/temporal-index-compat.js +16 -0
  293. package/dist/temporal-index-compat.js.map +1 -0
  294. package/dist/temporal-index.d.ts +58 -7
  295. package/dist/temporal-index.js +23 -3
  296. package/dist/temporal-supersession.d.ts +2 -2
  297. package/dist/temporal-timeline-recall.d.ts +1 -1
  298. package/dist/temporal-validity.d.ts +1 -1
  299. package/dist/threading.d.ts +1 -1
  300. package/dist/tier-migration.d.ts +2 -2
  301. package/dist/tier-routing.d.ts +1 -1
  302. package/dist/topics.d.ts +1 -1
  303. package/dist/transcript.d.ts +1 -1
  304. package/dist/transfer/capsule-import.js +6 -2
  305. package/dist/transfer/import-sqlite.js +2 -2
  306. package/dist/transfer/types.d.ts +66 -66
  307. package/dist/trust-score-stage.d.ts +1 -1
  308. package/dist/trust-score.d.ts +1 -1
  309. package/dist/{types-DpxqC8dI.d.ts → types-Cmkz7csa.d.ts} +27 -0
  310. package/dist/types.d.ts +1 -1
  311. package/dist/types.js +1 -1
  312. package/dist/utility-runtime.d.ts +1 -1
  313. package/dist/verified-recall.d.ts +4 -0
  314. package/dist/verified-recall.js +15 -13
  315. package/package.json +12 -2
  316. package/src/binary-lifecycle/pipeline.ts +22 -0
  317. package/src/cli.ts +8 -0
  318. package/src/compounding/engine.ts +9 -1
  319. package/src/config.ts +14 -0
  320. package/src/consolidation-undo.ts +8 -0
  321. package/src/curation/index.ts +9 -0
  322. package/src/in-flight-reads.ts +63 -0
  323. package/src/maintenance/memory-governance.ts +6 -0
  324. package/src/memory-cache-hot.test.ts +131 -0
  325. package/src/memory-cache.ts +146 -22
  326. package/src/memory-corpus-version.ts +45 -0
  327. package/src/namespaces/storage.ts +20 -1
  328. package/src/operator-toolkit.ts +7 -1
  329. package/src/orchestration/consolidation-run.ts +5 -5
  330. package/src/orchestration/contradiction-linking-coordinator.ts +2 -2
  331. package/src/orchestration/lifecycle-policy-coordinator.ts +2 -2
  332. package/src/orchestration/persistence-index.ts +6 -6
  333. package/src/orchestration/recall-internal.ts +6 -3
  334. package/src/orchestration/recall-search-pipeline.ts +36 -21
  335. package/src/orchestration/semantic-consolidation-coordinator.ts +2 -2
  336. package/src/orchestrator.ts +25 -5
  337. package/src/page-versioning.ts +14 -1
  338. package/src/provenance-frontmatter.test.ts +6 -0
  339. package/src/review/index.ts +16 -8
  340. package/src/semantic-rule-promotion.ts +9 -1
  341. package/src/semantic-rule-verifier.ts +19 -6
  342. package/src/spaces/index.ts +10 -0
  343. package/src/storage/memory-read-store.ts +9 -2
  344. package/src/storage-contract/cache-coherence.test.ts +242 -0
  345. package/src/storage-contract/namespace-routing.test.ts +26 -1
  346. package/src/storage.ts +513 -31
  347. package/src/temporal-index-compat.ts +311 -0
  348. package/src/temporal-index.test.ts +273 -22
  349. package/src/temporal-index.ts +427 -178
  350. package/src/transfer/capsule-cache-coherence.test.ts +107 -0
  351. package/src/transfer/capsule-import.ts +8 -0
  352. package/src/transfer/capsule-merge.ts +7 -0
  353. package/src/types.ts +27 -0
  354. package/src/verified-recall.ts +20 -6
  355. package/dist/capsule-merge-GK5E647P.js.map +0 -1
  356. package/dist/chunk-74MJH7I3.js.map +0 -1
  357. package/dist/chunk-AD2KK5MT.js.map +0 -1
  358. package/dist/chunk-AWJ2FHCF.js.map +0 -1
  359. package/dist/chunk-EJCVMPIM.js.map +0 -1
  360. package/dist/chunk-GL3SMLMZ.js.map +0 -1
  361. package/dist/chunk-HHAA35A3.js.map +0 -1
  362. package/dist/chunk-JO5AT6QT.js.map +0 -1
  363. package/dist/chunk-KTOH7EJE.js.map +0 -1
  364. package/dist/chunk-M5QKGHCR.js.map +0 -1
  365. package/dist/chunk-Q6JPMCPO.js.map +0 -1
  366. package/dist/chunk-R2SJDIPO.js.map +0 -1
  367. package/dist/chunk-TVRPRFUB.js.map +0 -1
  368. package/dist/chunk-UVIQHSS7.js.map +0 -1
  369. package/dist/chunk-VF4XKTX3.js.map +0 -1
  370. package/dist/chunk-ZUFJ7ZTR.js.map +0 -1
  371. /package/dist/{auto-sync-676N6N6Y.js.map → auto-sync-AAMTVZMK.js.map} +0 -0
  372. /package/dist/{chunk-CNWC274T.js.map → chunk-3OQ4IZJN.js.map} +0 -0
  373. /package/dist/{chunk-7XH7VJN4.js.map → chunk-5S7Z5SHA.js.map} +0 -0
  374. /package/dist/{chunk-WKE4VXSZ.js.map → chunk-673NPXE3.js.map} +0 -0
  375. /package/dist/{chunk-UHGHTOR5.js.map → chunk-7EX4YOKH.js.map} +0 -0
  376. /package/dist/{chunk-TVVEYCNW.js.map → chunk-7K5Q6COX.js.map} +0 -0
  377. /package/dist/{chunk-3F4N4WDH.js.map → chunk-AHLPZ2SF.js.map} +0 -0
  378. /package/dist/{chunk-6C35R7P4.js.map → chunk-DJCKXI4M.js.map} +0 -0
  379. /package/dist/{chunk-FYKIEOG6.js.map → chunk-FNJX2O6G.js.map} +0 -0
  380. /package/dist/{chunk-DQPPRXFO.js.map → chunk-HQ7QZJM5.js.map} +0 -0
  381. /package/dist/{chunk-RKNJBZ55.js.map → chunk-JBPKEARU.js.map} +0 -0
  382. /package/dist/{chunk-KIPLG7S7.js.map → chunk-JKLJ6A7W.js.map} +0 -0
  383. /package/dist/{chunk-O3BZ6PFY.js.map → chunk-JZM5QUTE.js.map} +0 -0
  384. /package/dist/{chunk-SYNYI4ZM.js.map → chunk-L5HJQC36.js.map} +0 -0
  385. /package/dist/{chunk-VXMWDXIJ.js.map → chunk-LC7OODWZ.js.map} +0 -0
  386. /package/dist/{chunk-ZW6OKHVX.js.map → chunk-LQKITFGB.js.map} +0 -0
  387. /package/dist/{chunk-TZJVTH3F.js.map → chunk-MXRMNKSY.js.map} +0 -0
  388. /package/dist/{chunk-PKGWJFNR.js.map → chunk-OVMS3DCB.js.map} +0 -0
  389. /package/dist/{chunk-LRGSLV7M.js.map → chunk-PYHOAXTI.js.map} +0 -0
  390. /package/dist/{chunk-NMMKRVUF.js.map → chunk-QVALQE4P.js.map} +0 -0
  391. /package/dist/{chunk-FXGC5UUH.js.map → chunk-RT7IP3D3.js.map} +0 -0
  392. /package/dist/{chunk-AH3M6F4X.js.map → chunk-SBFDL2EQ.js.map} +0 -0
  393. /package/dist/{chunk-UYZUJ4EB.js.map → chunk-SECMZACR.js.map} +0 -0
  394. /package/dist/{chunk-HXDKC3BI.js.map → chunk-TGBJ2OMK.js.map} +0 -0
  395. /package/dist/{chunk-DE6ENYLG.js.map → chunk-TXLGHDHS.js.map} +0 -0
  396. /package/dist/{chunk-CRFTLUWT.js.map → chunk-UTDA43TB.js.map} +0 -0
  397. /package/dist/{chunk-GKCCHHZK.js.map → chunk-UXKMMNJD.js.map} +0 -0
  398. /package/dist/{chunk-57INMZ6F.js.map → chunk-UZRIKUFS.js.map} +0 -0
  399. /package/dist/{chunk-2FSHROYK.js.map → chunk-V53GJUSL.js.map} +0 -0
  400. /package/dist/{chunk-LKT755LP.js.map → chunk-VA4WSW54.js.map} +0 -0
  401. /package/dist/{chunk-ZHKD4EZM.js.map → chunk-VCEH5UYS.js.map} +0 -0
  402. /package/dist/{chunk-CHVU4RE5.js.map → chunk-VDLMAJXS.js.map} +0 -0
  403. /package/dist/{chunk-XFTPUXCE.js.map → chunk-VRAD2FGO.js.map} +0 -0
  404. /package/dist/{chunk-T7VNN3ZM.js.map → chunk-XVLEGMJI.js.map} +0 -0
  405. /package/dist/{chunk-QDCJNRKK.js.map → chunk-YXOAQAH2.js.map} +0 -0
  406. /package/dist/{forget-KHF7A44H.js.map → forget-CQCLB5LB.js.map} +0 -0
@@ -25,7 +25,8 @@ import {
25
25
  writeFile,
26
26
  unlink,
27
27
  } from "node:fs/promises";
28
- import { ALL_CATEGORY_DIRS } from "./utils/category-dir.js";
28
+ import { ALL_CATEGORY_DIRS, RECALL_FALLBACK_DIRS } from "./utils/category-dir.js";
29
+ import { bumpMemoryCorpusVersionForDir } from "./memory-corpus-version.js";
29
30
 
30
31
  // ---------------------------------------------------------------------------
31
32
  // Public interfaces
@@ -332,6 +333,18 @@ export async function revertToVersion(
332
333
 
333
334
  // Write the reverted content to the actual page
334
335
  await writeFile(pagePath, targetContent, "utf-8");
336
+ // A revert overwrites a memory file out-of-band (not via a StorageManager
337
+ // mutation), so bump the corpus sentinel when the reverted page lives under a
338
+ // recall category dir — forcing a warm hot-memories cache to rescan on its
339
+ // next read (issue #1902, Codex P1). Non-recall pages (entities/artifacts/
340
+ // profiles) are not in the corpus scan, so they need no bump.
341
+ // The hot-memories corpus covers the RECALL corpus, which excludes the
342
+ // non-memory queue dirs (questions/). Use RECALL_FALLBACK_DIRS — a revert of a
343
+ // questions/ page must NOT bump the recall corpus sentinel and force a rescan.
344
+ const revertedTop = relPath(pagePath, resolvedMemoryDir).split(path.sep)[0];
345
+ if ((RECALL_FALLBACK_DIRS as readonly string[]).includes(revertedTop)) {
346
+ bumpMemoryCorpusVersionForDir(resolvedMemoryDir);
347
+ }
335
348
  log.debug(`page-versioning: reverted ${pagePath} to version ${versionId}`);
336
349
 
337
350
  return version;
@@ -5,6 +5,7 @@ import path from "node:path";
5
5
  import { mkdtemp, mkdir, rm, writeFile, readFile } from "node:fs/promises";
6
6
 
7
7
  import { StorageManager } from "./storage.js";
8
+ import { clearMemoryCache } from "./memory-cache.js";
8
9
  import type { ProvenanceSource } from "./types.js";
9
10
 
10
11
  /**
@@ -64,6 +65,11 @@ async function writeFactFile(
64
65
  const factsDir = path.join((storage as unknown as { baseDir: string }).baseDir, "facts", today);
65
66
  await mkdir(factsDir, { recursive: true });
66
67
  await writeFile(path.join(factsDir, `${id}.md`), `${lines.join("\n")}\n\n${body}\n`, "utf-8");
68
+ // This is an out-of-band write (bypasses StorageManager.writeMemory), so it
69
+ // must invalidate the version-keyed hot-memories cache (issue #1902) — drop
70
+ // every cache layer for this dir via the exported chokepoint so the next
71
+ // readAllMemories() rescans disk and sees this file (matches the harness seam).
72
+ clearMemoryCache(storage.dir);
67
73
  return id;
68
74
  }
69
75
 
@@ -8,6 +8,7 @@
8
8
  import fs from "node:fs";
9
9
  import path from "node:path";
10
10
  import { getCategoryDir, ALL_CATEGORY_DIRS } from "../utils/category-dir.js";
11
+ import { bumpMemoryCorpusVersionForDir } from "../memory-corpus-version.js";
11
12
 
12
13
  // ── Types ────────────────────────────────────────────────────────────────────
13
14
 
@@ -259,14 +260,21 @@ export function performReview(
259
260
  action: ReviewAction,
260
261
  options: ReviewActionOptions = {},
261
262
  ): ReviewResult {
262
- switch (action) {
263
- case "approve":
264
- return approveItem(memoryDir, itemId, options);
265
- case "dismiss":
266
- return dismissItem(memoryDir, itemId, options);
267
- case "flag":
268
- return flagItem(memoryDir, itemId, options);
269
- }
263
+ const result =
264
+ action === "approve"
265
+ ? approveItem(memoryDir, itemId, options)
266
+ : action === "dismiss"
267
+ ? dismissItem(memoryDir, itemId, options)
268
+ : flagItem(memoryDir, itemId, options);
269
+ // Review approval/dismissal/flagging edits, moves, or removes memory files
270
+ // directly (out-of-band — it does not go through a StorageManager mutation
271
+ // method), so bump the corpus sentinel to force the version-keyed hot cache
272
+ // to rescan on its next read (issue #1902). Only bump when a file was
273
+ // actually modified/moved (updatedPath set) — no-op early returns (item not
274
+ // found, unparsable frontmatter) must not advance the sentinel or needlessly
275
+ // clear caches (Cursor Medium).
276
+ if (result.updatedPath !== undefined) bumpMemoryCorpusVersionForDir(memoryDir);
277
+ return result;
270
278
  }
271
279
 
272
280
  // ── Actions ──────────────────────────────────────────────────────────────────
@@ -498,7 +498,15 @@ function normalizeRuleWhitespace(value: string): string {
498
498
  }
499
499
 
500
500
  function stripTrailingClausePunctuation(value: string): string {
501
- return value.replace(/[,:;]+$/g, "").trim();
501
+ // Linear trailing-punctuation strip (no `/[,:;]+$/` — CodeQL flags that as a
502
+ // polynomial-backtracking ReDoS on long runs of ',;:'). Walk back once, O(n).
503
+ let end = value.length;
504
+ while (end > 0) {
505
+ const ch = value[end - 1];
506
+ if (ch === "," || ch === ":" || ch === ";") end--;
507
+ else break;
508
+ }
509
+ return value.slice(0, end).trim();
502
510
  }
503
511
 
504
512
  function canonicalizeRuleContent(value: string): string {
@@ -116,18 +116,31 @@ export async function searchVerifiedSemanticRules(options: {
116
116
  query: string;
117
117
  maxResults: number;
118
118
  minEffectiveConfidence?: number;
119
+ /** Hot-memories cache gate (issue #1902, Codex P2). Threaded from the caller
120
+ * so a named-namespace root (never registered in the per-dir default map)
121
+ * honors the owning daemon's config instead of the process-wide default. */
122
+ hotMemoriesCacheEnabled?: boolean;
119
123
  }): Promise<VerifiedSemanticRuleResult[]> {
120
124
  const queryTokens = new Set(normalizeRecallTokens(options.query, ["what", "which"]));
121
125
  if (queryTokens.size === 0 || options.maxResults <= 0) return [];
122
126
 
123
- const storage = new StorageManager(options.memoryDir);
124
- const version = storage.getMemoryStatusVersion();
125
-
126
- // Use derived rule cache to avoid O(146K) iteration on every call.
127
- let cachedRules = getCachedRuleMemories(storage.dir, version);
127
+ const storage = new StorageManager(options.memoryDir, undefined, options.hotMemoriesCacheEnabled);
128
+ // Key the derived rule cache on the CORPUS version, not memory-status (Codex
129
+ // P1, #1902): rule memories are derived from the full corpus, and plain
130
+ // creates/writeChunk bump only the corpus sentinel. Keying on memory-status
131
+ // would let a peer process serve a stale rule set that omits newly created
132
+ // rule memories.
133
+ const version = storage.getMemoryCorpusVersion();
134
+
135
+ // Use derived rule cache to avoid O(146K) iteration on every call — but honor
136
+ // the hotMemoriesCacheEnabled opt-out (Codex P2): when disabled, never read or
137
+ // retain the derived cache, always rebuild from a fresh scan.
138
+ const cacheEnabled = storage.isHotCacheEnabled();
139
+ const keyId = storage.hotCacheKeyId();
140
+ let cachedRules = cacheEnabled ? getCachedRuleMemories(storage.dir, version, keyId, storage.hotCacheTtlMs()) : null;
128
141
  if (!cachedRules) {
129
142
  const allMems = await storage.readAllMemories();
130
- cachedRules = setCachedRuleMemories(storage.dir, allMems, version);
143
+ cachedRules = setCachedRuleMemories(storage.dir, allMems, version, cacheEnabled, keyId);
131
144
  }
132
145
  const { all: ruleMemories, byId: memoryById } = cachedRules;
133
146
  const minEffectiveConfidence = options.minEffectiveConfidence ?? DEFAULT_MIN_EFFECTIVE_CONFIDENCE;
@@ -13,6 +13,7 @@ import crypto from "node:crypto";
13
13
  import fs from "node:fs";
14
14
  import path from "node:path";
15
15
  import { readEnvVar, resolveHomeDir } from "../runtime/env.js";
16
+ import { bumpMemoryCorpusVersionForDir } from "../memory-corpus-version.js";
16
17
 
17
18
  // ── Types ────────────────────────────────────────────────────────────────────
18
19
 
@@ -935,6 +936,15 @@ function copyMemories(
935
936
  }
936
937
  fs.writeFileSync(targetPath, content);
937
938
  merged++;
939
+ // Bump the target's corpus sentinel per write (out-of-band — not through a
940
+ // StorageManager mutation), on the CANONICAL root (targetRoot), matching
941
+ // where files were written and where a StorageManager resolves its
942
+ // version/cache keys. Per-write (not once after the loop) so a warm
943
+ // hot-memories cache for the target rescans MID-batch and never serves the
944
+ // pre-copy corpus while copies are still landing (#1902; a symlinked target
945
+ // would otherwise diverge, and a once-after bump leaves in-batch reads
946
+ // stale).
947
+ bumpMemoryCorpusVersionForDir(targetRoot);
938
948
  }
939
949
 
940
950
  return { merged, conflicts, skipped };
@@ -60,6 +60,8 @@ export interface MemoryReadStoreDeps {
60
60
  readonly proceduresDir: string;
61
61
  readonly questionsDir: string;
62
62
  readColdWriteVersion(): number;
63
+ /** Secure-store key identity for keying decrypted-corpus caches (#1902). */
64
+ hotCacheKeyId(): string;
63
65
  readMemoryByPath(filePath: string): Promise<MemoryFile | null>;
64
66
  readParsedMemoriesFromPaths(
65
67
  filePaths: string[],
@@ -365,10 +367,15 @@ export class MemoryReadStore {
365
367
  // stale data for up to 30s when another process wrote a new cold memory.
366
368
  const currentColdVersion = this.deps.readColdWriteVersion();
367
369
 
368
- // Return cached result if still valid by both TTL and sentinel version.
370
+ // Return cached result if still valid by TTL, sentinel version, AND the
371
+ // secure-store key identity (issue #1902). readParsedMemoriesFromPaths
372
+ // decrypts per this manager's key, so a differently-keyed manager must not
373
+ // read a cold corpus another instance decrypted for the same coldRoot.
374
+ const keyId = this.deps.hotCacheKeyId();
369
375
  const cached = this.deps.storageManagerClass.coldMemoriesCache.get(coldRoot);
370
376
  if (
371
377
  cached &&
378
+ cached.keyId === keyId &&
372
379
  Date.now() - cached.loadedAt < this.deps.storageManagerClass.COLD_SCAN_CACHE_TTL_MS &&
373
380
  cached.coldVersion === currentColdVersion
374
381
  ) {
@@ -407,7 +414,7 @@ export class MemoryReadStore {
407
414
 
408
415
  // Store in cache with the sentinel version captured above so that any
409
416
  // subsequent cold-version bump (by this or another process) invalidates it.
410
- this.deps.storageManagerClass.coldMemoriesCache.set(coldRoot, { memories, loadedAt: Date.now(), coldVersion: currentColdVersion });
417
+ this.deps.storageManagerClass.coldMemoriesCache.set(coldRoot, { memories, loadedAt: Date.now(), coldVersion: currentColdVersion, keyId });
411
418
  return memories;
412
419
  }
413
420
 
@@ -10,6 +10,9 @@
10
10
 
11
11
  import assert from "node:assert/strict";
12
12
  import test from "node:test";
13
+ import { appendFile, mkdtemp, rm, mkdir, writeFile } from "node:fs/promises";
14
+ import os from "node:os";
15
+ import path from "node:path";
13
16
 
14
17
  import { StorageManager } from "../storage.js";
15
18
  import { resetStaticCaches } from "./harness.js";
@@ -98,3 +101,242 @@ test("cache-coherence: readAllMemories reflects writes across instances after in
98
101
  await rm(dir, { recursive: true, force: true });
99
102
  }
100
103
  });
104
+
105
+ type ScanSpy = { _readAllMemoriesFromDisk: () => Promise<unknown> };
106
+
107
+ function installScanSpy(sm: StorageManager): { scans: () => number } {
108
+ const spy = sm as unknown as ScanSpy;
109
+ const orig = spy._readAllMemoriesFromDisk.bind(sm);
110
+ let count = 0;
111
+ spy._readAllMemoriesFromDisk = async () => {
112
+ count += 1;
113
+ return orig();
114
+ };
115
+ return { scans: () => count };
116
+ }
117
+
118
+ test("hot cache (#1902): a warm readAllMemories() serves without re-scanning disk", async () => {
119
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-hot-warm-"));
120
+ try {
121
+ resetStaticCaches();
122
+ const sm = new StorageManager(dir);
123
+ await sm.ensureDirectories();
124
+ await sm.writeMemory("fact", "A");
125
+ await sm.readAllMemories(); // warming scan populates the hot cache
126
+ const spy = installScanSpy(sm);
127
+ const warm = await sm.readAllMemories();
128
+ assert.equal(spy.scans(), 0, "a warm read must not touch disk");
129
+ assert.ok(warm.some((m) => m.content === "A"));
130
+ } finally {
131
+ resetStaticCaches();
132
+ await rm(dir, { recursive: true, force: true });
133
+ }
134
+ });
135
+
136
+ test("hot cache (#1902): a single-file write patches the cache in place — next read stays warm", async () => {
137
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-hot-patch-"));
138
+ try {
139
+ resetStaticCaches();
140
+ const sm = new StorageManager(dir);
141
+ await sm.ensureDirectories();
142
+ await sm.writeMemory("fact", "A");
143
+ await sm.readAllMemories(); // warm
144
+ const spy = installScanSpy(sm);
145
+ await sm.writeMemory("fact", "B"); // patch-on-write keeps the entry warm
146
+ const after = await sm.readAllMemories();
147
+ assert.equal(spy.scans(), 0, "patch-on-write must keep the cache warm (no rescan)");
148
+ assert.ok(after.some((m) => m.content === "B"), "the newly written fact is present");
149
+ assert.ok(after.some((m) => m.content === "A"), "the prior fact survives the patch");
150
+ } finally {
151
+ resetStaticCaches();
152
+ await rm(dir, { recursive: true, force: true });
153
+ }
154
+ });
155
+
156
+ test("hot cache (#1902): a peer corpus-version bump forces exactly one rescan (cross-process coherence)", async () => {
157
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-hot-peer-"));
158
+ try {
159
+ resetStaticCaches();
160
+ const sm = new StorageManager(dir);
161
+ await sm.ensureDirectories();
162
+ await sm.writeMemory("fact", "A");
163
+ await sm.readAllMemories(); // warm
164
+ const spy = installScanSpy(sm);
165
+ // Simulate a peer process advancing the on-disk corpus sentinel (its byte
166
+ // size IS the version). This instance's warm entry is now stale-versioned.
167
+ await appendFile(path.join(dir, "state", ".memory-corpus-version.log"), "x");
168
+ const afterPeer = await sm.readAllMemories();
169
+ assert.equal(spy.scans(), 1, "a peer sentinel bump must force exactly one rescan");
170
+ assert.ok(afterPeer.some((m) => m.content === "A"));
171
+ } finally {
172
+ resetStaticCaches();
173
+ await rm(dir, { recursive: true, force: true });
174
+ }
175
+ });
176
+
177
+ test("hot cache (#1902): a delete rescans and drops the removed memory", async () => {
178
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-hot-del-"));
179
+ try {
180
+ resetStaticCaches();
181
+ const sm = new StorageManager(dir);
182
+ await sm.ensureDirectories();
183
+ const { id } = await sm.writeMemory("fact", "A");
184
+ await sm.writeMemory("fact", "B");
185
+ await sm.readAllMemories(); // warm
186
+ await sm.invalidateMemory(id); // delete drops the hot layer wholesale
187
+ const after = await sm.readAllMemories();
188
+ assert.ok(after.some((m) => m.content === "B"), "surviving memory remains");
189
+ assert.ok(!after.some((m) => m.content === "A"), "the deleted memory is gone");
190
+ } finally {
191
+ resetStaticCaches();
192
+ await rm(dir, { recursive: true, force: true });
193
+ }
194
+ });
195
+
196
+ test("hot cache (#1902/#1904 compat): a plain fact create does NOT bump memory-status (entity cache stays warm)", async () => {
197
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-hot-status-"));
198
+ try {
199
+ resetStaticCaches();
200
+ const sm = new StorageManager(dir);
201
+ await sm.ensureDirectories();
202
+ await sm.writeMemory("fact", "seed"); // establish the state dir + baseline
203
+ const before = sm.getMemoryStatusVersion();
204
+ await sm.writeMemory("fact", "another create");
205
+ assert.equal(
206
+ sm.getMemoryStatusVersion(),
207
+ before,
208
+ "a plain fact create must not bump memory-status (the entity cache keys on it)",
209
+ );
210
+ } finally {
211
+ resetStaticCaches();
212
+ await rm(dir, { recursive: true, force: true });
213
+ }
214
+ });
215
+
216
+ test("hot cache (#1902): hotMemoriesCacheEnabled=false forces every read to rescan", async () => {
217
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-hot-off-"));
218
+ try {
219
+ resetStaticCaches();
220
+ const sm = new StorageManager(dir, undefined, false); // gate off
221
+ await sm.ensureDirectories();
222
+ await sm.writeMemory("fact", "A");
223
+ const spy = installScanSpy(sm);
224
+ await sm.readAllMemories();
225
+ await sm.readAllMemories();
226
+ assert.equal(spy.scans(), 2, "with the cache disabled, each sequential read scans disk");
227
+ } finally {
228
+ resetStaticCaches();
229
+ await rm(dir, { recursive: true, force: true });
230
+ }
231
+ });
232
+
233
+ test("hot cache (#1902): the gate default is isolated per memory dir (Codex P2)", async () => {
234
+ const dirOff = await mkdtemp(path.join(os.tmpdir(), "remnic-hot-perdir-off-"));
235
+ const dirOn = await mkdtemp(path.join(os.tmpdir(), "remnic-hot-perdir-on-"));
236
+ try {
237
+ resetStaticCaches();
238
+ // Register divergent per-dir settings; a later registration must NOT flip
239
+ // an earlier dir's behavior (the "last orchestrator wins" bug).
240
+ StorageManager.setHotMemoriesCacheDefault(dirOff, false);
241
+ StorageManager.setHotMemoriesCacheDefault(dirOn, true);
242
+ // Constructed WITHOUT a per-instance override, so each resolves its own
243
+ // dir's registered default.
244
+ const smOff = new StorageManager(dirOff);
245
+ const smOn = new StorageManager(dirOn);
246
+ await smOff.ensureDirectories();
247
+ await smOff.writeMemory("fact", "A");
248
+ await smOn.ensureDirectories();
249
+ await smOn.writeMemory("fact", "B");
250
+ const spyOff = installScanSpy(smOff);
251
+ const spyOn = installScanSpy(smOn);
252
+ await smOff.readAllMemories();
253
+ await smOff.readAllMemories();
254
+ await smOn.readAllMemories();
255
+ await smOn.readAllMemories();
256
+ assert.equal(spyOff.scans(), 2, "dir registered false rescans on every read");
257
+ assert.equal(spyOn.scans(), 1, "dir registered true serves the second read from cache");
258
+ } finally {
259
+ resetStaticCaches();
260
+ await rm(dirOff, { recursive: true, force: true });
261
+ await rm(dirOn, { recursive: true, force: true });
262
+ }
263
+ });
264
+
265
+ test("hot cache (#1902): flushAccessTracking patches locally + bumps for cross-process coherence", async () => {
266
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-hot-access-"));
267
+ try {
268
+ resetStaticCaches();
269
+ const sm = new StorageManager(dir);
270
+ await sm.ensureDirectories();
271
+ const { id } = await sm.writeMemory("fact", "trackable");
272
+ // Warm the cache.
273
+ const warm = await sm.readAllMemories();
274
+ assert.equal(warm.length, 1);
275
+ const versionBefore = sm.getMemoryCorpusVersion();
276
+ const spy = installScanSpy(sm);
277
+ await sm.flushAccessTracking([
278
+ { memoryId: id, newCount: 7, lastAccessed: "2026-07-16T00:00:00.000Z" },
279
+ ]);
280
+ const after = await sm.readAllMemories();
281
+ // Local process: patched in place + re-keyed, so no rescan and fresh counts.
282
+ assert.equal(spy.scans(), 0, "access-tracking flush must not rescan the corpus locally");
283
+ const m = after.find((x) => x.frontmatter.id === id);
284
+ assert.equal(m?.frontmatter.accessCount, 7, "hot cache reflects the flushed access count");
285
+ assert.equal(m?.frontmatter.lastAccessed, "2026-07-16T00:00:00.000Z");
286
+ // Cross-process: the shared sentinel advanced so peer processes rescan and
287
+ // never overwrite this process's increment (Codex P2).
288
+ assert.ok(
289
+ sm.getMemoryCorpusVersion() > versionBefore,
290
+ "access-tracking flush advances the corpus sentinel for peer coherence",
291
+ );
292
+ } finally {
293
+ resetStaticCaches();
294
+ await rm(dir, { recursive: true, force: true });
295
+ }
296
+ });
297
+
298
+ test("hot cache (#1902): ensureDirectories activates a version-0 corpus so reads cache (Cursor Medium)", async () => {
299
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-hot-zero-"));
300
+ try {
301
+ resetStaticCaches();
302
+ // Simulate an existing corpus with NO sentinel yet: write a fact file
303
+ // directly to disk (bypassing StorageManager, so nothing bumps the sentinel).
304
+ await mkdir(path.join(dir, "facts"), { recursive: true });
305
+ await writeFile(path.join(dir, "facts", "a.md"), "---\nid: fact-a\n---\nA");
306
+ const sm = new StorageManager(dir);
307
+ assert.equal(sm.getMemoryCorpusVersion(), 0, "precondition: sentinel starts at 0");
308
+ // The daemon calls ensureDirectories() at startup (before serving reads); it
309
+ // seeds a nonzero sentinel so the hot path engages on a read-heavy workload.
310
+ await sm.ensureDirectories();
311
+ assert.ok(sm.getMemoryCorpusVersion() > 0, "ensureDirectories activates the sentinel");
312
+ const spy = installScanSpy(sm);
313
+ const first = await sm.readAllMemories();
314
+ assert.equal(first.length, 1);
315
+ await sm.readAllMemories();
316
+ assert.equal(spy.scans(), 1, "second read is served from cache — activation worked");
317
+ } finally {
318
+ resetStaticCaches();
319
+ await rm(dir, { recursive: true, force: true });
320
+ }
321
+ });
322
+
323
+ test("hot cache (#1902): an empty corpus is served from cache, not rescanned (kilo null-check)", async () => {
324
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-hot-empty-"));
325
+ try {
326
+ resetStaticCaches();
327
+ const sm = new StorageManager(dir);
328
+ // ensureDirectories activates the sentinel (version > 0); the corpus is empty.
329
+ await sm.ensureDirectories();
330
+ const first = await sm.readAllMemories();
331
+ assert.equal(first.length, 0, "corpus is empty");
332
+ const spy = installScanSpy(sm);
333
+ await sm.readAllMemories();
334
+ await sm.readAllMemories();
335
+ // getCachedMemories returns [] (falsy) for an empty corpus; the read guard
336
+ // must null-check, not truthiness-check, or every read rescans.
337
+ assert.equal(spy.scans(), 0, "empty corpus is served from cache on repeat reads");
338
+ } finally {
339
+ resetStaticCaches();
340
+ await rm(dir, { recursive: true, force: true });
341
+ }
342
+ });
@@ -18,9 +18,10 @@ import { mkdir, writeFile } from "node:fs/promises";
18
18
  import path from "node:path";
19
19
  import test from "node:test";
20
20
 
21
- import { makeStorage, makeNamespaceRouter } from "./harness.js";
21
+ import { makeStorage, makeNamespaceRouter, resetStaticCaches } from "./harness.js";
22
22
  import { resolveNamespaceStorageRoot } from "../namespaces/storage.js";
23
23
  import { namespaceIdentityToken } from "../namespaces/identity.js";
24
+ import { getCachedMemories } from "../memory-cache.js";
24
25
 
25
26
  test("namespace-routing: default namespace resolves to the legacy memoryDir root", async () => {
26
27
  const { router, memoryDir, config, cleanup } = await makeNamespaceRouter();
@@ -133,3 +134,27 @@ test("namespace-routing: router caches StorageManager per namespace (same instan
133
134
  await cleanup();
134
135
  }
135
136
  });
137
+
138
+ test("namespace-routing: hotMemoriesCacheEnabled=false propagates to namespace child storages (#1902 Codex P2)", async () => {
139
+ const { router, config, cleanup } = await makeNamespaceRouter({ hotMemoriesCacheEnabled: false });
140
+ try {
141
+ resetStaticCaches();
142
+ const sm = await router.storageFor("team-alpha");
143
+ await sm.ensureDirectories();
144
+ await sm.writeMemory("fact", "A");
145
+ await sm.readAllMemories();
146
+ await sm.readAllMemories();
147
+ const root = await resolveNamespaceStorageRoot(config, "team-alpha");
148
+ // Gate propagated to the child: with caching disabled the child storage
149
+ // never populates the module-level hot cache, so getCachedMemories stays
150
+ // null even after reads. Before the fix the child fell back to the
151
+ // process-wide default (true) and would have cached the corpus.
152
+ assert.equal(
153
+ getCachedMemories(root, sm.getMemoryCorpusVersion()),
154
+ null,
155
+ "namespace child honors the disabled gate and never caches the corpus",
156
+ );
157
+ } finally {
158
+ await cleanup();
159
+ }
160
+ });