@remnic/core 9.29.1 → 9.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (355) 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-FUYEVEX7.js → chunk-PUS63Q3G.js} +24 -22
  98. package/dist/{chunk-FUYEVEX7.js.map → chunk-PUS63Q3G.js.map} +1 -1
  99. package/dist/{chunk-2QMKMVHH.js → chunk-QNWXCJ76.js} +2 -2
  100. package/dist/{chunk-JMQUBRV6.js → chunk-RGIWINDN.js} +340 -771
  101. package/dist/chunk-RGIWINDN.js.map +1 -0
  102. package/dist/{chunk-WRFBW2TT.js → chunk-SPJXKAVF.js} +3 -3
  103. package/dist/{chunk-HKJCMOT6.js → chunk-TH2LSRMG.js} +2 -2
  104. package/dist/{chunk-CW4AGY3G.js → chunk-VI2JQPNQ.js} +2 -2
  105. package/dist/{chunk-OP6F6L3S.js → chunk-W5EG6LE3.js} +2 -2
  106. package/dist/{chunk-DEBHXHJR.js → chunk-WIPEXG6R.js} +2 -2
  107. package/dist/{chunk-SH6YO3LT.js → chunk-WVO4G37W.js} +2 -2
  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/secure-store/secure-fs.ts +78 -1
  304. package/src/storage/content-hash-index.ts +43 -17
  305. package/src/storage/tombstone-blocked-capture-index.ts +562 -0
  306. package/src/storage/tombstone-blocked-capture-mutation.ts +284 -0
  307. package/src/storage/tombstone-blocked-capture-sync.ts +872 -0
  308. package/src/storage-fact-hash-index-defer.test.ts +2900 -57
  309. package/src/storage.ts +266 -265
  310. package/src/testing/subjects/tombstone-blocked-capture.test.ts +181 -0
  311. package/src/utils/serialize-mutations.test.ts +4 -1
  312. package/src/utils/serialize-mutations.ts +26 -27
  313. package/dist/chunk-55MDBZLC.js +0 -490
  314. package/dist/chunk-55MDBZLC.js.map +0 -1
  315. package/dist/chunk-AIK5W3IW.js.map +0 -1
  316. package/dist/chunk-JMQUBRV6.js.map +0 -1
  317. package/dist/chunk-WL5EBRDH.js.map +0 -1
  318. package/dist/purge-YSGX5BOG.js.map +0 -1
  319. /package/dist/{chunk-FZRQZW4H.js.map → chunk-2EUVEDF5.js.map} +0 -0
  320. /package/dist/{chunk-3JNDOAQ2.js.map → chunk-5W4ZLBEZ.js.map} +0 -0
  321. /package/dist/{chunk-5ZCUDWD4.js.map → chunk-62U4J2GN.js.map} +0 -0
  322. /package/dist/{chunk-IGPEJVBY.js.map → chunk-66772WBP.js.map} +0 -0
  323. /package/dist/{chunk-EP5BVCRI.js.map → chunk-6BNE62PM.js.map} +0 -0
  324. /package/dist/{chunk-AAP5T6QG.js.map → chunk-6DNMUYEE.js.map} +0 -0
  325. /package/dist/{chunk-35DL7IOI.js.map → chunk-6FUPCJUL.js.map} +0 -0
  326. /package/dist/{chunk-JP3YPDAW.js.map → chunk-6U7F52DE.js.map} +0 -0
  327. /package/dist/{chunk-E4OCKU67.js.map → chunk-7XC2HX75.js.map} +0 -0
  328. /package/dist/{chunk-OVRQLAYR.js.map → chunk-AZ5BTEKX.js.map} +0 -0
  329. /package/dist/{chunk-2AXMQWCN.js.map → chunk-CZRYLSSW.js.map} +0 -0
  330. /package/dist/{chunk-PTSNLEXK.js.map → chunk-FDAMS4PK.js.map} +0 -0
  331. /package/dist/{chunk-2GXM5N5A.js.map → chunk-GIBMDSJ2.js.map} +0 -0
  332. /package/dist/{chunk-E5OBR6CJ.js.map → chunk-H5CPS5JP.js.map} +0 -0
  333. /package/dist/{chunk-2D7FEUCK.js.map → chunk-I5NLEENJ.js.map} +0 -0
  334. /package/dist/{chunk-DRBXHRXE.js.map → chunk-IB26HVAJ.js.map} +0 -0
  335. /package/dist/{chunk-QDKK63Y7.js.map → chunk-IC6CBHCA.js.map} +0 -0
  336. /package/dist/{chunk-IAEAFLF2.js.map → chunk-JCBWCYEJ.js.map} +0 -0
  337. /package/dist/{chunk-3PDOVUHC.js.map → chunk-KXB4VXHY.js.map} +0 -0
  338. /package/dist/{chunk-WU7TTVOD.js.map → chunk-LBOD4U5B.js.map} +0 -0
  339. /package/dist/{chunk-UBKEQOTD.js.map → chunk-LGHKJABJ.js.map} +0 -0
  340. /package/dist/{chunk-VSURVXKY.js.map → chunk-M3C2V3PV.js.map} +0 -0
  341. /package/dist/{chunk-OFHFH655.js.map → chunk-MJCGLM25.js.map} +0 -0
  342. /package/dist/{chunk-OYJKSZIQ.js.map → chunk-NHWB6HG2.js.map} +0 -0
  343. /package/dist/{chunk-TC3VNKGX.js.map → chunk-NM2WP3B7.js.map} +0 -0
  344. /package/dist/{chunk-GAWOEQEQ.js.map → chunk-P5PXWTW7.js.map} +0 -0
  345. /package/dist/{chunk-2QMKMVHH.js.map → chunk-QNWXCJ76.js.map} +0 -0
  346. /package/dist/{chunk-WRFBW2TT.js.map → chunk-SPJXKAVF.js.map} +0 -0
  347. /package/dist/{chunk-HKJCMOT6.js.map → chunk-TH2LSRMG.js.map} +0 -0
  348. /package/dist/{chunk-CW4AGY3G.js.map → chunk-VI2JQPNQ.js.map} +0 -0
  349. /package/dist/{chunk-OP6F6L3S.js.map → chunk-W5EG6LE3.js.map} +0 -0
  350. /package/dist/{chunk-DEBHXHJR.js.map → chunk-WIPEXG6R.js.map} +0 -0
  351. /package/dist/{chunk-SH6YO3LT.js.map → chunk-WVO4G37W.js.map} +0 -0
  352. /package/dist/{chunk-7L45JFPW.js.map → chunk-YTQNQ4KB.js.map} +0 -0
  353. /package/dist/{chunk-PKRIQL3V.js.map → chunk-YTSNAUXN.js.map} +0 -0
  354. /package/dist/{chunk-5QDRGWT2.js.map → chunk-ZPSVBSOF.js.map} +0 -0
  355. /package/dist/{forget-HRPLVB3W.js.map → forget-4UY2EOKH.js.map} +0 -0
