@remnic/core 9.29.1 → 9.30.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (358) hide show
  1. package/dist/access-admin-ops-surface.d.ts +11 -11
  2. package/dist/access-admin-ops-surface.js +19 -18
  3. package/dist/access-authorization-probe.d.ts +11 -11
  4. package/dist/access-authorization-probe.js +20 -19
  5. package/dist/access-boundary.d.ts +11 -11
  6. package/dist/access-boundary.js +19 -18
  7. package/dist/access-citation-namespace.js +2 -2
  8. package/dist/access-cli.js +45 -44
  9. package/dist/access-cli.js.map +1 -1
  10. package/dist/access-health-types.d.ts +1 -1
  11. package/dist/access-http-lcm-compaction.d.ts +11 -11
  12. package/dist/access-http.d.ts +11 -11
  13. package/dist/access-http.js +25 -24
  14. package/dist/access-identity-continuity-surface.d.ts +8 -8
  15. package/dist/access-identity-continuity-surface.js +19 -18
  16. package/dist/access-lcm-surface.d.ts +10 -10
  17. package/dist/access-lcm-surface.js +19 -18
  18. package/dist/access-mcp.d.ts +11 -11
  19. package/dist/access-mcp.js +23 -22
  20. package/dist/access-memory-search-fanout.d.ts +2 -2
  21. package/dist/{access-namespace-preflight-DCXBkDp0.d.ts → access-namespace-preflight-VfXssvoI.d.ts} +1 -1
  22. package/dist/access-namespace-preflight.d.ts +3 -3
  23. package/dist/access-namespace-preflight.js +19 -18
  24. package/dist/access-observe-write-surface.d.ts +10 -10
  25. package/dist/access-observe-write-surface.js +19 -18
  26. package/dist/access-operations-batch.js +20 -19
  27. package/dist/access-operations.d.ts +11 -11
  28. package/dist/access-operations.js +22 -21
  29. package/dist/access-recall-concurrency.d.ts +11 -11
  30. package/dist/access-recall-concurrency.js +19 -18
  31. package/dist/access-recall-response.d.ts +10 -10
  32. package/dist/access-recall-response.js +19 -18
  33. package/dist/access-recall-surface.d.ts +10 -10
  34. package/dist/access-recall-surface.js +19 -18
  35. package/dist/access-schema.js +3 -3
  36. package/dist/{access-service-DvPW_M5j.d.ts → access-service-B5RwVsTV.d.ts} +7 -7
  37. package/dist/access-service.d.ts +11 -11
  38. package/dist/access-service.js +19 -18
  39. package/dist/access-surface-catalog.d.ts +11 -11
  40. package/dist/access-wearables-meetings-surface.d.ts +4 -3
  41. package/dist/action-confidence.d.ts +1 -1
  42. package/dist/active-memory-bridge.d.ts +1 -1
  43. package/dist/active-recall.d.ts +1 -1
  44. package/dist/active-recall.js +3 -3
  45. package/dist/behavior-learner.d.ts +1 -1
  46. package/dist/behavior-signals.d.ts +1 -1
  47. package/dist/bootstrap.d.ts +8 -8
  48. package/dist/briefing.d.ts +3 -2
  49. package/dist/briefing.js +10 -9
  50. package/dist/buffer-surprise-report.d.ts +1 -1
  51. package/dist/buffer.d.ts +3 -2
  52. package/dist/bulk-import/index.d.ts +3 -3
  53. package/dist/calibration.d.ts +1 -1
  54. package/dist/capabilities.d.ts +1 -1
  55. package/dist/{catalog-BIVgftWu.d.ts → catalog-V7inUQdp.d.ts} +1 -1
  56. package/dist/causal-behavior.d.ts +1 -1
  57. package/dist/causal-consolidation.d.ts +1 -1
  58. package/dist/causal-consolidation.js +11 -10
  59. package/dist/causal-consolidation.js.map +1 -1
  60. package/dist/causal-trajectory-graph.d.ts +1 -1
  61. package/dist/{chunk-FZRQZW4H.js → chunk-2EUVEDF5.js} +2 -2
  62. package/dist/{chunk-PBUZNGMX.js → chunk-4WKA6I65.js} +20 -20
  63. package/dist/{chunk-PBUZNGMX.js.map → chunk-4WKA6I65.js.map} +1 -1
  64. package/dist/{chunk-AIK5W3IW.js → chunk-5TFP4CUL.js} +68 -2
  65. package/dist/chunk-5TFP4CUL.js.map +1 -0
  66. package/dist/{chunk-3JNDOAQ2.js → chunk-5W4ZLBEZ.js} +9 -9
  67. package/dist/{chunk-5ZCUDWD4.js → chunk-62U4J2GN.js} +2 -2
  68. package/dist/{chunk-IGPEJVBY.js → chunk-66772WBP.js} +2 -2
  69. package/dist/{chunk-EP5BVCRI.js → chunk-6BNE62PM.js} +2 -2
  70. package/dist/{chunk-AAP5T6QG.js → chunk-6DNMUYEE.js} +2 -2
  71. package/dist/{chunk-35DL7IOI.js → chunk-6FUPCJUL.js} +7 -7
  72. package/dist/{chunk-JP3YPDAW.js → chunk-6U7F52DE.js} +19 -19
  73. package/dist/{chunk-E4OCKU67.js → chunk-7XC2HX75.js} +2 -2
  74. package/dist/{chunk-OVRQLAYR.js → chunk-AZ5BTEKX.js} +2 -2
  75. package/dist/{chunk-2AXMQWCN.js → chunk-CZRYLSSW.js} +3 -3
  76. package/dist/chunk-FBUE6PD3.js +1769 -0
  77. package/dist/chunk-FBUE6PD3.js.map +1 -0
  78. package/dist/{chunk-PTSNLEXK.js → chunk-FDAMS4PK.js} +2 -2
  79. package/dist/{chunk-WL5EBRDH.js → chunk-FFAZ2XFU.js} +9 -8
  80. package/dist/chunk-FFAZ2XFU.js.map +1 -0
  81. package/dist/{chunk-2GXM5N5A.js → chunk-GIBMDSJ2.js} +2 -2
  82. package/dist/{chunk-E5OBR6CJ.js → chunk-H5CPS5JP.js} +3 -3
  83. package/dist/{chunk-2D7FEUCK.js → chunk-I5NLEENJ.js} +2 -2
  84. package/dist/{chunk-DRBXHRXE.js → chunk-IB26HVAJ.js} +2 -2
  85. package/dist/{chunk-QDKK63Y7.js → chunk-IC6CBHCA.js} +3 -3
  86. package/dist/{chunk-IAEAFLF2.js → chunk-JCBWCYEJ.js} +2 -2
  87. package/dist/{chunk-3PDOVUHC.js → chunk-KXB4VXHY.js} +2 -2
  88. package/dist/{chunk-WU7TTVOD.js → chunk-LBOD4U5B.js} +7 -7
  89. package/dist/{chunk-UBKEQOTD.js → chunk-LGHKJABJ.js} +3 -3
  90. package/dist/{chunk-VSURVXKY.js → chunk-M3C2V3PV.js} +10 -10
  91. package/dist/{chunk-OFHFH655.js → chunk-MJCGLM25.js} +8 -8
  92. package/dist/{chunk-OYJKSZIQ.js → chunk-NHWB6HG2.js} +2 -2
  93. package/dist/{chunk-TC3VNKGX.js → chunk-NM2WP3B7.js} +4 -4
  94. package/dist/{chunk-GAWOEQEQ.js → chunk-P5PXWTW7.js} +2 -2
  95. package/dist/chunk-PH3ZWL3W.js +786 -0
  96. package/dist/chunk-PH3ZWL3W.js.map +1 -0
  97. package/dist/{chunk-2QMKMVHH.js → chunk-QNWXCJ76.js} +2 -2
  98. package/dist/{chunk-JMQUBRV6.js → chunk-RGIWINDN.js} +340 -771
  99. package/dist/chunk-RGIWINDN.js.map +1 -0
  100. package/dist/{chunk-WRFBW2TT.js → chunk-SPJXKAVF.js} +3 -3
  101. package/dist/{chunk-HKJCMOT6.js → chunk-TH2LSRMG.js} +2 -2
  102. package/dist/{chunk-CW4AGY3G.js → chunk-VI2JQPNQ.js} +2 -2
  103. package/dist/{chunk-OP6F6L3S.js → chunk-W5EG6LE3.js} +2 -2
  104. package/dist/{chunk-DEBHXHJR.js → chunk-WIPEXG6R.js} +2 -2
  105. package/dist/{chunk-SH6YO3LT.js → chunk-WVO4G37W.js} +2 -2
  106. package/dist/{chunk-FUYEVEX7.js → chunk-YNQBRLC5.js} +24 -22
  107. package/dist/chunk-YNQBRLC5.js.map +1 -0
  108. package/dist/{chunk-7L45JFPW.js → chunk-YTQNQ4KB.js} +14 -14
  109. package/dist/{chunk-PKRIQL3V.js → chunk-YTSNAUXN.js} +2 -2
  110. package/dist/{chunk-5QDRGWT2.js → chunk-ZPSVBSOF.js} +4 -4
  111. package/dist/{cli-CxoSOiG0.d.ts → cli-C4GbETPC.d.ts} +5 -5
  112. package/dist/cli.d.ts +13 -13
  113. package/dist/cli.js +36 -35
  114. package/dist/compounding/engine.d.ts +3 -2
  115. package/dist/compounding/engine.js +10 -9
  116. package/dist/compounding/preference-consolidator.d.ts +1 -1
  117. package/dist/compression-optimizer.d.ts +1 -1
  118. package/dist/config.d.ts +1 -1
  119. package/dist/config.js +3 -3
  120. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  121. package/dist/connectors/codex-materialize-runner.js +10 -9
  122. package/dist/connectors/codex-materialize.d.ts +1 -1
  123. package/dist/connectors/index.d.ts +1 -1
  124. package/dist/connectors/index.js +10 -9
  125. package/dist/consolidation-provenance-check.d.ts +3 -2
  126. package/dist/consolidation-undo.d.ts +3 -2
  127. package/dist/contradiction/index.d.ts +3 -2
  128. package/dist/conversation-index/backend.d.ts +1 -1
  129. package/dist/conversation-index/chunker.d.ts +1 -1
  130. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  131. package/dist/conversation-index/indexer.d.ts +1 -1
  132. package/dist/conversation-index/search.d.ts +1 -1
  133. package/dist/corpus-watermark.d.ts +1 -1
  134. package/dist/corpus-watermark.js +12 -11
  135. package/dist/day-summary.d.ts +1 -1
  136. package/dist/delinearize.d.ts +1 -1
  137. package/dist/direct-answer-wiring.d.ts +1 -1
  138. package/dist/direct-answer.d.ts +1 -1
  139. package/dist/embedding-fallback.d.ts +1 -1
  140. package/dist/enrichment/index.d.ts +1 -1
  141. package/dist/entity-retrieval.d.ts +3 -2
  142. package/dist/entity-retrieval.js +10 -9
  143. package/dist/entity-schema.d.ts +1 -1
  144. package/dist/explicit-capture.d.ts +52 -10
  145. package/dist/explicit-capture.js +13 -1
  146. package/dist/extraction-error-classification.d.ts +1 -1
  147. package/dist/extraction-faithfulness.d.ts +1 -1
  148. package/dist/extraction-judge-telemetry.d.ts +1 -1
  149. package/dist/extraction-judge-training.d.ts +1 -1
  150. package/dist/extraction-judge.d.ts +1 -1
  151. package/dist/extraction-liveness.d.ts +1 -1
  152. package/dist/extraction-source-grounding-rules.d.ts +1 -1
  153. package/dist/extraction-source-grounding.d.ts +1 -1
  154. package/dist/extraction.d.ts +1 -1
  155. package/dist/fallback-llm.d.ts +1 -1
  156. package/dist/{forget-HRPLVB3W.js → forget-4UY2EOKH.js} +3 -3
  157. package/dist/graph-dashboard-diff.d.ts +1 -1
  158. package/dist/graph-dashboard-key.d.ts +1 -1
  159. package/dist/graph-dashboard-parser.d.ts +1 -1
  160. package/dist/graph-edge-reinforcement.d.ts +1 -1
  161. package/dist/graph-snapshot.d.ts +1 -1
  162. package/dist/graph.d.ts +1 -1
  163. package/dist/importance.d.ts +1 -1
  164. package/dist/importers/index.d.ts +1 -1
  165. package/dist/in-flight-reads.d.ts +1 -1
  166. package/dist/index.d.ts +19 -19
  167. package/dist/index.js +47 -45
  168. package/dist/intent.d.ts +1 -1
  169. package/dist/lcm/engine.d.ts +1 -1
  170. package/dist/lcm/index.d.ts +1 -1
  171. package/dist/lcm/tools.d.ts +1 -1
  172. package/dist/lifecycle.d.ts +1 -1
  173. package/dist/live-connectors-runner.d.ts +1 -1
  174. package/dist/local-llm.d.ts +1 -1
  175. package/dist/local-model-endpoint.d.ts +1 -1
  176. package/dist/maintenance/memory-governance.d.ts +1 -1
  177. package/dist/maintenance/memory-governance.js +10 -9
  178. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
  179. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +10 -9
  180. package/dist/maintenance/rebuild-memory-projection.d.ts +3 -2
  181. package/dist/maintenance/rebuild-memory-projection.js +11 -10
  182. package/dist/{maintenance-Cb-PjxQd.d.ts → maintenance-CL30NWtc.d.ts} +3 -3
  183. package/dist/mcp-memory-inspector-app.d.ts +11 -11
  184. package/dist/memory-action-policy.d.ts +1 -1
  185. package/dist/memory-cache.d.ts +1 -1
  186. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  187. package/dist/memory-projection-store.d.ts +1 -1
  188. package/dist/memory-provenance.d.ts +1 -1
  189. package/dist/memory-worth-outcomes.d.ts +3 -2
  190. package/dist/models-json.d.ts +1 -1
  191. package/dist/namespaces/migrate.d.ts +4 -3
  192. package/dist/namespaces/migrate.js +11 -10
  193. package/dist/namespaces/principal.d.ts +1 -1
  194. package/dist/namespaces/search.d.ts +1 -1
  195. package/dist/namespaces/storage.d.ts +4 -3
  196. package/dist/namespaces/storage.js +10 -9
  197. package/dist/native-knowledge.d.ts +1 -1
  198. package/dist/offline-sync-impression-drain.d.ts +1 -1
  199. package/dist/offline-sync-impression-drain.js +5 -5
  200. package/dist/offline-sync.js +2 -2
  201. package/dist/operator-doctor-corpus.d.ts +1 -1
  202. package/dist/operator-doctor-corpus.js +13 -12
  203. package/dist/operator-doctor-replica.d.ts +1 -1
  204. package/dist/operator-toolkit.d.ts +4 -3
  205. package/dist/operator-toolkit.js +18 -17
  206. package/dist/orchestration/compression-guideline-coordinator.d.ts +3 -2
  207. package/dist/orchestration/maintenance.d.ts +5 -4
  208. package/dist/orchestration/maintenance.js +15 -14
  209. package/dist/{orchestrator-5GKqb_HL.d.ts → orchestrator-CKTCRwWV.d.ts} +7 -7
  210. package/dist/orchestrator.d.ts +8 -8
  211. package/dist/orchestrator.js +45 -44
  212. package/dist/patterns-cli.d.ts +1 -1
  213. package/dist/{pipeline-3Qi1TpRo.d.ts → pipeline-B7wj4rRZ.d.ts} +1 -1
  214. package/dist/policy-runtime.d.ts +1 -1
  215. package/dist/proactive-contention.d.ts +1 -1
  216. package/dist/provenance.d.ts +1 -1
  217. package/dist/{purge-YSGX5BOG.js → purge-533FZLS4.js} +16 -9
  218. package/dist/purge-533FZLS4.js.map +1 -0
  219. package/dist/qmd-preflight.d.ts +2 -2
  220. package/dist/{qmd-D9JqzzRX.d.ts → qmd-q9tg4dHp.d.ts} +1 -1
  221. package/dist/qmd-recall-cache.d.ts +1 -1
  222. package/dist/qmd.d.ts +2 -2
  223. package/dist/recall-concurrency-config.d.ts +1 -1
  224. package/dist/recall-disclosure-escalation.d.ts +1 -1
  225. package/dist/recall-explain-renderer.d.ts +1 -1
  226. package/dist/recall-memory-map.d.ts +1 -1
  227. package/dist/recall-planner-llm.d.ts +1 -1
  228. package/dist/recall-state.d.ts +1 -1
  229. package/dist/recall-state.js +2 -2
  230. package/dist/recall-tag-filter.d.ts +1 -1
  231. package/dist/recall-timings.d.ts +1 -1
  232. package/dist/recall-xray-cli.d.ts +1 -1
  233. package/dist/recall-xray-renderer.d.ts +1 -1
  234. package/dist/recall-xray.d.ts +1 -1
  235. package/dist/replay/normalizers/chatgpt.d.ts +1 -1
  236. package/dist/replay/normalizers/claude.d.ts +1 -1
  237. package/dist/replay/normalizers/openclaw.d.ts +1 -1
  238. package/dist/replay/normalizers/shared.d.ts +1 -1
  239. package/dist/replay/runner.d.ts +1 -1
  240. package/dist/replay/types.d.ts +1 -1
  241. package/dist/replica-divergence.d.ts +1 -1
  242. package/dist/replica-peers-config.d.ts +1 -1
  243. package/dist/resolve-auth-token.d.ts +1 -1
  244. package/dist/resume-bundles.js +4 -4
  245. package/dist/retrieval-agents.d.ts +2 -2
  246. package/dist/retrieval-tiers.d.ts +1 -1
  247. package/dist/routing/engine.d.ts +1 -1
  248. package/dist/routing/store.d.ts +1 -1
  249. package/dist/salvage-envelope.d.ts +1 -1
  250. package/dist/{scope-profiles-C4g6_UdP.d.ts → scope-profiles-CWDyglPM.d.ts} +1 -1
  251. package/dist/search/embed-helper.d.ts +1 -1
  252. package/dist/search/factory.d.ts +1 -1
  253. package/dist/search/index.d.ts +1 -1
  254. package/dist/search/lancedb-backend.d.ts +1 -1
  255. package/dist/search/meilisearch-backend.d.ts +1 -1
  256. package/dist/search/noop-backend.d.ts +1 -1
  257. package/dist/search/orama-backend.d.ts +1 -1
  258. package/dist/search/port.d.ts +1 -1
  259. package/dist/search/remote-backend.d.ts +1 -1
  260. package/dist/secure-store/index.js +2 -2
  261. package/dist/{semantic-consolidation-BrxqnzTW.d.ts → semantic-consolidation-BUE3deLn.d.ts} +1 -1
  262. package/dist/semantic-consolidation.d.ts +2 -2
  263. package/dist/semantic-consolidation.js +11 -10
  264. package/dist/semantic-rule-promotion.js +10 -9
  265. package/dist/semantic-rule-verifier.d.ts +1 -1
  266. package/dist/semantic-rule-verifier.js +10 -9
  267. package/dist/{service-Bi1JgxdP.d.ts → service-CXgM0bQG.d.ts} +2 -2
  268. package/dist/session-observer-bands.d.ts +1 -1
  269. package/dist/session-observer-state.d.ts +1 -1
  270. package/dist/shared-context/manager.d.ts +1 -1
  271. package/dist/signal.d.ts +1 -1
  272. package/dist/{storage-CcH6yPk2.d.ts → storage-DysJq-XH.d.ts} +163 -27
  273. package/dist/storage.d.ts +3 -2
  274. package/dist/storage.js +12 -10
  275. package/dist/summarizer.d.ts +1 -1
  276. package/dist/summarizer.js +3 -3
  277. package/dist/summary-snapshot.d.ts +1 -1
  278. package/dist/summary-snapshot.js +2 -2
  279. package/dist/temporal-supersession.d.ts +3 -2
  280. package/dist/temporal-timeline-recall.d.ts +1 -1
  281. package/dist/temporal-validity.d.ts +1 -1
  282. package/dist/threading.d.ts +1 -1
  283. package/dist/tier-migration.d.ts +3 -2
  284. package/dist/tier-routing.d.ts +1 -1
  285. package/dist/topics.d.ts +1 -1
  286. package/dist/transcript.d.ts +1 -1
  287. package/dist/trust-score-stage.d.ts +1 -1
  288. package/dist/trust-score.d.ts +1 -1
  289. package/dist/{types-Dg0w9ADr.d.ts → types-aPPzhnjT.d.ts} +1 -1
  290. package/dist/types.d.ts +1 -1
  291. package/dist/utility-runtime.d.ts +1 -1
  292. package/dist/utils/serialize-mutations.d.ts +11 -7
  293. package/dist/utils/serialize-mutations.js +1 -1
  294. package/dist/verified-recall.js +10 -9
  295. package/dist/write-envelope.d.ts +1 -1
  296. package/package.json +2 -2
  297. package/src/access-observe-write-surface.ts +1 -1
  298. package/src/access-service-coding-write.test.ts +40 -0
  299. package/src/explicit-capture.test.ts +1110 -1
  300. package/src/explicit-capture.ts +534 -135
  301. package/src/maintenance/purge.test.ts +157 -1
  302. package/src/maintenance/purge.ts +15 -9
  303. package/src/orchestration/recall-internal-deps.ts +385 -0
  304. package/src/orchestration/recall-internal.ts +16 -375
  305. package/src/secure-store/secure-fs.ts +78 -1
  306. package/src/storage/content-hash-index.ts +43 -17
  307. package/src/storage/tombstone-blocked-capture-index.ts +562 -0
  308. package/src/storage/tombstone-blocked-capture-mutation.ts +284 -0
  309. package/src/storage/tombstone-blocked-capture-sync.ts +872 -0
  310. package/src/storage-fact-hash-index-defer.test.ts +2900 -57
  311. package/src/storage.ts +266 -265
  312. package/src/testing/subjects/tombstone-blocked-capture.test.ts +181 -0
  313. package/src/utils/serialize-mutations.test.ts +4 -1
  314. package/src/utils/serialize-mutations.ts +26 -27
  315. package/dist/chunk-55MDBZLC.js +0 -490
  316. package/dist/chunk-55MDBZLC.js.map +0 -1
  317. package/dist/chunk-AIK5W3IW.js.map +0 -1
  318. package/dist/chunk-FUYEVEX7.js.map +0 -1
  319. package/dist/chunk-JMQUBRV6.js.map +0 -1
  320. package/dist/chunk-WL5EBRDH.js.map +0 -1
  321. package/dist/purge-YSGX5BOG.js.map +0 -1
  322. /package/dist/{chunk-FZRQZW4H.js.map → chunk-2EUVEDF5.js.map} +0 -0
  323. /package/dist/{chunk-3JNDOAQ2.js.map → chunk-5W4ZLBEZ.js.map} +0 -0
  324. /package/dist/{chunk-5ZCUDWD4.js.map → chunk-62U4J2GN.js.map} +0 -0
  325. /package/dist/{chunk-IGPEJVBY.js.map → chunk-66772WBP.js.map} +0 -0
  326. /package/dist/{chunk-EP5BVCRI.js.map → chunk-6BNE62PM.js.map} +0 -0
  327. /package/dist/{chunk-AAP5T6QG.js.map → chunk-6DNMUYEE.js.map} +0 -0
  328. /package/dist/{chunk-35DL7IOI.js.map → chunk-6FUPCJUL.js.map} +0 -0
  329. /package/dist/{chunk-JP3YPDAW.js.map → chunk-6U7F52DE.js.map} +0 -0
  330. /package/dist/{chunk-E4OCKU67.js.map → chunk-7XC2HX75.js.map} +0 -0
  331. /package/dist/{chunk-OVRQLAYR.js.map → chunk-AZ5BTEKX.js.map} +0 -0
  332. /package/dist/{chunk-2AXMQWCN.js.map → chunk-CZRYLSSW.js.map} +0 -0
  333. /package/dist/{chunk-PTSNLEXK.js.map → chunk-FDAMS4PK.js.map} +0 -0
  334. /package/dist/{chunk-2GXM5N5A.js.map → chunk-GIBMDSJ2.js.map} +0 -0
  335. /package/dist/{chunk-E5OBR6CJ.js.map → chunk-H5CPS5JP.js.map} +0 -0
  336. /package/dist/{chunk-2D7FEUCK.js.map → chunk-I5NLEENJ.js.map} +0 -0
  337. /package/dist/{chunk-DRBXHRXE.js.map → chunk-IB26HVAJ.js.map} +0 -0
  338. /package/dist/{chunk-QDKK63Y7.js.map → chunk-IC6CBHCA.js.map} +0 -0
  339. /package/dist/{chunk-IAEAFLF2.js.map → chunk-JCBWCYEJ.js.map} +0 -0
  340. /package/dist/{chunk-3PDOVUHC.js.map → chunk-KXB4VXHY.js.map} +0 -0
  341. /package/dist/{chunk-WU7TTVOD.js.map → chunk-LBOD4U5B.js.map} +0 -0
  342. /package/dist/{chunk-UBKEQOTD.js.map → chunk-LGHKJABJ.js.map} +0 -0
  343. /package/dist/{chunk-VSURVXKY.js.map → chunk-M3C2V3PV.js.map} +0 -0
  344. /package/dist/{chunk-OFHFH655.js.map → chunk-MJCGLM25.js.map} +0 -0
  345. /package/dist/{chunk-OYJKSZIQ.js.map → chunk-NHWB6HG2.js.map} +0 -0
  346. /package/dist/{chunk-TC3VNKGX.js.map → chunk-NM2WP3B7.js.map} +0 -0
  347. /package/dist/{chunk-GAWOEQEQ.js.map → chunk-P5PXWTW7.js.map} +0 -0
  348. /package/dist/{chunk-2QMKMVHH.js.map → chunk-QNWXCJ76.js.map} +0 -0
  349. /package/dist/{chunk-WRFBW2TT.js.map → chunk-SPJXKAVF.js.map} +0 -0
  350. /package/dist/{chunk-HKJCMOT6.js.map → chunk-TH2LSRMG.js.map} +0 -0
  351. /package/dist/{chunk-CW4AGY3G.js.map → chunk-VI2JQPNQ.js.map} +0 -0
  352. /package/dist/{chunk-OP6F6L3S.js.map → chunk-W5EG6LE3.js.map} +0 -0
  353. /package/dist/{chunk-DEBHXHJR.js.map → chunk-WIPEXG6R.js.map} +0 -0
  354. /package/dist/{chunk-SH6YO3LT.js.map → chunk-WVO4G37W.js.map} +0 -0
  355. /package/dist/{chunk-7L45JFPW.js.map → chunk-YTQNQ4KB.js.map} +0 -0
  356. /package/dist/{chunk-PKRIQL3V.js.map → chunk-YTSNAUXN.js.map} +0 -0
  357. /package/dist/{chunk-5QDRGWT2.js.map → chunk-ZPSVBSOF.js.map} +0 -0
  358. /package/dist/{forget-HRPLVB3W.js.map → forget-4UY2EOKH.js.map} +0 -0
package/src/storage.ts CHANGED
@@ -45,10 +45,7 @@ export { normalizeEntityName } from "./entity-id-normalization.js";
45
45
  import { isErrnoCode } from "./utils/errno.js";
46
46
  import { getCategoryDir, categoryDirName } from "./utils/category-dir.js";
47
47
  import { decodeYamlScalar } from "./utils/yaml-scalar.js";
48
- import {
49
- qmdCollectionPathParts,
50
- qmdResultPathCandidates,
51
- } from "./orchestration/qmd-result-resolver.js";
48
+ import { qmdCollectionPathParts, qmdResultPathCandidates } from "./orchestration/qmd-result-resolver.js";
52
49
  import {
53
50
  clearMemoryCache,
54
51
  getCachedEntities,
@@ -90,7 +87,6 @@ import {
90
87
  readMaybeEncryptedFileBuffer,
91
88
  readMaybeEncryptedFile,
92
89
  writeMaybeEncryptedFile,
93
- writeMaybeEncryptedFileFromChunks,
94
90
  } from "./secure-store/secure-fs.js";
95
91
  import {
96
92
  isConsolidationOperator,
@@ -181,6 +177,12 @@ import {
181
177
  CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS,
182
178
  type ContentHashIndexLockOptions,
183
179
  } from "./storage/content-hash-index.js";
180
+ import {
181
+ TombstoneBlockedCaptureIndexHost,
182
+ buildExplicitCaptureDedupKey,
183
+ parseTombstoneBlockedOfflineSyncMemory,
184
+ type TombstoneBlockedCaptureIndexOptions,
185
+ } from "./storage/tombstone-blocked-capture-index.js";
184
186
  export { ContentHashIndex, FactHashIndexNotAuthoritativeError };
185
187
  export type { ContentHashIndexLockOptions };
186
188
  export {
@@ -201,11 +203,7 @@ import {
201
203
  readProjectedMemoryTimeline,
202
204
  updateProjectedMemoryPath,
203
205
  } from "./memory-projection-store.js";
204
- import {
205
- inferMemoryStatus,
206
- isArchivedMemoryPath,
207
- toMemoryPathRel,
208
- } from "./memory-lifecycle-ledger-utils.js";
206
+ import { inferMemoryStatus, isArchivedMemoryPath, toMemoryPathRel } from "./memory-lifecycle-ledger-utils.js";
209
207
  import { normalizeProjectionPreview, normalizeProjectionTags } from "./memory-projection-format.js";
210
208
  import { parseFlexibleIsoTimestamp } from "./utils/iso-timestamp.js";
211
209
  import {
@@ -652,8 +650,10 @@ function quotedArrayValues(raw: string): string[] {
652
650
  while (i < inner.length) {
653
651
  const ch = inner[i++];
654
652
  if (ch === "\\" && i < inner.length) value += ch + inner[i++];
655
- else if (ch === '"') { values.push(value); break; }
656
- else value += ch;
653
+ else if (ch === '"') {
654
+ values.push(value);
655
+ break;
656
+ } else value += ch;
657
657
  }
658
658
  }
659
659
  return values;
@@ -1698,6 +1698,8 @@ export interface MemoryWriteResult {
1698
1698
  tombstoneBlocked: boolean;
1699
1699
  /** The tombstone id that blocked the write, when `tombstoneBlocked`. */
1700
1700
  blockedBy?: string;
1701
+ /** Existing pending-review memory id when a blocked write coalesced with it. */
1702
+ duplicateOf?: string;
1701
1703
  }
1702
1704
 
1703
1705
  /**
@@ -1706,9 +1708,7 @@ export interface MemoryWriteResult {
1706
1708
  * loader: unknown/legacy shapes are dropped, never thrown on. Uses `in`/typeof
1707
1709
  * narrowing (no inline casts) so a malformed entry can't slip through typed.
1708
1710
  */
1709
- function parseExtractionRetryStateEntries(
1710
- raw: unknown,
1711
- ): NonNullable<MetaState["extractionRetryState"]> {
1711
+ function parseExtractionRetryStateEntries(raw: unknown): NonNullable<MetaState["extractionRetryState"]> {
1712
1712
  if (!Array.isArray(raw)) return [];
1713
1713
  const valid: NonNullable<MetaState["extractionRetryState"]> = [];
1714
1714
  for (const entry of raw) {
@@ -1821,17 +1821,10 @@ export interface WriteMemoryOptions {
1821
1821
  */
1822
1822
  export type SealedWriteExtras = Omit<
1823
1823
  WriteMemoryOptions,
1824
- | "confidence"
1825
- | "tags"
1826
- | "entityRef"
1827
- | "source"
1828
- | "expiresAt"
1829
- | "validAt"
1830
- | "structuredAttributes"
1831
- | "sourceConnector"
1824
+ "confidence" | "tags" | "entityRef" | "source" | "expiresAt" | "validAt" | "structuredAttributes" | "sourceConnector"
1832
1825
  >;
1833
1826
 
1834
- export class StorageManager {
1827
+ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
1835
1828
  private knowledgeIndexCache: { result: string; builtAt: number } | null = null;
1836
1829
  private artifactIndexCache: { memories: MemoryFile[]; loadedAtMs: number; writeVersion: number } | null = null;
1837
1830
  /** Read by storage/entity-store.ts (decomposition), hence not `private`. */
@@ -2031,7 +2024,7 @@ export class StorageManager {
2031
2024
  * OUTSIDE `stateDir`, so a data write always touches; a write under `stateDir`
2032
2025
  * is a pure state write and is skipped unless `touchStateWrites` is enabled.
2033
2026
  */
2034
- private notifyCatalogWriteForPath(filePath: string): void {
2027
+ protected notifyCatalogWriteForPath(filePath: string): void {
2035
2028
  if (!this.touchStateWrites && this.isStateFilePath(filePath)) return;
2036
2029
  this.notifyCatalogWrite();
2037
2030
  }
@@ -2180,10 +2173,7 @@ export class StorageManager {
2180
2173
  private isColdOrArchiveTierPath(p: string): boolean {
2181
2174
  const rel = path.relative(this.baseDir, p);
2182
2175
  return (
2183
- rel === "cold" ||
2184
- rel === "archive" ||
2185
- rel.startsWith(`cold${path.sep}`) ||
2186
- rel.startsWith(`archive${path.sep}`)
2176
+ rel === "cold" || rel === "archive" || rel.startsWith(`cold${path.sep}`) || rel.startsWith(`archive${path.sep}`)
2187
2177
  );
2188
2178
  }
2189
2179
 
@@ -2302,8 +2292,9 @@ export class StorageManager {
2302
2292
  * (falling back to the process-wide default); pass an explicit boolean to
2303
2293
  * override per-instance (tests, memory-constrained readers).
2304
2294
  */
2305
- hotMemoriesCacheEnabledOverride?: boolean,
2295
+ hotMemoriesCacheEnabledOverride?: boolean
2306
2296
  ) {
2297
+ super();
2307
2298
  this.hotMemoriesCacheEnabled =
2308
2299
  hotMemoriesCacheEnabledOverride ??
2309
2300
  StorageManager.hotMemoriesCacheDefaultByDir.get(baseDir) ??
@@ -2323,11 +2314,7 @@ export class StorageManager {
2323
2314
  * StorageManager, including ephemeral recall-sub-stage instances. Only an
2324
2315
  * explicit `false` disables; `undefined` (e.g. a cast test config missing
2325
2316
  * the field) leaves the cache on. */
2326
- static setHotMemoriesCacheDefault(
2327
- memoryDir: string,
2328
- enabled: boolean | undefined,
2329
- ttlMs?: number,
2330
- ): void {
2317
+ static setHotMemoriesCacheDefault(memoryDir: string, enabled: boolean | undefined, ttlMs?: number): void {
2331
2318
  // Register per-dir so each orchestrator's config reaches every
2332
2319
  // StorageManager built over its own memory dir (Codex P2). The per-dir map
2333
2320
  // is authoritative for every registered dir.
@@ -2459,7 +2446,7 @@ export class StorageManager {
2459
2446
  }
2460
2447
  }
2461
2448
 
2462
- private bumpMemoryStatusVersion(): void {
2449
+ protected bumpMemoryStatusVersion(): void {
2463
2450
  this.bumpSharedVersion("memory-status", StorageManager.memoryStatusVersionByDir);
2464
2451
  // Also bump the corpus sentinel so peer processes rescan the hot-memories
2465
2452
  // cache after status/lifecycle/bulk mutations (issue #1902). invalidateAllForDir
@@ -2491,7 +2478,7 @@ export class StorageManager {
2491
2478
  return this.readSharedVersion("memory-corpus", StorageManager.memoryCorpusVersionByDir);
2492
2479
  }
2493
2480
 
2494
- private bumpMemoryCorpusVersion(): void {
2481
+ protected bumpMemoryCorpusVersion(): void {
2495
2482
  // Bump only — unlike bumpMemoryStatusVersion this must NOT invalidateAllForDir
2496
2483
  // (that would drop the very hot entry the write path just patched).
2497
2484
  this.bumpSharedVersion("memory-corpus", StorageManager.memoryCorpusVersionByDir);
@@ -2534,7 +2521,7 @@ export class StorageManager {
2534
2521
  }
2535
2522
  }
2536
2523
 
2537
- private bumpArtifactWriteVersion(): number {
2524
+ protected bumpArtifactWriteVersion(): number {
2538
2525
  return this.bumpSharedVersion("artifact-write", StorageManager.artifactWriteVersionByDir);
2539
2526
  }
2540
2527
 
@@ -2735,7 +2722,7 @@ export class StorageManager {
2735
2722
  private readStorageSecureFile(filePath: string): Promise<string> {
2736
2723
  return readMaybeEncryptedFile(filePath, this._secureStoreKey, this.baseDir);
2737
2724
  }
2738
- private writeStorageSecureFile(filePath: string, content: string | Buffer, forceEncrypt = false): Promise<void> {
2725
+ protected writeStorageSecureFile(filePath: string, content: string | Buffer, forceEncrypt = false): Promise<void> {
2739
2726
  const writeKey = this.resolveWriteKey(forceEncrypt);
2740
2727
  return writeMaybeEncryptedFile(filePath, content, writeKey, {}, this.baseDir).then(() => {
2741
2728
  // No manual sniff-cache update needed (issue #1909 round 10): this rewrite
@@ -2745,14 +2732,8 @@ export class StorageManager {
2745
2732
  this.notifyCatalogWriteForPath(filePath);
2746
2733
  });
2747
2734
  }
2748
- private writeStorageSecureFileChunks(filePath: string, chunks: AsyncIterable<Buffer>): Promise<void> {
2749
- const writeKey = this.resolveWriteKey();
2750
- return writeMaybeEncryptedFileFromChunks(filePath, chunks, writeKey, {}, this.baseDir).then(() => {
2751
- this.notifyCatalogWriteForPath(filePath);
2752
- });
2753
- }
2754
2735
 
2755
- private assertManagedStoragePath(filePath: string, method: string): string {
2736
+ protected assertManagedStoragePath(filePath: string, method: string): string {
2756
2737
  const resolved = path.resolve(filePath);
2757
2738
  const base = path.resolve(this.baseDir);
2758
2739
  const rel = path.relative(base, resolved);
@@ -2924,56 +2905,18 @@ export class StorageManager {
2924
2905
  }
2925
2906
  }
2926
2907
 
2927
- async writeOfflineSyncFile(filePath: string, content: Buffer): Promise<void> {
2928
- const target = this.assertManagedStoragePath(filePath, "storage.writeOfflineSyncFile");
2929
- await this.writeStorageSecureFile(target, content);
2930
- await this.invalidateAfterOfflineSyncMutation(target);
2931
- }
2932
-
2933
2908
  async writeOfflineSyncStagingFile(filePath: string, content: Buffer): Promise<void> {
2934
2909
  const target = this.assertManagedStoragePath(filePath, "storage.writeOfflineSyncStagingFile");
2935
2910
  await this.writeStorageSecureFile(target, content);
2936
2911
  }
2937
2912
 
2938
- async writeOfflineSyncFileChunks(filePath: string, chunks: AsyncIterable<Buffer>): Promise<void> {
2939
- const target = this.assertManagedStoragePath(filePath, "storage.writeOfflineSyncFileChunks");
2940
- await this.writeStorageSecureFileChunks(target, chunks);
2941
- await this.invalidateAfterOfflineSyncMutation(target);
2942
- }
2943
-
2944
- async deleteOfflineSyncFile(filePath: string): Promise<void> {
2945
- const target = this.assertManagedStoragePath(filePath, "storage.deleteOfflineSyncFile");
2946
- await unlink(target).catch((error: unknown) => {
2947
- if (isErrnoCode(error, "ENOENT")) return;
2948
- throw error;
2949
- });
2950
- await this.invalidateAfterOfflineSyncMutation(target);
2951
- }
2952
-
2953
- private async invalidateAfterOfflineSyncMutation(filePath: string): Promise<void> {
2954
- // invalidateAllMemoriesCache() routes through the invalidateAllForDir()
2955
- // chokepoint, which also covers the entity cache (issue #1535).
2956
- this.invalidateAllMemoriesCache();
2957
- this.invalidateKnowledgeIndexCache();
2958
- // Force a corpus rebuild of the fact-hash index on next use (round 11: no
2959
- // on-disk ready marker — the in-memory authoritative flag is the only gate).
2960
- this.factHashIndexAuthoritative = false;
2961
- if (filePath.includes(`${path.sep}cold${path.sep}`)) {
2962
- this.invalidateColdMemoriesCache();
2963
- }
2964
- if (filePath.includes(`${path.sep}artifacts${path.sep}`)) {
2965
- this.bumpArtifactWriteVersion();
2966
- }
2967
- this.bumpMemoryStatusVersion();
2968
- }
2969
-
2970
2913
  createContentHashIndex(): ContentHashIndex {
2971
2914
  return new ContentHashIndex(
2972
2915
  this.stateDir,
2973
2916
  () => this._secureStoreKey,
2974
2917
  () => this.resolveWriteKey(),
2975
2918
  this.baseDir,
2976
- this.factHashIndexLockOptions,
2919
+ this.factHashIndexLockOptions
2977
2920
  );
2978
2921
  }
2979
2922
 
@@ -3262,7 +3205,29 @@ export class StorageManager {
3262
3205
  });
3263
3206
  return await store.rebuild(retired);
3264
3207
  }
3265
-
3208
+ protected markFactHashIndexNotAuthoritative(): void {
3209
+ this.factHashIndexAuthoritative = false;
3210
+ }
3211
+ protected tombstoneBlockedCaptureIndexOptions(): TombstoneBlockedCaptureIndexOptions {
3212
+ return {
3213
+ stateDir: this.stateDir,
3214
+ memoryDir: this.baseDir,
3215
+ secureStoreKeyProvider: () => this._secureStoreKey,
3216
+ secureStoreWriteKeyProvider: () => this.resolveWriteKey(),
3217
+ lockOptions: () => this.factHashIndexLockOptions,
3218
+ parseMemory: (filePath, content) =>
3219
+ parseTombstoneBlockedOfflineSyncMemory(
3220
+ filePath,
3221
+ content,
3222
+ this.baseDir,
3223
+ parseFrontmatter,
3224
+ normalizeFrontmatterForPath,
3225
+ toMemoryPathRel
3226
+ ),
3227
+ readAllMemories: () => this.readAllMemories(),
3228
+ readAllColdMemories: () => this.readAllColdMemories(),
3229
+ };
3230
+ }
3266
3231
  private async getFactHashIndex(): Promise<ContentHashIndex> {
3267
3232
  if (this.factHashIndex) {
3268
3233
  return this.factHashIndex;
@@ -3282,7 +3247,6 @@ export class StorageManager {
3282
3247
  }
3283
3248
  return this.factHashIndexLoadPromise;
3284
3249
  }
3285
-
3286
3250
  /**
3287
3251
  * Return the fact-hash index after ensuring it is authoritative — i.e. rebuilt
3288
3252
  * from the durable fact corpus (issue #1909 review round 12). This is the ONE
@@ -3298,7 +3262,6 @@ export class StorageManager {
3298
3262
  }
3299
3263
  return this.getFactHashIndex();
3300
3264
  }
3301
-
3302
3265
  /**
3303
3266
  * Return the shared fact-hash index instance, corpus-rebuilt under the lock
3304
3267
  * when the lock is free. Unlike {@link getAuthoritativeFactHashIndex} this
@@ -3312,7 +3275,6 @@ export class StorageManager {
3312
3275
  await this.ensureFactHashIndexAuthoritative();
3313
3276
  return this.getFactHashIndex();
3314
3277
  }
3315
-
3316
3278
  private async ensureFactHashIndexAuthoritative(): Promise<boolean> {
3317
3279
  if (this.factHashIndexAuthoritative === true) {
3318
3280
  // PR #2016 review: authority is NOT permanent. A peer process can advance
@@ -3332,7 +3294,6 @@ export class StorageManager {
3332
3294
  if (this.factHashIndexAuthoritativePromise) {
3333
3295
  return this.factHashIndexAuthoritativePromise;
3334
3296
  }
3335
-
3336
3297
  this.factHashIndexAuthoritative = false;
3337
3298
  this.factHashIndexAuthoritativePromise = (async () => {
3338
3299
  // Round 11: ALWAYS rebuild the fact-hash index from the durable corpus on
@@ -3352,15 +3313,11 @@ export class StorageManager {
3352
3313
  // hasFactContentHash()/isFactContentHashAuthoritative() fall back to the
3353
3314
  // durable corpus so a stale loaded snapshot never answers as current.
3354
3315
  const factHashIndex = await this.getFactHashIndex();
3355
- const maxAttempts =
3356
- this.factHashIndexLockOptions.retryMaxAttempts ?? FACT_HASH_INDEX_REBUILD_MAX_ATTEMPTS;
3357
- const baseMs =
3358
- this.factHashIndexLockOptions.retryBaseMs ?? FACT_HASH_INDEX_REBUILD_RETRY_BASE_MS;
3316
+ const maxAttempts = this.factHashIndexLockOptions.retryMaxAttempts ?? FACT_HASH_INDEX_REBUILD_MAX_ATTEMPTS;
3317
+ const baseMs = this.factHashIndexLockOptions.retryBaseMs ?? FACT_HASH_INDEX_REBUILD_RETRY_BASE_MS;
3359
3318
  let published = false;
3360
3319
  for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
3361
- published = await factHashIndex.rebuildUnderLock(() =>
3362
- this.rebuildFactHashIndexFromCorpus(factHashIndex),
3363
- );
3320
+ published = await factHashIndex.rebuildUnderLock(() => this.rebuildFactHashIndexFromCorpus(factHashIndex));
3364
3321
  if (published || attempt === maxAttempts - 1) break;
3365
3322
  const wait = Math.min(baseMs * 2 ** attempt, CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS);
3366
3323
  await new Promise<void>((resolve) => setTimeout(resolve, wait));
@@ -3369,7 +3326,7 @@ export class StorageManager {
3369
3326
  if (!published) {
3370
3327
  log.warn(
3371
3328
  `ensureFactHashIndexAuthoritative: fact-hash index lock unavailable after ${maxAttempts} attempt(s); ` +
3372
- `index left non-authoritative (reads verify against the durable corpus; next use retries the locked rebuild)`,
3329
+ `index left non-authoritative (reads verify against the durable corpus; next use retries the locked rebuild)`
3373
3330
  );
3374
3331
  }
3375
3332
  return published;
@@ -3378,7 +3335,6 @@ export class StorageManager {
3378
3335
  });
3379
3336
  return this.factHashIndexAuthoritativePromise;
3380
3337
  }
3381
-
3382
3338
  /**
3383
3339
  * Repopulate `factHashIndex` in memory from the durable HOT+COLD corpus. Runs
3384
3340
  * WHILE the per-index cross-process lock is held (see
@@ -3401,10 +3357,7 @@ export class StorageManager {
3401
3357
  // survive the corpus rebuild, or a restart would drop the hash and let the
3402
3358
  // next extraction re-create the demoted memory. Matches the hot+cold union
3403
3359
  // that removeFactContentHashesForMemories already reconciles against.
3404
- const existing = [
3405
- ...(await this.readAllMemories()),
3406
- ...(await this.readAllColdMemories()),
3407
- ];
3360
+ const existing = [...(await this.readAllMemories()), ...(await this.readAllColdMemories())];
3408
3361
  let legacyRecovered = 0;
3409
3362
  for (const memory of existing) {
3410
3363
  // #1909 review round 15 (PR #2016): the content-hash dedup index is
@@ -3444,7 +3397,6 @@ export class StorageManager {
3444
3397
  );
3445
3398
  }
3446
3399
  }
3447
-
3448
3400
  /**
3449
3401
  * The content-hash the corpus rebuild registers for `memory`, or null when the
3450
3402
  * body carries a citation from an unknown/custom template that cannot be
@@ -3536,9 +3488,7 @@ export class StorageManager {
3536
3488
  * every append. A foreign write changes size/mtime, forcing a reload; this
3537
3489
  * process's own appends refresh the identity in place. null => reload.
3538
3490
  */
3539
- private behaviorSignalsKeyCache:
3540
- | { identity: { size: number; mtimeMs: number }; keys: Set<string> }
3541
- | null = null;
3491
+ private behaviorSignalsKeyCache: { identity: { size: number; mtimeMs: number }; keys: Set<string> } | null = null;
3542
3492
  /**
3543
3493
  * Per-instance serializer for `appendBehaviorSignals` (issue #1909 review
3544
3494
  * round 3). The read→dedup→append→cache-commit transaction must run to
@@ -3700,6 +3650,26 @@ export class StorageManager {
3700
3650
  return path.join(datedDir, `${id}.md`);
3701
3651
  }
3702
3652
 
3653
+ private async findExistingTombstoneBlockedMemory(
3654
+ content: string,
3655
+ category: MemoryCategory,
3656
+ sourceConnector?: string
3657
+ ): Promise<MemoryFile | null> {
3658
+ const identity = buildExplicitCaptureDedupKey(content, category, sourceConnector);
3659
+ const memories = [...(await this.readAllMemories()), ...(await this.readAllColdMemories())];
3660
+ return (
3661
+ memories.find((memory) => {
3662
+ if (memory.frontmatter.status !== "pending_review" || !memory.frontmatter.blockedBy) return false;
3663
+ return (
3664
+ buildExplicitCaptureDedupKey(
3665
+ memory.content,
3666
+ memory.frontmatter.category,
3667
+ memory.frontmatter.sourceConnector
3668
+ ) === identity
3669
+ );
3670
+ }) ?? null
3671
+ );
3672
+ }
3703
3673
  async writeMemory(
3704
3674
  category: MemoryCategory,
3705
3675
  content: string,
@@ -3869,9 +3839,31 @@ export class StorageManager {
3869
3839
  const fileContent = `${serializeFrontmatter(fm)}\n\n${sanitized.text}\n`;
3870
3840
 
3871
3841
  const filePath = await this.resolveCategoryWritePath(category, id, today);
3872
-
3873
- await this.snapshotBeforeWrite(filePath, "write");
3874
- await this.writeStorageSecureFile(filePath, fileContent);
3842
+ const persistFile = async (): Promise<MemoryFile | null> => {
3843
+ if (tombstoneBlocked) {
3844
+ const duplicate = await this.findExistingTombstoneBlockedMemory(sanitized.text, category, fm.sourceConnector);
3845
+ if (duplicate) return duplicate;
3846
+ }
3847
+ await this.snapshotBeforeWrite(filePath, "write");
3848
+ await this.writeTombstoneBlockedMemory(filePath, fileContent, fm, sanitized.text, async () => {
3849
+ this.invalidateAllMemoriesCache();
3850
+ });
3851
+ return null;
3852
+ };
3853
+ const duplicateBlocked = tombstoneBlocked
3854
+ ? await this.withTombstoneBlockedCaptureWriteLock(
3855
+ persistFile,
3856
+ buildExplicitCaptureDedupKey(sanitized.text, category, fm.sourceConnector)
3857
+ )
3858
+ : await persistFile();
3859
+ if (duplicateBlocked) {
3860
+ return {
3861
+ id: duplicateBlocked.frontmatter.id,
3862
+ tombstoneBlocked: true,
3863
+ blockedBy: duplicateBlocked.frontmatter.blockedBy,
3864
+ duplicateOf: duplicateBlocked.frontmatter.id,
3865
+ };
3866
+ }
3875
3867
  await this.patchHotMemoriesCache({ addedPath: filePath }, "memory-create");
3876
3868
  this.notifyMemoryWrite(filePath);
3877
3869
  await this.appendGeneratedMemoryLifecycleEventFailOpen("storage.writeMemory", {
@@ -3948,13 +3940,10 @@ export class StorageManager {
3948
3940
  * `envelope.sourceReason` is access-layer metadata with no frontmatter
3949
3941
  * field and is deliberately not persisted here.
3950
3942
  */
3951
- async writeSealedMemory(
3952
- envelope: SealedMemoryEnvelope,
3953
- extras: SealedWriteExtras = {},
3954
- ): Promise<MemoryWriteResult> {
3943
+ async writeSealedMemory(envelope: SealedMemoryEnvelope, extras: SealedWriteExtras = {}): Promise<MemoryWriteResult> {
3955
3944
  if (!isSealedMemoryEnvelope(envelope)) {
3956
3945
  throw new Error(
3957
- "writeSealedMemory: value is not a valid sealed memory envelope (fails the composeMemoryEnvelope contract)",
3946
+ "writeSealedMemory: value is not a valid sealed memory envelope (fails the composeMemoryEnvelope contract)"
3958
3947
  );
3959
3948
  }
3960
3949
  const { category, content, options } = sealedWriteToLegacyArgs(envelope, extras);
@@ -3986,10 +3975,7 @@ export class StorageManager {
3986
3975
  }
3987
3976
 
3988
3977
  private async factContentHashPresentInCorpus(targetHash: string): Promise<boolean> {
3989
- const existing = [
3990
- ...(await this.readAllMemories()),
3991
- ...(await this.readAllColdMemories()),
3992
- ];
3978
+ const existing = [...(await this.readAllMemories()), ...(await this.readAllColdMemories())];
3993
3979
  for (const memory of existing) {
3994
3980
  if (memory.frontmatter.category !== "fact") continue;
3995
3981
  if (inferMemoryStatus(memory.frontmatter, memory.path) !== "active") continue;
@@ -4341,7 +4327,7 @@ export class StorageManager {
4341
4327
  this.baseDir,
4342
4328
  this.getMemoryCorpusVersion(),
4343
4329
  this.hotCacheKeyId(),
4344
- this.hotCacheTtlMs(),
4330
+ this.hotCacheTtlMs()
4345
4331
  );
4346
4332
  // Null-check, not truthiness (kilo WARNING): getCachedMemories returns []
4347
4333
  // for a cached EMPTY corpus, which is falsy — a truthiness guard would
@@ -4372,11 +4358,7 @@ export class StorageManager {
4372
4358
  // during the scan. The version guard prevents clobbering a newer patched +
4373
4359
  // re-keyed entry; the keyId guard prevents publishing this key's decrypted
4374
4360
  // corpus under a since-changed identity (Codex Medium, #1902).
4375
- if (
4376
- this.hotMemoriesCacheEnabled &&
4377
- this.getMemoryCorpusVersion() === version &&
4378
- this.hotCacheKeyId() === keyId
4379
- ) {
4361
+ if (this.hotMemoriesCacheEnabled && this.getMemoryCorpusVersion() === version && this.hotCacheKeyId() === keyId) {
4380
4362
  setCachedMemories(this.baseDir, memories, version, keyId, this.hotCacheTtlMs());
4381
4363
  }
4382
4364
  return memories;
@@ -4455,7 +4437,7 @@ export class StorageManager {
4455
4437
  * exclusively by invalidateColdMemoriesCache(), which is called only when
4456
4438
  * cold content actually changes (hot→cold demotions, writeMemoryFileAtomic
4457
4439
  * inside cold/, archiveMemory, etc.). */
4458
- private invalidateAllMemoriesCache(): void {
4440
+ protected invalidateAllMemoriesCache(): void {
4459
4441
  deleteInFlightReadsForDir(this.baseDir);
4460
4442
  // Bulk/ambiguous mutations drop the hot layer wholesale (below); bump the
4461
4443
  // corpus sentinel too so PEER processes rescan instead of serving a warm
@@ -4506,19 +4488,17 @@ export class StorageManager {
4506
4488
  */
4507
4489
  private async patchHotMemoriesCache(
4508
4490
  opts: { addedPath?: string; removedPath?: string },
4509
- scope: "memory-create" | "memory-mutate" = "memory-mutate",
4491
+ scope: "memory-create" | "memory-mutate" = "memory-mutate"
4510
4492
  ): Promise<void> {
4511
4493
  const prevVersion = this.getMemoryCorpusVersion();
4512
4494
  // Snapshot the key identity once (issue #1902, Codex Medium): a mid-await
4513
4495
  // setSecureStoreKey change would otherwise let the post-await patch/re-key
4514
4496
  // store this key's decrypted memory under a since-changed identity.
4515
4497
  const keyId = this.hotCacheKeyId();
4516
- const warm = this.hotMemoriesCacheEnabled && getCachedMemories(this.baseDir, prevVersion, keyId, this.hotCacheTtlMs()) !== null;
4517
- if (
4518
- warm &&
4519
- opts.removedPath !== undefined &&
4520
- !this.isColdOrArchiveTierPath(opts.removedPath)
4521
- ) {
4498
+ const warm =
4499
+ this.hotMemoriesCacheEnabled &&
4500
+ getCachedMemories(this.baseDir, prevVersion, keyId, this.hotCacheTtlMs()) !== null;
4501
+ if (warm && opts.removedPath !== undefined && !this.isColdOrArchiveTierPath(opts.removedPath)) {
4522
4502
  updateCacheOnDelete(this.baseDir, opts.removedPath, keyId);
4523
4503
  }
4524
4504
  // Bump the corpus sentinel AND drop the in-flight slot BEFORE the awaited
@@ -4532,11 +4512,7 @@ export class StorageManager {
4532
4512
  const { produced, exclusive } = this.bumpMemoryCorpusVersionExclusive();
4533
4513
  deleteInFlightReadsForDir(this.baseDir);
4534
4514
  let patchedOk = warm;
4535
- if (
4536
- warm &&
4537
- opts.addedPath !== undefined &&
4538
- !this.isColdOrArchiveTierPath(opts.addedPath)
4539
- ) {
4515
+ if (warm && opts.addedPath !== undefined && !this.isColdOrArchiveTierPath(opts.addedPath)) {
4540
4516
  // Re-read + parse the ONE just-written file so the cached object is
4541
4517
  // exactly what a fresh disk scan would yield — serialize→parse normalizes
4542
4518
  // fields (e.g. an empty array becomes undefined), and the in-memory write
@@ -4566,7 +4542,13 @@ export class StorageManager {
4566
4542
  patchedOk = false;
4567
4543
  }
4568
4544
  }
4569
- if (patchedOk && exclusive && produced === prevVersion + 1 && this.getMemoryCorpusVersion() === produced && this.hotCacheKeyId() === keyId) {
4545
+ if (
4546
+ patchedOk &&
4547
+ exclusive &&
4548
+ produced === prevVersion + 1 &&
4549
+ this.getMemoryCorpusVersion() === produced &&
4550
+ this.hotCacheKeyId() === keyId
4551
+ ) {
4570
4552
  // Re-key the patched entry (still tagged prevVersion) to the version THIS
4571
4553
  // mutation produced — ONLY if no peer appended between prevVersion capture
4572
4554
  // and our bump (produced === prevVersion + 1), our bump was exclusive, and
@@ -4609,7 +4591,7 @@ export class StorageManager {
4609
4591
  * Finding UvUy (PR #402 round-11): bumping the sentinel here makes the
4610
4592
  * per-process in-memory cache safe across process boundaries.
4611
4593
  */
4612
- private invalidateColdMemoriesCache(): void {
4594
+ protected invalidateColdMemoriesCache(): void {
4613
4595
  const coldRoot = path.join(this.baseDir, "cold");
4614
4596
  StorageManager.coldMemoriesCache.delete(coldRoot);
4615
4597
  this.bumpColdWriteVersion();
@@ -4981,51 +4963,49 @@ export class StorageManager {
4981
4963
  * Returns the new file path on success, null on failure.
4982
4964
  */
4983
4965
  async archiveMemory(memory: MemoryFile, lifecycle?: MemoryLifecycleEventWriteOptions): Promise<string | null> {
4984
- try {
4985
- const now = lifecycle?.at ?? new Date();
4986
- const today = now.toISOString().slice(0, 10);
4987
- const destDir = path.join(this.archiveDir, today);
4988
- await mkdir(destDir, { recursive: true });
4989
-
4990
- // Update frontmatter to reflect archived status
4991
- const updatedFm: MemoryFrontmatter = {
4992
- ...memory.frontmatter,
4993
- status: "archived",
4994
- archivedAt: now.toISOString(),
4995
- updated: now.toISOString(),
4996
- };
4997
-
4998
- const fileContent = `${serializeFrontmatter(updatedFm)}\n\n${memory.content}\n`;
4999
- const destPath = path.join(destDir, path.basename(memory.path));
5000
-
5001
- // Write to archive location first (encrypted if applicable), then remove original.
5002
- await this.writeStorageSecureFile(destPath, fileContent);
5003
- await unlink(memory.path);
5004
- markProjectedMemoryPathInvalid(this.baseDir, memory.frontmatter.id);
5005
- this.invalidateAllMemoriesCache();
5006
- await this.appendGeneratedMemoryLifecycleEventFailOpen(
5007
- "storage.archiveMemory",
5008
- {
5009
- memoryId: memory.frontmatter.id,
5010
- eventType: "archived",
5011
- timestamp: updatedFm.archivedAt ?? updatedFm.updated,
5012
- actor: lifecycle?.actor ?? "storage.archiveMemory",
5013
- reasonCode: lifecycle?.reasonCode,
5014
- before: this.summarizeLifecycleState(memory.frontmatter, memory.path),
5015
- after: this.summarizeLifecycleState(updatedFm, destPath),
5016
- relatedMemoryIds: lifecycle?.relatedMemoryIds,
5017
- correlationId: lifecycle?.correlationId,
5018
- },
5019
- lifecycle?.ruleVersion
5020
- );
5021
- this.bumpMemoryStatusVersion();
5022
-
5023
- log.debug(`archived memory ${memory.frontmatter.id} → ${destPath}`);
5024
- return destPath;
5025
- } catch (err) {
5026
- log.warn(`failed to archive memory ${memory.frontmatter.id}: ${err}`);
5027
- return null;
5028
- }
4966
+ const archiveCurrent = async (current: MemoryFile, markDurable: () => void): Promise<string | null> => {
4967
+ try {
4968
+ const now = lifecycle?.at ?? new Date();
4969
+ const today = now.toISOString().slice(0, 10);
4970
+ const destDir = path.join(this.archiveDir, today);
4971
+ await mkdir(destDir, { recursive: true });
4972
+ const updatedFm: MemoryFrontmatter = {
4973
+ ...current.frontmatter,
4974
+ status: "archived",
4975
+ archivedAt: now.toISOString(),
4976
+ updated: now.toISOString(),
4977
+ };
4978
+ const fileContent = `${serializeFrontmatter(updatedFm)}\n\n${current.content}\n`;
4979
+ const destPath = path.join(destDir, path.basename(current.path));
4980
+ await this.writeStorageSecureFile(destPath, fileContent);
4981
+ await unlink(current.path);
4982
+ markDurable();
4983
+ markProjectedMemoryPathInvalid(this.baseDir, current.frontmatter.id);
4984
+ this.invalidateAllMemoriesCache();
4985
+ await this.appendGeneratedMemoryLifecycleEventFailOpen(
4986
+ "storage.archiveMemory",
4987
+ {
4988
+ memoryId: current.frontmatter.id,
4989
+ eventType: "archived",
4990
+ timestamp: updatedFm.archivedAt ?? updatedFm.updated,
4991
+ actor: lifecycle?.actor ?? "storage.archiveMemory",
4992
+ reasonCode: lifecycle?.reasonCode,
4993
+ before: this.summarizeLifecycleState(current.frontmatter, current.path),
4994
+ after: this.summarizeLifecycleState(updatedFm, destPath),
4995
+ relatedMemoryIds: lifecycle?.relatedMemoryIds,
4996
+ correlationId: lifecycle?.correlationId,
4997
+ },
4998
+ lifecycle?.ruleVersion
4999
+ );
5000
+ this.bumpMemoryStatusVersion();
5001
+ log.debug(`archived memory ${current.frontmatter.id} → ${destPath}`);
5002
+ return destPath;
5003
+ } catch (err) {
5004
+ log.warn(`failed to archive memory ${current.frontmatter.id}: ${err}`);
5005
+ return null;
5006
+ }
5007
+ };
5008
+ return await this.runTombstoneBlockedArchive(memory, archiveCurrent);
5029
5009
  }
5030
5010
 
5031
5011
  async readEntities(): Promise<string[]> {
@@ -5095,21 +5075,25 @@ export class StorageManager {
5095
5075
  return null;
5096
5076
  }
5097
5077
 
5078
+
5098
5079
  async invalidateMemory(id: string): Promise<boolean> {
5099
5080
  const memories = await this.readAllMemories();
5100
- const memory = memories.find((m) => m.frontmatter.id === id);
5081
+ const memory = memories.find((candidate) => candidate.frontmatter.id === id);
5101
5082
  if (!memory) return false;
5102
5083
 
5103
- try {
5104
- await unlink(memory.path);
5105
- markProjectedMemoryPathInvalid(this.baseDir, id);
5106
- this.invalidateAllMemoriesCache();
5107
- this.bumpMemoryStatusVersion();
5108
- log.debug(`invalidated memory ${id}`);
5109
- return true;
5110
- } catch {
5111
- return false;
5112
- }
5084
+ return await this.runTombstoneBlockedInvalidation(
5085
+ memory,
5086
+ async (current, rebuildMarker, markDurable) => {
5087
+ await unlink(current.path);
5088
+ markDurable();
5089
+ markProjectedMemoryPathInvalid(this.baseDir, id);
5090
+ this.invalidateAllMemoriesCache();
5091
+ await this.rebuildTombstoneBlockedCaptureAfterInvalidation(rebuildMarker);
5092
+ this.bumpMemoryStatusVersion();
5093
+ log.debug(`invalidated memory ${id}`);
5094
+ return true;
5095
+ }
5096
+ );
5113
5097
  }
5114
5098
 
5115
5099
  async updateMemory(
@@ -5139,7 +5123,9 @@ export class StorageManager {
5139
5123
  log.warn(`updated memory content sanitized for ${id}; violations=${sanitized.violations.join(", ")}`);
5140
5124
  }
5141
5125
  const fileContent = `${serializeFrontmatter(updated)}\n\n${sanitized.text}\n`;
5142
- await this.writeStorageSecureFile(memory.path, fileContent);
5126
+ await this.writeTombstoneBlockedUpdate(memory, fileContent, updated, sanitized.text, async () => {
5127
+ this.invalidateAllMemoriesCache();
5128
+ });
5143
5129
  await this.patchHotMemoriesCache({ addedPath: memory.path });
5144
5130
  await this.appendGeneratedMemoryLifecycleEventFailOpen("storage.updateMemory", {
5145
5131
  memoryId: id,
@@ -5174,11 +5160,14 @@ export class StorageManager {
5174
5160
  const afterStatus = updated.status ?? "active";
5175
5161
 
5176
5162
  const fileContent = `${serializeFrontmatter(updated)}\n\n${memory.content}\n`;
5177
- await this.writeStorageSecureFile(memory.path, fileContent);
5163
+ await this.writeTombstoneBlockedFrontmatter(memory, fileContent, updated, async () => {
5164
+ this.invalidateAllMemoriesCache();
5165
+ // Rebuild the blocked index from the post-write cold-tier cache.
5166
+ if (memory.path.includes(`${path.sep}cold${path.sep}`)) {
5167
+ this.invalidateColdMemoriesCache();
5168
+ }
5169
+ });
5178
5170
  await this.patchHotMemoriesCache({ addedPath: memory.path });
5179
- // If the target file lives in cold/, bump the cold-version sentinel so
5180
- // other processes detect the change on their next readAllColdMemories()
5181
- // call (Finding UvUy fix).
5182
5171
  if (memory.path.includes(`${path.sep}cold${path.sep}`)) {
5183
5172
  this.invalidateColdMemoriesCache();
5184
5173
  }
@@ -5238,19 +5227,16 @@ export class StorageManager {
5238
5227
  for (const m of memories) {
5239
5228
  if (!m.frontmatter.expiresAt) continue;
5240
5229
  const expiresAt = new Date(m.frontmatter.expiresAt).getTime();
5241
- if (expiresAt < now) {
5242
- try {
5243
- await unlink(m.path);
5244
- markProjectedMemoryPathInvalid(this.baseDir, m.frontmatter.id);
5245
- // Bump per deletion so a concurrent readAllMemories mid-loop rescans
5246
- // and never re-caches a partially-cleaned corpus (Cursor Medium,
5247
- // #1902); invalidateAllMemoriesCache below still runs at loop end.
5248
- this.bumpMemoryCorpusVersion();
5249
- deleted.push(m);
5250
- log.debug(`cleaned expired memory ${m.frontmatter.id} (TTL expired)`);
5251
- } catch {
5252
- // Ignore
5253
- }
5230
+ if (expiresAt >= now) continue;
5231
+ const removed = await this.deleteMemoryForMaintenance(m, (current) => {
5232
+ const currentExpiresAt = current.frontmatter.expiresAt
5233
+ ? new Date(current.frontmatter.expiresAt).getTime()
5234
+ : Number.NaN;
5235
+ return Number.isFinite(currentExpiresAt) && currentExpiresAt < now;
5236
+ });
5237
+ if (removed) {
5238
+ deleted.push(removed);
5239
+ log.debug(`cleaned expired memory ${removed.frontmatter.id} (TTL expired)`);
5254
5240
  }
5255
5241
  }
5256
5242
 
@@ -5355,7 +5341,7 @@ export class StorageManager {
5355
5341
  this.memoryLifecycleLedgerPath,
5356
5342
  (p) => this.appendStorageSecureFile(this.memoryLifecycleLedgerPath, p),
5357
5343
  serializeLifecycleAppendPayload(events),
5358
- { writeSecure: (p, c) => this.writeStorageSecureFile(p, c), readSecure: (p) => this.readStorageSecureFile(p) },
5344
+ { writeSecure: (p, c) => this.writeStorageSecureFile(p, c), readSecure: (p) => this.readStorageSecureFile(p) }
5359
5345
  );
5360
5346
  return events.length;
5361
5347
  }
@@ -5367,7 +5353,7 @@ export class StorageManager {
5367
5353
  this.memoryLifecycleLedgerPath,
5368
5354
  { writeSecure: (p, c) => this.writeStorageSecureFile(p, c), readSecure: (p) => this.readStorageSecureFile(p) },
5369
5355
  (p) => this.appendStorageSecureFile(this.memoryLifecycleLedgerPath, p),
5370
- () => this.ensureDirectories(),
5356
+ () => this.ensureDirectories()
5371
5357
  );
5372
5358
  }
5373
5359
 
@@ -5380,20 +5366,15 @@ export class StorageManager {
5380
5366
  this.memoryLifecycleLedgerPath,
5381
5367
  { writeSecure: (p, c) => this.writeStorageSecureFile(p, c), readSecure: (p) => this.readStorageSecureFile(p) },
5382
5368
  (p) => this.appendStorageSecureFile(this.memoryLifecycleLedgerPath, p),
5383
- () => this.ensureDirectories(),
5369
+ () => this.ensureDirectories()
5384
5370
  );
5385
5371
  }
5386
5372
 
5387
5373
  /** Drain pending lifecycle spills for any ledger inside this storage root.
5388
5374
  * The offline CLI uses this path-aware variant for per-namespace ledgers so
5389
5375
  * secure-store encryption and path-bound authentication remain consistent. */
5390
- async drainPendingMemoryLifecycleEventsForSyncAt(
5391
- ledgerPath: string,
5392
- ): Promise<DrainPendingLifecycleForSyncResult> {
5393
- const target = this.assertManagedStoragePath(
5394
- ledgerPath,
5395
- "storage.drainPendingMemoryLifecycleEventsForSyncAt",
5396
- );
5376
+ async drainPendingMemoryLifecycleEventsForSyncAt(ledgerPath: string): Promise<DrainPendingLifecycleForSyncResult> {
5377
+ const target = this.assertManagedStoragePath(ledgerPath, "storage.drainPendingMemoryLifecycleEventsForSyncAt");
5397
5378
  return drainPendingLifecycleLedgerForSync(
5398
5379
  target,
5399
5380
  {
@@ -5403,7 +5384,7 @@ export class StorageManager {
5403
5384
  (payload) => this.appendStorageSecureFile(target, payload),
5404
5385
  async () => {
5405
5386
  await mkdir(path.dirname(target), { recursive: true });
5406
- },
5387
+ }
5407
5388
  );
5408
5389
  }
5409
5390
 
@@ -5416,7 +5397,7 @@ export class StorageManager {
5416
5397
  async writeMemoryLifecycleLedgerContent(
5417
5398
  content: string | Buffer,
5418
5399
  targetPath: string = this.memoryLifecycleLedgerPath,
5419
- forceEncrypt = false,
5400
+ forceEncrypt = false
5420
5401
  ): Promise<void> {
5421
5402
  await this.ensureDirectories();
5422
5403
  await this.writeStorageSecureFile(targetPath, content, forceEncrypt);
@@ -5664,20 +5645,19 @@ export class StorageManager {
5664
5645
  }
5665
5646
  async readAllMemoryLifecycleEventsForCompaction(): Promise<MemoryLifecycleEvent[]> {
5666
5647
  if (!(await probeEncryptedRegularFileHeader(this.memoryLifecycleLedgerPath))) {
5667
- return readAllLifecycleEventsFromLedger(
5668
- this.memoryLifecycleLedgerPath,
5669
- (p) => this.readStorageSecureFile(p),
5670
- );
5648
+ return readAllLifecycleEventsFromLedger(this.memoryLifecycleLedgerPath, (p) => this.readStorageSecureFile(p));
5671
5649
  }
5672
- return readAllLifecycleEventsFromLedgerBuffer(
5673
- this.memoryLifecycleLedgerPath,
5674
- (p) => readMaybeEncryptedFileBuffer(p, this._secureStoreKey, this.baseDir),
5650
+ return readAllLifecycleEventsFromLedgerBuffer(this.memoryLifecycleLedgerPath, (p) =>
5651
+ readMaybeEncryptedFileBuffer(p, this._secureStoreKey, this.baseDir)
5675
5652
  );
5676
5653
  }
5677
5654
 
5678
5655
  async readMemoryLifecycleEvents(limit: number = 200): Promise<MemoryLifecycleEvent[]> {
5679
5656
  return readBoundedLifecycleEventsFromLedger(
5680
- this.memoryLifecycleLedgerPath, (p) => this.readStorageSecureFile(p), limit);
5657
+ this.memoryLifecycleLedgerPath,
5658
+ (p) => this.readStorageSecureFile(p),
5659
+ limit
5660
+ );
5681
5661
  }
5682
5662
 
5683
5663
  async writeCompressionGuidelines(content: string): Promise<void> {
@@ -6366,20 +6346,19 @@ export class StorageManager {
6366
6346
  if (!isResolved) continue;
6367
6347
 
6368
6348
  const updatedAt = new Date(m.frontmatter.updated).getTime();
6369
- if (updatedAt < cutoff) {
6370
- // Remove the file
6371
- try {
6372
- await unlink(m.path);
6373
- markProjectedMemoryPathInvalid(this.baseDir, m.frontmatter.id);
6374
- // Bump per deletion so a concurrent readAllMemories mid-loop rescans
6375
- // and never re-caches a partially-cleaned corpus (Cursor Medium,
6376
- // #1902); bumpMemoryStatusVersion below still runs at loop end.
6377
- this.bumpMemoryCorpusVersion();
6378
- deleted.push(m);
6379
- log.debug(`cleaned expired commitment ${m.frontmatter.id}`);
6380
- } catch {
6381
- // Ignore
6349
+ if (updatedAt >= cutoff) continue;
6350
+ try {
6351
+ const removed = await this.deleteMemoryForMaintenance(m, (current) => {
6352
+ const currentResolved = current.frontmatter.tags.some((tag) => tag === "fulfilled" || tag === "expired");
6353
+ const currentUpdatedAt = new Date(current.frontmatter.updated).getTime();
6354
+ return currentResolved && currentUpdatedAt < cutoff;
6355
+ });
6356
+ if (removed) {
6357
+ deleted.push(removed);
6358
+ log.debug(`cleaned expired commitment ${removed.frontmatter.id}`);
6382
6359
  }
6360
+ } catch {
6361
+ // Ignore
6383
6362
  }
6384
6363
  }
6385
6364
 
@@ -6416,7 +6395,8 @@ export class StorageManager {
6416
6395
  // different identity than the one that decrypted this corpus.
6417
6396
  const keyId = this.hotCacheKeyId();
6418
6397
  const warm =
6419
- this.hotMemoriesCacheEnabled && getCachedMemories(this.baseDir, prevVersion, keyId, this.hotCacheTtlMs()) !== null;
6398
+ this.hotMemoriesCacheEnabled &&
6399
+ getCachedMemories(this.baseDir, prevVersion, keyId, this.hotCacheTtlMs()) !== null;
6420
6400
  // Record each applied patch so the end-of-flush re-key can re-apply them on
6421
6401
  // top of whatever is cached at prevVersion — robust to a concurrent scan
6422
6402
  // that republished an UNpatched corpus mid-flush (Cursor Medium #1902).
@@ -6469,7 +6449,13 @@ export class StorageManager {
6469
6449
  // and could republish it at the old version, clobbering our patches. New
6470
6450
  // readers now start a fresh scan at the bumped version.
6471
6451
  deleteInFlightReadsForDir(this.baseDir);
6472
- if (warm && exclusive && produced === prevVersion + 1 && this.getMemoryCorpusVersion() === produced && this.hotCacheKeyId() === keyId) {
6452
+ if (
6453
+ warm &&
6454
+ exclusive &&
6455
+ produced === prevVersion + 1 &&
6456
+ this.getMemoryCorpusVersion() === produced &&
6457
+ this.hotCacheKeyId() === keyId
6458
+ ) {
6473
6459
  const cur = getCachedMemories(this.baseDir, prevVersion, keyId);
6474
6460
  if (cur) {
6475
6461
  // Re-apply our patches on top of whatever is cached at prevVersion
@@ -6482,7 +6468,10 @@ export class StorageManager {
6482
6468
  const reapplied = cur.map((m) => {
6483
6469
  const patch = appliedPatches.get(path.resolve(m.path));
6484
6470
  return patch
6485
- ? { ...m, frontmatter: { ...m.frontmatter, accessCount: patch.accessCount, lastAccessed: patch.lastAccessed } }
6471
+ ? {
6472
+ ...m,
6473
+ frontmatter: { ...m.frontmatter, accessCount: patch.accessCount, lastAccessed: patch.lastAccessed },
6474
+ }
6486
6475
  : m;
6487
6476
  });
6488
6477
  setCachedMemories(this.baseDir, reapplied, produced, keyId, this.hotCacheTtlMs());
@@ -6516,7 +6505,7 @@ export class StorageManager {
6516
6505
  */
6517
6506
  async findExistingMemoryPaths(
6518
6507
  ids: string[],
6519
- preferredPaths: Map<string, string[]> = new Map(),
6508
+ preferredPaths: Map<string, string[]> = new Map()
6520
6509
  ): Promise<Map<string, string[]>> {
6521
6510
  if (ids.length === 0) return new Map();
6522
6511
  const wantedIds = new Set(ids);
@@ -6535,12 +6524,8 @@ export class StorageManager {
6535
6524
  const preferred = preferredPaths.get(id) ?? [];
6536
6525
  const preferredMatches: string[] = [];
6537
6526
  for (const preferredPath of preferred) {
6538
- const directCandidates = new Set(
6539
- qmdResultPathCandidates(this.baseDir, preferredPath),
6540
- );
6541
- const directMatch = existingPaths.find((filePath) =>
6542
- directCandidates.has(path.resolve(filePath)),
6543
- );
6527
+ const directCandidates = new Set(qmdResultPathCandidates(this.baseDir, preferredPath));
6528
+ const directMatch = existingPaths.find((filePath) => directCandidates.has(path.resolve(filePath)));
6544
6529
  if (directMatch) {
6545
6530
  preferredMatches.push(directMatch);
6546
6531
  continue;
@@ -6642,7 +6627,11 @@ export class StorageManager {
6642
6627
  return `projection ${age} stale, last rebuilt ${rebuiltAt}`;
6643
6628
  });
6644
6629
  return readBoundedLifecycleEventsFromLedger(
6645
- this.memoryLifecycleLedgerPath, (p) => this.readStorageSecureFile(p), cappedLimit, memoryId);
6630
+ this.memoryLifecycleLedgerPath,
6631
+ (p) => this.readStorageSecureFile(p),
6632
+ cappedLimit,
6633
+ memoryId
6634
+ );
6646
6635
  }
6647
6636
 
6648
6637
  // ---------------------------------------------------------------------------
@@ -6742,12 +6731,19 @@ export class StorageManager {
6742
6731
 
6743
6732
  const filePath = await this.resolveCategoryWritePath(category, id, today);
6744
6733
 
6745
- await this.writeStorageSecureFile(filePath, fileContent);
6746
- // Keep the version-keyed hot-memories cache coherent with the new chunk
6747
- // file (issue #1902) — same single-file patch path writeMemory uses.
6748
- await this.patchHotMemoriesCache({ addedPath: filePath }, "memory-create");
6749
- log.debug(`wrote chunk ${id} (${chunkIndex + 1}/${chunkTotal}) to ${filePath}`);
6750
- return id;
6734
+ return await this.writeTombstoneBlockedChunk(
6735
+ filePath,
6736
+ fileContent,
6737
+ fm,
6738
+ sanitized.text,
6739
+ () => this.findExistingTombstoneBlockedMemory(sanitized.text, category, fm.sourceConnector),
6740
+ async () => {
6741
+ // Keep the version-keyed hot-memories cache coherent with the new chunk
6742
+ // file (issue #1902) — same single-file patch path writeMemory uses.
6743
+ await this.patchHotMemoriesCache({ addedPath: filePath }, "memory-create");
6744
+ log.debug(`wrote chunk ${id} (${chunkIndex + 1}/${chunkTotal}) to ${filePath}`);
6745
+ }
6746
+ );
6751
6747
  }
6752
6748
 
6753
6749
  /**
@@ -6835,7 +6831,12 @@ export class StorageManager {
6835
6831
 
6836
6832
  // Audit-trail correction — sealed even INSIDE the engine (#2022 review).
6837
6833
  const auditBody = `Superseded: ${oldMemory.content}\n\nReason: ${reason}`;
6838
- const auditInput = { content: auditBody, category: "correction" as const, confidence: 1.0, tags: ["supersession", "auto-resolved"] };
6834
+ const auditInput = {
6835
+ content: auditBody,
6836
+ category: "correction" as const,
6837
+ confidence: 1.0,
6838
+ tags: ["supersession", "auto-resolved"],
6839
+ };
6839
6840
  const auditEnvelope = composeMemoryEnvelope(auditInput, { source: "contradiction-detection" });
6840
6841
  await this.writeSealedMemory(auditEnvelope, { lineage: [oldMemoryId, newMemoryId] });
6841
6842