@@ -0,0 +1,562 @@
1
+ import { execFile } from "node:child_process";
2
+ import { randomUUID } from "node:crypto";
3
+ import { mkdir, readFile, readdir, rename, stat, unlink, writeFile } from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { promisify } from "node:util";
6
+ import { log } from "../logger.js";
7
+ import type { MemoryFile, MemoryFrontmatter } from "../types.js";
8
+ import { RECALL_FALLBACK_DIRS } from "../utils/category-dir.js";
9
+ import { isErrnoCode } from "../utils/errno.js";
10
+ import { withHeldFileLock } from "../utils/serialize-mutations.js";
11
+ import {
12
+ CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS,
13
+ ContentHashIndex,
14
+ type ContentHashIndexLockOptions,
15
+ } from "./content-hash-index.js";
16
+ import {
17
+ buildExplicitCaptureDedupKey,
18
+ TombstoneBlockedCaptureWriteLock,
19
+ } from "./tombstone-blocked-capture-mutation.js";
20
+ import type { OfflineSyncMemoryParser } from "./tombstone-blocked-capture-sync.js";
21
+ export {
22
+ buildExplicitCaptureDedupKey,
23
+ isTombstoneBlockedCaptureWriteLockBusy,
24
+ normalizeExplicitCaptureContent,
25
+ } from "./tombstone-blocked-capture-mutation.js";
26
+ export {
27
+ parseTombstoneBlockedOfflineSyncMemory,
28
+ type OfflineSyncMemoryParser,
29
+ TombstoneBlockedCaptureIndexHost,
30
+ } from "./tombstone-blocked-capture-sync.js";
31
+
32
+ const REBUILD_MAX_ATTEMPTS = 3;
33
+ const execFileAsync = promisify(execFile);
34
+ const REBUILD_RETRY_BASE_MS = 50;
35
+
36
+ const ABANDONED_MARKER_MIN_AGE_MS = 60_000;
37
+ const MARKER_PROCESS_START_TOLERANCE_MS = 2_000;
38
+ const DOTNET_UNIX_EPOCH_TICKS = 621_355_968_000_000_000;
39
+ const MARKER_PROCESS_STARTED_AT_MS = Date.now() - process.uptime() * 1000;
40
+
41
+
42
+ type RebuildMarker = {
43
+ path: string;
44
+ committed: boolean;
45
+ pid?: number;
46
+ ownerId?: string;
47
+ createdAt?: number;
48
+ processStartedAtMs?: number;
49
+ malformed?: boolean;
50
+ };
51
+
52
+
53
+ export type TombstoneBlockedCaptureIndexOptions = {
54
+ readonly stateDir: string;
55
+ readonly memoryDir: string;
56
+ readonly secureStoreKeyProvider: () => Buffer | null;
57
+ readonly secureStoreWriteKeyProvider: () => Buffer | null;
58
+ readonly lockOptions: () => ContentHashIndexLockOptions;
59
+ readonly readAllMemories: () => Promise<MemoryFile[]>;
60
+ readonly readAllColdMemories: () => Promise<MemoryFile[]>;
61
+ readonly parseMemory?: OfflineSyncMemoryParser;
62
+ readonly withHeldFileLock?: typeof withHeldFileLock;
63
+ };
64
+
65
+ /**
66
+ * Durable targeted deduplication for tombstone-blocked explicit captures.
67
+ *
68
+ * The active fact-hash index intentionally excludes blocked rows. This small
69
+ * companion index keeps the blocked identity visible without making every
70
+ * authoritative active-index miss scan the full corpus.
71
+ */
72
+ export class TombstoneBlockedCaptureIndex {
73
+ private readonly options: TombstoneBlockedCaptureIndexOptions;
74
+ private index: ContentHashIndex | null = null;
75
+ private loadPromise: Promise<ContentHashIndex> | null = null;
76
+ private refreshPromise: Promise<ContentHashIndex> | null = null;
77
+ private authoritative = false;
78
+ private readonly captureWriteLock: TombstoneBlockedCaptureWriteLock;
79
+
80
+ constructor(options: TombstoneBlockedCaptureIndexOptions) {
81
+ this.options = options;
82
+ this.captureWriteLock = new TombstoneBlockedCaptureWriteLock(options);
83
+ }
84
+
85
+ private createIndex(): ContentHashIndex {
86
+ return new ContentHashIndex(
87
+ path.join(this.options.stateDir, "tombstone-blocked-capture"),
88
+ this.options.secureStoreKeyProvider,
89
+ this.options.secureStoreWriteKeyProvider,
90
+ this.options.memoryDir,
91
+ this.options.lockOptions()
92
+ );
93
+ }
94
+
95
+ private indexPath(): string {
96
+ return path.join(this.options.stateDir, "tombstone-blocked-capture", "fact-hashes.txt");
97
+ }
98
+
99
+ private rebuildMarkerPath(): string {
100
+ return path.join(this.options.stateDir, "tombstone-blocked-capture", "rebuild-required");
101
+ }
102
+
103
+ private async readRebuildMarkers(): Promise<RebuildMarker[]> {
104
+ try {
105
+ const entries = (await readdir(this.rebuildMarkerPath())).filter((entry) => !entry.startsWith("."));
106
+ return (
107
+ await Promise.all(
108
+ entries.map(async (entry) => {
109
+ const markerPath = path.join(this.rebuildMarkerPath(), entry);
110
+ try {
111
+ const raw = (await readFile(markerPath, "utf8")).trim();
112
+ if (raw === "committed") return { path: markerPath, committed: true };
113
+ let value: Record<string, unknown>;
114
+ let malformed = false;
115
+ try {
116
+ const parsed = JSON.parse(raw) as unknown;
117
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
118
+ malformed = true;
119
+ value = {};
120
+ } else {
121
+ value = parsed as Record<string, unknown>;
122
+ }
123
+ } catch {
124
+ malformed = true;
125
+ value = {};
126
+ }
127
+ const state = value.state;
128
+ if (state !== "pending" && state !== "committed") malformed = true;
129
+ const markerStat = await stat(markerPath).catch(() => null);
130
+ // A torn payload can lose its JSON metadata, but the atomic
131
+ // marker filename and filesystem mtime still identify its owner
132
+ // and age. Preserve both so stale malformed markers can be
133
+ // reaped without treating a fresh pre-commit gap as abandoned.
134
+ const createdAt =
135
+ typeof value.createdAt === "number" && Number.isFinite(value.createdAt)
136
+ ? value.createdAt
137
+ : markerStat?.mtimeMs;
138
+ if (typeof value.pid !== "number" || !Number.isInteger(value.pid)) malformed = true;
139
+ const ownerId =
140
+ typeof value.ownerId === "string" && value.ownerId.length > 0
141
+ ? value.ownerId
142
+ : path.basename(markerPath);
143
+ if (typeof value.ownerId !== "string" || value.ownerId.length === 0) malformed = true;
144
+ const processStartedAtMs =
145
+ typeof value.processStartedAtMs === "number" &&
146
+ Number.isFinite(value.processStartedAtMs) &&
147
+ value.processStartedAtMs > 0
148
+ ? value.processStartedAtMs
149
+ : undefined;
150
+ return {
151
+ path: markerPath,
152
+ committed: state === "committed",
153
+ ...(typeof value.pid === "number" && Number.isInteger(value.pid) ? { pid: value.pid } : {}),
154
+ ...(ownerId.length > 0 ? { ownerId } : {}),
155
+ ...(typeof createdAt === "number" && Number.isFinite(createdAt) ? { createdAt } : {}),
156
+ ...(processStartedAtMs === undefined ? {} : { processStartedAtMs }),
157
+ ...(malformed ? { malformed: true } : {}),
158
+ };
159
+ } catch (err) {
160
+ if (isErrnoCode(err, "ENOENT")) return null;
161
+ throw err;
162
+ }
163
+ })
164
+ )
165
+ ).filter((marker): marker is RebuildMarker => marker !== null);
166
+ } catch (err) {
167
+ if (isErrnoCode(err, "ENOENT")) return [];
168
+ throw err;
169
+ }
170
+ }
171
+ private async isAbandonedMarker(marker: RebuildMarker): Promise<boolean> {
172
+ if (marker.committed || marker.createdAt === undefined) return false;
173
+ if (Date.now() - marker.createdAt < ABANDONED_MARKER_MIN_AGE_MS) return false;
174
+ if (marker.malformed) return true;
175
+ return marker.pid !== undefined && !(await this.isProcessAlive(marker.pid, marker.processStartedAtMs));
176
+ }
177
+
178
+ private async isProcessAlive(pid: number, processStartedAtMs?: number): Promise<boolean> {
179
+ try {
180
+ process.kill(pid, 0);
181
+ } catch (err) {
182
+ return !isErrnoCode(err, "ESRCH");
183
+ }
184
+ if (processStartedAtMs === undefined) return true;
185
+ const runningStartedAtMs = await this.readProcessStartedAtMs(pid);
186
+ if (runningStartedAtMs === null) return true;
187
+ return runningStartedAtMs <= processStartedAtMs + MARKER_PROCESS_START_TOLERANCE_MS;
188
+ }
189
+ private async readProcessStartedAtMs(pid: number): Promise<number | null> {
190
+ try {
191
+ if (process.platform === "win32") {
192
+ const { stdout } = await execFileAsync(
193
+ "powershell.exe",
194
+ [
195
+ "-NoLogo",
196
+ "-NoProfile",
197
+ "-NonInteractive",
198
+ "-Command",
199
+ `(Get-Process -Id ${pid} -ErrorAction Stop).StartTime.ToUniversalTime().Ticks`,
200
+ ],
201
+ { encoding: "utf8", timeout: 1_000 }
202
+ );
203
+ const ticks = Number(stdout.trim());
204
+ return Number.isFinite(ticks) ? (ticks - DOTNET_UNIX_EPOCH_TICKS) / 10_000 : null;
205
+ }
206
+ const { stdout } = await execFileAsync("ps", ["-p", String(pid), "-o", "lstart="], {
207
+ encoding: "utf8",
208
+ timeout: 1_000,
209
+ });
210
+ const output = stdout.trim();
211
+ if (!output) return null;
212
+ const startedAtMs = Date.parse(output);
213
+ return Number.isFinite(startedAtMs) ? startedAtMs : null;
214
+ } catch {
215
+ return null;
216
+ }
217
+ }
218
+
219
+ private async getClearableRebuildMarkerPaths(markers: readonly RebuildMarker[]): Promise<string[]> {
220
+ const paths = await Promise.all(
221
+ markers.map(async (marker) => (marker.committed || (await this.isAbandonedMarker(marker)) ? marker.path : null))
222
+ );
223
+ return paths.filter((markerPath): markerPath is string => markerPath !== null);
224
+ }
225
+
226
+ private async getRebuildMarkers(): Promise<RebuildMarker[]> {
227
+ return await this.readRebuildMarkers();
228
+ }
229
+
230
+ // A pending marker protects the pre-commit gap; only committed markers are safe for another writer to clear.
231
+ private async writeMarkerAtomically(markerPath: string, payload: string): Promise<void> {
232
+ const temporaryPath = `${markerPath}.tmp-${process.pid}-${randomUUID()}`;
233
+ try {
234
+ await writeFile(temporaryPath, payload, "utf8");
235
+ await rename(temporaryPath, markerPath);
236
+ } finally {
237
+ await unlink(temporaryPath).catch((err: unknown) => {
238
+ if (!isErrnoCode(err, "ENOENT")) throw err;
239
+ });
240
+ }
241
+ }
242
+
243
+ private async markRebuildRequired(committed = false): Promise<string> {
244
+ const markerDir = this.rebuildMarkerPath();
245
+ await mkdir(markerDir, { recursive: true });
246
+ const ownerId = randomUUID();
247
+ const markerPath = path.join(markerDir, ownerId);
248
+ await this.writeMarkerAtomically(
249
+ markerPath,
250
+ `${JSON.stringify({
251
+ state: committed ? "committed" : "pending",
252
+ pid: process.pid,
253
+ ownerId,
254
+ createdAt: Date.now(),
255
+ processStartedAtMs: MARKER_PROCESS_STARTED_AT_MS,
256
+ })}\n`
257
+ );
258
+ return markerPath;
259
+ }
260
+
261
+ private async markRebuildCommitted(markerPath: string): Promise<void> {
262
+ await this.writeMarkerAtomically(
263
+ markerPath,
264
+ `${JSON.stringify({
265
+ state: "committed",
266
+ pid: process.pid,
267
+ ownerId: path.basename(markerPath),
268
+ createdAt: Date.now(),
269
+ processStartedAtMs: MARKER_PROCESS_STARTED_AT_MS,
270
+ })}\n`
271
+ );
272
+ }
273
+
274
+ private async clearRebuildRequired(markerPaths: readonly string[]): Promise<void> {
275
+ await Promise.all(
276
+ markerPaths.map(async (markerPath) => {
277
+ try {
278
+ await unlink(markerPath);
279
+ } catch (err) {
280
+ if (!isErrnoCode(err, "ENOENT")) throw err;
281
+ }
282
+ })
283
+ );
284
+ }
285
+
286
+ private async hasRebuildRequired(excludedMarkers: readonly string[] = []): Promise<boolean> {
287
+ const excluded = new Set(excludedMarkers);
288
+ return (await this.getRebuildMarkers()).some((marker) => !excluded.has(marker.path));
289
+ }
290
+
291
+ private async setAuthoritative(rebuilt: boolean): Promise<void> {
292
+ this.authoritative = rebuilt && !(await this.getRebuildMarkers()).some((marker) => !marker.committed);
293
+ }
294
+ private isBlocked(memory: MemoryFile): boolean {
295
+ return memory.frontmatter.status === "pending_review" && Boolean(memory.frontmatter.blockedBy);
296
+ }
297
+
298
+ private async rebuild(index: ContentHashIndex): Promise<boolean> {
299
+ return await index.rebuildUnderLock(async () => {
300
+ index.clear();
301
+ const existing = [...(await this.options.readAllMemories()), ...(await this.options.readAllColdMemories())];
302
+ for (const memory of existing) {
303
+ if (!this.isBlocked(memory)) continue;
304
+ index.add(
305
+ buildExplicitCaptureDedupKey(memory.content, memory.frontmatter.category, memory.frontmatter.sourceConnector)
306
+ );
307
+ }
308
+ });
309
+ }
310
+
311
+ private async hasPersistedIndexOrMarkers(): Promise<boolean> {
312
+ try {
313
+ await stat(this.indexPath());
314
+ return true;
315
+ } catch (err) {
316
+ if (!isErrnoCode(err, "ENOENT")) throw err;
317
+ return (await this.readRebuildMarkers()).length > 0;
318
+ }
319
+ }
320
+
321
+ private async getIndex(): Promise<ContentHashIndex> {
322
+ if (this.index) return this.index;
323
+ if (!this.loadPromise) {
324
+ const index = this.createIndex();
325
+ this.loadPromise = (async () => {
326
+ await index.load();
327
+ const rebuildMarkers = await this.getRebuildMarkers();
328
+ const clearableRebuildMarkers = await this.getClearableRebuildMarkerPaths(rebuildMarkers);
329
+ let persisted = true;
330
+ try {
331
+ await stat(this.indexPath());
332
+ if (rebuildMarkers.length > 0) persisted = false;
333
+ } catch (err) {
334
+ if (isErrnoCode(err, "ENOENT")) persisted = false;
335
+ else throw err;
336
+ }
337
+ if (!persisted) {
338
+ const lockOptions = this.options.lockOptions();
339
+ const maxAttempts = lockOptions.retryMaxAttempts ?? REBUILD_MAX_ATTEMPTS;
340
+ const baseMs = lockOptions.retryBaseMs ?? REBUILD_RETRY_BASE_MS;
341
+ let rebuilt = false;
342
+ for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
343
+ rebuilt = await this.rebuild(index);
344
+ if (rebuilt || attempt === maxAttempts - 1) break;
345
+ const wait = Math.min(baseMs * 2 ** attempt, CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS);
346
+ const delayState = Promise.withResolvers<void>();
347
+ setTimeout(delayState.resolve, wait);
348
+ await delayState.promise;
349
+ }
350
+ if (!rebuilt) {
351
+ throw new Error("tombstone-blocked capture index rebuild lock unavailable");
352
+ }
353
+ await this.clearRebuildRequired(clearableRebuildMarkers);
354
+ }
355
+ await this.setAuthoritative(true);
356
+ this.index = index;
357
+ return index;
358
+ })().catch((err) => {
359
+ this.loadPromise = null;
360
+ throw err;
361
+ });
362
+ }
363
+ return this.loadPromise;
364
+ }
365
+
366
+ async withCaptureWriteLock<T>(task: () => Promise<T>, identity?: string | readonly string[]): Promise<T> {
367
+ return await this.captureWriteLock.withCaptureWriteLock(task, identity);
368
+ }
369
+
370
+ async withCaptureWriteLockHashes<T>(task: () => Promise<T>, identityHashes: readonly string[]): Promise<T> {
371
+ return await this.captureWriteLock.withCaptureWriteLockHashes(task, identityHashes);
372
+ }
373
+ async withCaptureWriteLockAndHashes<T>(
374
+ task: () => Promise<T>,
375
+ identity: string | readonly string[] | undefined,
376
+ identityHashes: readonly string[]
377
+ ): Promise<T> {
378
+ return await this.captureWriteLock.withCaptureWriteLockAndHashes(task, identity, identityHashes);
379
+ }
380
+
381
+ private async reload(): Promise<ContentHashIndex> {
382
+ if (!this.refreshPromise) {
383
+ this.refreshPromise = (async () => {
384
+ this.index = null;
385
+ this.loadPromise = null;
386
+ return await this.getIndex();
387
+ })().finally(() => {
388
+ this.refreshPromise = null;
389
+ });
390
+ }
391
+ return this.refreshPromise;
392
+ }
393
+
394
+ /** Return blocked-row membership and authority from one index snapshot. */
395
+ async check(
396
+ content: string,
397
+ category: string,
398
+ sourceConnector?: string
399
+ ): Promise<{ has: boolean; authoritative: boolean }> {
400
+ const key = buildExplicitCaptureDedupKey(content, category, sourceConnector);
401
+ let index = await this.getIndex();
402
+ if ((await this.hasRebuildRequired()) || !(await index.isDiskFingerprintCurrent())) {
403
+ index = await this.reload();
404
+ }
405
+ return { has: index.has(key), authoritative: this.authoritative };
406
+ }
407
+
408
+ /** Return whether a blocked row matches this explicit-capture identity. */
409
+ async has(content: string, category: string, sourceConnector?: string): Promise<boolean> {
410
+ return (await this.check(content, category, sourceConnector)).has;
411
+ }
412
+
413
+ /** Return whether the in-memory index is safe to answer an authoritative miss. */
414
+ async isAuthoritative(): Promise<boolean> {
415
+ const index = await this.getIndex();
416
+ if ((await this.hasRebuildRequired()) || !(await index.isDiskFingerprintCurrent())) {
417
+ await this.reload();
418
+ }
419
+ return this.authoritative;
420
+ }
421
+
422
+ /** Reserve durable rebuild intent before a blocked memory file is committed. */
423
+ async prepareWrite(): Promise<string> {
424
+ return this.markRebuildRequired();
425
+ }
426
+
427
+ /** Mark a prepared write committed once its memory file is durable. */
428
+ async commitWrite(rebuildMarker: string): Promise<void> {
429
+ await this.markRebuildCommitted(rebuildMarker);
430
+ }
431
+
432
+ /** Drop a pending marker when the protected memory write never commits. */
433
+ async discardWrite(rebuildMarker: string): Promise<void> {
434
+ await this.clearRebuildRequired([rebuildMarker]);
435
+ }
436
+
437
+ /** Add a newly persisted blocked row to the durable targeted index. */
438
+ async add(memory: MemoryFile, rebuildMarker?: string): Promise<void> {
439
+ if (!this.isBlocked(memory)) return;
440
+ let index = await this.getIndex();
441
+ if (
442
+ (await this.hasRebuildRequired(rebuildMarker ? [rebuildMarker] : [])) ||
443
+ !(await index.isDiskFingerprintCurrent())
444
+ ) {
445
+ index = await this.reload();
446
+ }
447
+ index.add(
448
+ buildExplicitCaptureDedupKey(memory.content, memory.frontmatter.category, memory.frontmatter.sourceConnector)
449
+ );
450
+ const marker = rebuildMarker ?? (await this.markRebuildRequired(true));
451
+ if (rebuildMarker) await this.markRebuildCommitted(rebuildMarker);
452
+ await index.saveMergingWithDisk();
453
+ await index.flushReconcileRetry();
454
+ if (index.hasPendingChanges) {
455
+ this.markUntrusted();
456
+ return;
457
+ }
458
+ await this.clearRebuildRequired([marker]);
459
+ await this.setAuthoritative(true);
460
+ }
461
+
462
+ /** Rebuild the index after a blocked row changes or is removed. */
463
+ async rebuildIfLoaded(ownedMarker?: string): Promise<void> {
464
+ const index = this.index ?? ((await this.hasPersistedIndexOrMarkers()) ? this.getIndex() : null);
465
+ if (!index) return;
466
+ const rebuildMarkers = await this.getRebuildMarkers();
467
+ const rebuildMarker = await this.markRebuildRequired(true);
468
+ const clearableRebuildMarkers = await this.getClearableRebuildMarkerPaths(rebuildMarkers);
469
+ const rebuilt = await this.rebuild(await index);
470
+ if (rebuilt) {
471
+ await this.clearRebuildRequired([
472
+ ...clearableRebuildMarkers,
473
+ ...(ownedMarker === undefined || clearableRebuildMarkers.includes(ownedMarker) ? [] : [ownedMarker]),
474
+ rebuildMarker,
475
+ ]);
476
+ }
477
+ await this.setAuthoritative(rebuilt);
478
+ }
479
+
480
+ /** Rebuild when either side of a write is blocked and its identity changed. */
481
+ async sync(before: MemoryFile, after: MemoryFile, rebuildMarker?: string): Promise<void> {
482
+ const beforeBlocked = this.isBlocked(before);
483
+ const afterBlocked = this.isBlocked(after);
484
+ if (!beforeBlocked && !afterBlocked) return;
485
+ const beforeKey = buildExplicitCaptureDedupKey(
486
+ before.content,
487
+ before.frontmatter.category,
488
+ before.frontmatter.sourceConnector
489
+ );
490
+ const afterKey = buildExplicitCaptureDedupKey(
491
+ after.content,
492
+ after.frontmatter.category,
493
+ after.frontmatter.sourceConnector
494
+ );
495
+ if (beforeBlocked && afterBlocked && beforeKey === afterKey) {
496
+ if (rebuildMarker) {
497
+ await this.markRebuildCommitted(rebuildMarker);
498
+ await this.clearRebuildRequired([rebuildMarker]);
499
+ }
500
+ return;
501
+ }
502
+ const existingMarkers = await this.getRebuildMarkers();
503
+ const marker = rebuildMarker ?? (await this.markRebuildRequired(true));
504
+ const clearableExistingMarkers = await this.getClearableRebuildMarkerPaths(existingMarkers);
505
+ if (rebuildMarker) await this.markRebuildCommitted(rebuildMarker);
506
+ const rebuilt = await this.rebuild(await this.getIndex());
507
+ if (rebuilt) {
508
+ await this.clearRebuildRequired([
509
+ ...clearableExistingMarkers,
510
+ marker,
511
+ ]);
512
+ }
513
+ await this.setAuthoritative(rebuilt);
514
+ }
515
+ markUntrusted(): void {
516
+ this.authoritative = false;
517
+ }
518
+
519
+ private async failOpen(operation: string, action: () => Promise<void>): Promise<void> {
520
+ try {
521
+ await action();
522
+ } catch (err) {
523
+ this.markUntrusted();
524
+ log.warn(`storage.${operation} completed but failed to update tombstone-blocked capture index: ${err}`);
525
+ }
526
+ }
527
+
528
+ async addWrittenMemory(
529
+ pathname: string,
530
+ frontmatter: MemoryFrontmatter,
531
+ content: string,
532
+ rebuildMarker?: string
533
+ ): Promise<void> {
534
+ await this.failOpen("writeMemory", () => this.add({ path: pathname, frontmatter, content }, rebuildMarker));
535
+ }
536
+
537
+ async rebuildAfterInvalidation(ownedMarker?: string): Promise<void> {
538
+ await this.failOpen("invalidateMemory", () => this.rebuildIfLoaded(ownedMarker));
539
+ }
540
+
541
+ async syncUpdatedMemory(
542
+ before: MemoryFile,
543
+ frontmatter: MemoryFrontmatter,
544
+ content: string,
545
+ rebuildMarker?: string
546
+ ): Promise<void> {
547
+ await this.failOpen("updateMemory", () => this.sync(before, { ...before, frontmatter, content }, rebuildMarker));
548
+ }
549
+
550
+ async syncUpdatedFrontmatter(
551
+ before: MemoryFile,
552
+ frontmatter: MemoryFrontmatter,
553
+ rebuildMarker?: string
554
+ ): Promise<void> {
555
+ await this.failOpen("writeMemoryFrontmatter", () => this.sync(before, { ...before, frontmatter }, rebuildMarker));
556
+ }
557
+ shouldRebuildAfterInvalidationForPath(filePath: string): boolean {
558
+ const relative = path.relative(path.resolve(this.options.memoryDir), path.resolve(filePath));
559
+ const category = relative.split(path.sep)[0];
560
+ return category === "cold" || RECALL_FALLBACK_DIRS.includes(category);
561
+ }
562
+ }