@remnic/core 9.29.0 → 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 (359) 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.d.ts +7 -7
  36. package/dist/access-schema.js +3 -3
  37. package/dist/{access-service-DvPW_M5j.d.ts → access-service-B5RwVsTV.d.ts} +7 -7
  38. package/dist/access-service.d.ts +11 -11
  39. package/dist/access-service.js +19 -18
  40. package/dist/access-surface-catalog.d.ts +11 -11
  41. package/dist/access-wearables-meetings-surface.d.ts +4 -3
  42. package/dist/action-confidence.d.ts +1 -1
  43. package/dist/active-memory-bridge.d.ts +1 -1
  44. package/dist/active-recall.d.ts +1 -1
  45. package/dist/active-recall.js +3 -3
  46. package/dist/behavior-learner.d.ts +1 -1
  47. package/dist/behavior-signals.d.ts +1 -1
  48. package/dist/bootstrap.d.ts +8 -8
  49. package/dist/briefing.d.ts +3 -2
  50. package/dist/briefing.js +10 -9
  51. package/dist/buffer-surprise-report.d.ts +1 -1
  52. package/dist/buffer.d.ts +3 -2
  53. package/dist/bulk-import/index.d.ts +3 -3
  54. package/dist/calibration.d.ts +1 -1
  55. package/dist/capabilities.d.ts +1 -1
  56. package/dist/{catalog-BIVgftWu.d.ts → catalog-V7inUQdp.d.ts} +1 -1
  57. package/dist/causal-behavior.d.ts +1 -1
  58. package/dist/causal-consolidation.d.ts +1 -1
  59. package/dist/causal-consolidation.js +11 -10
  60. package/dist/causal-consolidation.js.map +1 -1
  61. package/dist/causal-trajectory-graph.d.ts +1 -1
  62. package/dist/{chunk-KUJQHXPH.js → chunk-2EUVEDF5.js} +2 -2
  63. package/dist/{chunk-2XC73SHY.js → chunk-4WKA6I65.js} +20 -20
  64. package/dist/{chunk-2XC73SHY.js.map → chunk-4WKA6I65.js.map} +1 -1
  65. package/dist/{chunk-AIK5W3IW.js → chunk-5TFP4CUL.js} +68 -2
  66. package/dist/chunk-5TFP4CUL.js.map +1 -0
  67. package/dist/{chunk-EVX44I7C.js → chunk-5W4ZLBEZ.js} +9 -9
  68. package/dist/{chunk-P3MH72CI.js → chunk-62U4J2GN.js} +2 -2
  69. package/dist/{chunk-YGHMSDLK.js → chunk-66772WBP.js} +2 -2
  70. package/dist/{chunk-EP5BVCRI.js → chunk-6BNE62PM.js} +2 -2
  71. package/dist/{chunk-AAP5T6QG.js → chunk-6DNMUYEE.js} +2 -2
  72. package/dist/{chunk-SG265CVF.js → chunk-6FUPCJUL.js} +7 -7
  73. package/dist/{chunk-WZ7UB7XQ.js → chunk-6U7F52DE.js} +19 -19
  74. package/dist/{chunk-E4OCKU67.js → chunk-7XC2HX75.js} +2 -2
  75. package/dist/{chunk-OVRQLAYR.js → chunk-AZ5BTEKX.js} +2 -2
  76. package/dist/{chunk-2HNUMF5V.js → chunk-CZRYLSSW.js} +3 -3
  77. package/dist/chunk-FBUE6PD3.js +1769 -0
  78. package/dist/chunk-FBUE6PD3.js.map +1 -0
  79. package/dist/{chunk-WONS4RZ2.js → chunk-FDAMS4PK.js} +2 -2
  80. package/dist/{chunk-WL5EBRDH.js → chunk-FFAZ2XFU.js} +9 -8
  81. package/dist/chunk-FFAZ2XFU.js.map +1 -0
  82. package/dist/{chunk-Q6VFVAKZ.js → chunk-GIBMDSJ2.js} +2 -2
  83. package/dist/{chunk-ZXAGJFHJ.js → chunk-H5CPS5JP.js} +3 -3
  84. package/dist/{chunk-NBDCZWP4.js → chunk-I5NLEENJ.js} +2 -2
  85. package/dist/{chunk-DRBXHRXE.js → chunk-IB26HVAJ.js} +2 -2
  86. package/dist/{chunk-XEQ7OT7U.js → chunk-IC6CBHCA.js} +3 -3
  87. package/dist/{chunk-CMRQ34J5.js → chunk-JCBWCYEJ.js} +2 -2
  88. package/dist/{chunk-3PDOVUHC.js → chunk-KXB4VXHY.js} +2 -2
  89. package/dist/{chunk-G6LBWZRF.js → chunk-LBOD4U5B.js} +7 -7
  90. package/dist/{chunk-ZGOQTOYA.js → chunk-LGHKJABJ.js} +3 -3
  91. package/dist/{chunk-T3GCXULH.js → chunk-M3C2V3PV.js} +10 -10
  92. package/dist/{chunk-OFHFH655.js → chunk-MJCGLM25.js} +8 -8
  93. package/dist/{chunk-JDZRYH2N.js → chunk-NHWB6HG2.js} +2 -2
  94. package/dist/{chunk-TC3VNKGX.js → chunk-NM2WP3B7.js} +4 -4
  95. package/dist/{chunk-IMA2EWLV.js → chunk-P5PXWTW7.js} +2 -2
  96. package/dist/chunk-PH3ZWL3W.js +786 -0
  97. package/dist/chunk-PH3ZWL3W.js.map +1 -0
  98. package/dist/{chunk-QMBNMPQM.js → chunk-PUS63Q3G.js} +24 -22
  99. package/dist/{chunk-QMBNMPQM.js.map → chunk-PUS63Q3G.js.map} +1 -1
  100. package/dist/{chunk-2QMKMVHH.js → chunk-QNWXCJ76.js} +2 -2
  101. package/dist/{chunk-FCDWWCLX.js → chunk-RGIWINDN.js} +427 -795
  102. package/dist/chunk-RGIWINDN.js.map +1 -0
  103. package/dist/{chunk-CSMCNOPH.js → chunk-SPJXKAVF.js} +3 -3
  104. package/dist/{chunk-4PBVXX7L.js → chunk-TH2LSRMG.js} +2 -2
  105. package/dist/{chunk-ESBQWC3U.js → chunk-VI2JQPNQ.js} +2 -2
  106. package/dist/{chunk-GGUBDXRR.js → chunk-W5EG6LE3.js} +2 -2
  107. package/dist/{chunk-DEBHXHJR.js → chunk-WIPEXG6R.js} +2 -2
  108. package/dist/{chunk-SH6YO3LT.js → chunk-WVO4G37W.js} +2 -2
  109. package/dist/{chunk-VVIW3PTA.js → chunk-YTQNQ4KB.js} +14 -14
  110. package/dist/{chunk-PKRIQL3V.js → chunk-YTSNAUXN.js} +2 -2
  111. package/dist/{chunk-QAOGGNE3.js → chunk-ZPSVBSOF.js} +4 -4
  112. package/dist/{cli-CxoSOiG0.d.ts → cli-C4GbETPC.d.ts} +5 -5
  113. package/dist/cli.d.ts +13 -13
  114. package/dist/cli.js +36 -35
  115. package/dist/compounding/engine.d.ts +3 -2
  116. package/dist/compounding/engine.js +10 -9
  117. package/dist/compounding/preference-consolidator.d.ts +1 -1
  118. package/dist/compression-optimizer.d.ts +1 -1
  119. package/dist/config.d.ts +1 -1
  120. package/dist/config.js +3 -3
  121. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  122. package/dist/connectors/codex-materialize-runner.js +10 -9
  123. package/dist/connectors/codex-materialize.d.ts +1 -1
  124. package/dist/connectors/index.d.ts +1 -1
  125. package/dist/connectors/index.js +10 -9
  126. package/dist/consolidation-provenance-check.d.ts +3 -2
  127. package/dist/consolidation-undo.d.ts +3 -2
  128. package/dist/contradiction/index.d.ts +3 -2
  129. package/dist/conversation-index/backend.d.ts +1 -1
  130. package/dist/conversation-index/chunker.d.ts +1 -1
  131. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  132. package/dist/conversation-index/indexer.d.ts +1 -1
  133. package/dist/conversation-index/search.d.ts +1 -1
  134. package/dist/corpus-watermark.d.ts +1 -1
  135. package/dist/corpus-watermark.js +12 -11
  136. package/dist/day-summary.d.ts +1 -1
  137. package/dist/delinearize.d.ts +1 -1
  138. package/dist/direct-answer-wiring.d.ts +1 -1
  139. package/dist/direct-answer.d.ts +1 -1
  140. package/dist/embedding-fallback.d.ts +1 -1
  141. package/dist/enrichment/index.d.ts +1 -1
  142. package/dist/entity-retrieval.d.ts +3 -2
  143. package/dist/entity-retrieval.js +10 -9
  144. package/dist/entity-schema.d.ts +1 -1
  145. package/dist/explicit-capture.d.ts +52 -10
  146. package/dist/explicit-capture.js +13 -1
  147. package/dist/extraction-error-classification.d.ts +1 -1
  148. package/dist/extraction-faithfulness.d.ts +1 -1
  149. package/dist/extraction-judge-telemetry.d.ts +1 -1
  150. package/dist/extraction-judge-training.d.ts +1 -1
  151. package/dist/extraction-judge.d.ts +1 -1
  152. package/dist/extraction-liveness.d.ts +1 -1
  153. package/dist/extraction-source-grounding-rules.d.ts +1 -1
  154. package/dist/extraction-source-grounding.d.ts +1 -1
  155. package/dist/extraction.d.ts +1 -1
  156. package/dist/fallback-llm.d.ts +1 -1
  157. package/dist/{forget-HRPLVB3W.js → forget-4UY2EOKH.js} +3 -3
  158. package/dist/graph-dashboard-diff.d.ts +1 -1
  159. package/dist/graph-dashboard-key.d.ts +1 -1
  160. package/dist/graph-dashboard-parser.d.ts +1 -1
  161. package/dist/graph-edge-reinforcement.d.ts +1 -1
  162. package/dist/graph-snapshot.d.ts +1 -1
  163. package/dist/graph.d.ts +1 -1
  164. package/dist/importance.d.ts +1 -1
  165. package/dist/importers/index.d.ts +1 -1
  166. package/dist/in-flight-reads.d.ts +1 -1
  167. package/dist/index.d.ts +21 -21
  168. package/dist/index.js +47 -45
  169. package/dist/intent.d.ts +1 -1
  170. package/dist/lcm/engine.d.ts +1 -1
  171. package/dist/lcm/index.d.ts +1 -1
  172. package/dist/lcm/tools.d.ts +1 -1
  173. package/dist/lifecycle.d.ts +1 -1
  174. package/dist/live-connectors-runner.d.ts +1 -1
  175. package/dist/local-llm.d.ts +1 -1
  176. package/dist/local-model-endpoint.d.ts +1 -1
  177. package/dist/maintenance/memory-governance.d.ts +1 -1
  178. package/dist/maintenance/memory-governance.js +10 -9
  179. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
  180. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +10 -9
  181. package/dist/maintenance/rebuild-memory-projection.d.ts +3 -2
  182. package/dist/maintenance/rebuild-memory-projection.js +11 -10
  183. package/dist/{maintenance-Cb-PjxQd.d.ts → maintenance-CL30NWtc.d.ts} +3 -3
  184. package/dist/mcp-memory-inspector-app.d.ts +11 -11
  185. package/dist/memory-action-policy.d.ts +1 -1
  186. package/dist/memory-cache.d.ts +1 -1
  187. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  188. package/dist/memory-projection-store.d.ts +1 -1
  189. package/dist/memory-provenance.d.ts +1 -1
  190. package/dist/memory-worth-outcomes.d.ts +3 -2
  191. package/dist/models-json.d.ts +1 -1
  192. package/dist/namespaces/migrate.d.ts +4 -3
  193. package/dist/namespaces/migrate.js +11 -10
  194. package/dist/namespaces/principal.d.ts +1 -1
  195. package/dist/namespaces/search.d.ts +1 -1
  196. package/dist/namespaces/storage.d.ts +4 -3
  197. package/dist/namespaces/storage.js +10 -9
  198. package/dist/native-knowledge.d.ts +1 -1
  199. package/dist/offline-sync-impression-drain.d.ts +1 -1
  200. package/dist/offline-sync-impression-drain.js +5 -5
  201. package/dist/offline-sync.js +2 -2
  202. package/dist/operator-doctor-corpus.d.ts +1 -1
  203. package/dist/operator-doctor-corpus.js +13 -12
  204. package/dist/operator-doctor-replica.d.ts +1 -1
  205. package/dist/operator-toolkit.d.ts +4 -3
  206. package/dist/operator-toolkit.js +18 -17
  207. package/dist/orchestration/compression-guideline-coordinator.d.ts +3 -2
  208. package/dist/orchestration/maintenance.d.ts +5 -4
  209. package/dist/orchestration/maintenance.js +15 -14
  210. package/dist/{orchestrator-5GKqb_HL.d.ts → orchestrator-CKTCRwWV.d.ts} +7 -7
  211. package/dist/orchestrator.d.ts +8 -8
  212. package/dist/orchestrator.js +45 -44
  213. package/dist/patterns-cli.d.ts +1 -1
  214. package/dist/{pipeline-3Qi1TpRo.d.ts → pipeline-B7wj4rRZ.d.ts} +1 -1
  215. package/dist/policy-runtime.d.ts +1 -1
  216. package/dist/proactive-contention.d.ts +1 -1
  217. package/dist/provenance.d.ts +1 -1
  218. package/dist/{purge-YSGX5BOG.js → purge-533FZLS4.js} +16 -9
  219. package/dist/purge-533FZLS4.js.map +1 -0
  220. package/dist/qmd-preflight.d.ts +2 -2
  221. package/dist/{qmd-D9JqzzRX.d.ts → qmd-q9tg4dHp.d.ts} +1 -1
  222. package/dist/qmd-recall-cache.d.ts +1 -1
  223. package/dist/qmd.d.ts +2 -2
  224. package/dist/recall-concurrency-config.d.ts +1 -1
  225. package/dist/recall-disclosure-escalation.d.ts +1 -1
  226. package/dist/recall-explain-renderer.d.ts +1 -1
  227. package/dist/recall-memory-map.d.ts +1 -1
  228. package/dist/recall-planner-llm.d.ts +1 -1
  229. package/dist/recall-state.d.ts +1 -1
  230. package/dist/recall-state.js +2 -2
  231. package/dist/recall-tag-filter.d.ts +1 -1
  232. package/dist/recall-timings.d.ts +1 -1
  233. package/dist/recall-xray-cli.d.ts +1 -1
  234. package/dist/recall-xray-renderer.d.ts +1 -1
  235. package/dist/recall-xray.d.ts +1 -1
  236. package/dist/replay/normalizers/chatgpt.d.ts +1 -1
  237. package/dist/replay/normalizers/claude.d.ts +1 -1
  238. package/dist/replay/normalizers/openclaw.d.ts +1 -1
  239. package/dist/replay/normalizers/shared.d.ts +1 -1
  240. package/dist/replay/runner.d.ts +1 -1
  241. package/dist/replay/types.d.ts +1 -1
  242. package/dist/replica-divergence.d.ts +1 -1
  243. package/dist/replica-peers-config.d.ts +1 -1
  244. package/dist/resolve-auth-token.d.ts +1 -1
  245. package/dist/resume-bundles.js +4 -4
  246. package/dist/retrieval-agents.d.ts +2 -2
  247. package/dist/retrieval-tiers.d.ts +1 -1
  248. package/dist/routing/engine.d.ts +1 -1
  249. package/dist/routing/store.d.ts +1 -1
  250. package/dist/salvage-envelope.d.ts +1 -1
  251. package/dist/schemas.d.ts +22 -22
  252. package/dist/{scope-profiles-C4g6_UdP.d.ts → scope-profiles-CWDyglPM.d.ts} +1 -1
  253. package/dist/search/embed-helper.d.ts +1 -1
  254. package/dist/search/factory.d.ts +1 -1
  255. package/dist/search/index.d.ts +1 -1
  256. package/dist/search/lancedb-backend.d.ts +1 -1
  257. package/dist/search/meilisearch-backend.d.ts +1 -1
  258. package/dist/search/noop-backend.d.ts +1 -1
  259. package/dist/search/orama-backend.d.ts +1 -1
  260. package/dist/search/port.d.ts +1 -1
  261. package/dist/search/remote-backend.d.ts +1 -1
  262. package/dist/secure-store/index.js +2 -2
  263. package/dist/{semantic-consolidation-BrxqnzTW.d.ts → semantic-consolidation-BUE3deLn.d.ts} +1 -1
  264. package/dist/semantic-consolidation.d.ts +2 -2
  265. package/dist/semantic-consolidation.js +11 -10
  266. package/dist/semantic-rule-promotion.js +10 -9
  267. package/dist/semantic-rule-verifier.d.ts +1 -1
  268. package/dist/semantic-rule-verifier.js +10 -9
  269. package/dist/{service-Bi1JgxdP.d.ts → service-CXgM0bQG.d.ts} +2 -2
  270. package/dist/session-observer-bands.d.ts +1 -1
  271. package/dist/session-observer-state.d.ts +1 -1
  272. package/dist/shared-context/manager.d.ts +1 -1
  273. package/dist/signal.d.ts +1 -1
  274. package/dist/{storage-CcH6yPk2.d.ts → storage-DysJq-XH.d.ts} +163 -27
  275. package/dist/storage.d.ts +3 -2
  276. package/dist/storage.js +12 -10
  277. package/dist/summarizer.d.ts +1 -1
  278. package/dist/summarizer.js +3 -3
  279. package/dist/summary-snapshot.d.ts +1 -1
  280. package/dist/summary-snapshot.js +2 -2
  281. package/dist/temporal-supersession.d.ts +3 -2
  282. package/dist/temporal-timeline-recall.d.ts +1 -1
  283. package/dist/temporal-validity.d.ts +1 -1
  284. package/dist/threading.d.ts +1 -1
  285. package/dist/tier-migration.d.ts +3 -2
  286. package/dist/tier-routing.d.ts +1 -1
  287. package/dist/topics.d.ts +1 -1
  288. package/dist/transcript.d.ts +1 -1
  289. package/dist/transfer/types.d.ts +12 -12
  290. package/dist/trust-score-stage.d.ts +1 -1
  291. package/dist/trust-score.d.ts +1 -1
  292. package/dist/{types-Dg0w9ADr.d.ts → types-aPPzhnjT.d.ts} +1 -1
  293. package/dist/types.d.ts +1 -1
  294. package/dist/utility-runtime.d.ts +1 -1
  295. package/dist/utils/serialize-mutations.d.ts +11 -7
  296. package/dist/utils/serialize-mutations.js +1 -1
  297. package/dist/verified-recall.js +10 -9
  298. package/dist/write-envelope.d.ts +1 -1
  299. package/package.json +2 -2
  300. package/src/access-observe-write-surface.ts +1 -1
  301. package/src/access-service-coding-write.test.ts +40 -0
  302. package/src/explicit-capture.test.ts +1110 -1
  303. package/src/explicit-capture.ts +534 -135
  304. package/src/maintenance/purge.test.ts +157 -1
  305. package/src/maintenance/purge.ts +15 -9
  306. package/src/secure-store/secure-fs.ts +78 -1
  307. package/src/storage/content-hash-index.ts +43 -17
  308. package/src/storage/entity-canonical-id-migration.ts +151 -23
  309. package/src/storage/tombstone-blocked-capture-index.ts +562 -0
  310. package/src/storage/tombstone-blocked-capture-mutation.ts +284 -0
  311. package/src/storage/tombstone-blocked-capture-sync.ts +872 -0
  312. package/src/storage-fact-hash-index-defer.test.ts +2900 -57
  313. package/src/storage.ts +266 -265
  314. package/src/testing/subjects/tombstone-blocked-capture.test.ts +181 -0
  315. package/src/utils/serialize-mutations.test.ts +4 -1
  316. package/src/utils/serialize-mutations.ts +26 -27
  317. package/dist/chunk-55MDBZLC.js +0 -490
  318. package/dist/chunk-55MDBZLC.js.map +0 -1
  319. package/dist/chunk-AIK5W3IW.js.map +0 -1
  320. package/dist/chunk-FCDWWCLX.js.map +0 -1
  321. package/dist/chunk-WL5EBRDH.js.map +0 -1
  322. package/dist/purge-YSGX5BOG.js.map +0 -1
  323. /package/dist/{chunk-KUJQHXPH.js.map → chunk-2EUVEDF5.js.map} +0 -0
  324. /package/dist/{chunk-EVX44I7C.js.map → chunk-5W4ZLBEZ.js.map} +0 -0
  325. /package/dist/{chunk-P3MH72CI.js.map → chunk-62U4J2GN.js.map} +0 -0
  326. /package/dist/{chunk-YGHMSDLK.js.map → chunk-66772WBP.js.map} +0 -0
  327. /package/dist/{chunk-EP5BVCRI.js.map → chunk-6BNE62PM.js.map} +0 -0
  328. /package/dist/{chunk-AAP5T6QG.js.map → chunk-6DNMUYEE.js.map} +0 -0
  329. /package/dist/{chunk-SG265CVF.js.map → chunk-6FUPCJUL.js.map} +0 -0
  330. /package/dist/{chunk-WZ7UB7XQ.js.map → chunk-6U7F52DE.js.map} +0 -0
  331. /package/dist/{chunk-E4OCKU67.js.map → chunk-7XC2HX75.js.map} +0 -0
  332. /package/dist/{chunk-OVRQLAYR.js.map → chunk-AZ5BTEKX.js.map} +0 -0
  333. /package/dist/{chunk-2HNUMF5V.js.map → chunk-CZRYLSSW.js.map} +0 -0
  334. /package/dist/{chunk-WONS4RZ2.js.map → chunk-FDAMS4PK.js.map} +0 -0
  335. /package/dist/{chunk-Q6VFVAKZ.js.map → chunk-GIBMDSJ2.js.map} +0 -0
  336. /package/dist/{chunk-ZXAGJFHJ.js.map → chunk-H5CPS5JP.js.map} +0 -0
  337. /package/dist/{chunk-NBDCZWP4.js.map → chunk-I5NLEENJ.js.map} +0 -0
  338. /package/dist/{chunk-DRBXHRXE.js.map → chunk-IB26HVAJ.js.map} +0 -0
  339. /package/dist/{chunk-XEQ7OT7U.js.map → chunk-IC6CBHCA.js.map} +0 -0
  340. /package/dist/{chunk-CMRQ34J5.js.map → chunk-JCBWCYEJ.js.map} +0 -0
  341. /package/dist/{chunk-3PDOVUHC.js.map → chunk-KXB4VXHY.js.map} +0 -0
  342. /package/dist/{chunk-G6LBWZRF.js.map → chunk-LBOD4U5B.js.map} +0 -0
  343. /package/dist/{chunk-ZGOQTOYA.js.map → chunk-LGHKJABJ.js.map} +0 -0
  344. /package/dist/{chunk-T3GCXULH.js.map → chunk-M3C2V3PV.js.map} +0 -0
  345. /package/dist/{chunk-OFHFH655.js.map → chunk-MJCGLM25.js.map} +0 -0
  346. /package/dist/{chunk-JDZRYH2N.js.map → chunk-NHWB6HG2.js.map} +0 -0
  347. /package/dist/{chunk-TC3VNKGX.js.map → chunk-NM2WP3B7.js.map} +0 -0
  348. /package/dist/{chunk-IMA2EWLV.js.map → chunk-P5PXWTW7.js.map} +0 -0
  349. /package/dist/{chunk-2QMKMVHH.js.map → chunk-QNWXCJ76.js.map} +0 -0
  350. /package/dist/{chunk-CSMCNOPH.js.map → chunk-SPJXKAVF.js.map} +0 -0
  351. /package/dist/{chunk-4PBVXX7L.js.map → chunk-TH2LSRMG.js.map} +0 -0
  352. /package/dist/{chunk-ESBQWC3U.js.map → chunk-VI2JQPNQ.js.map} +0 -0
  353. /package/dist/{chunk-GGUBDXRR.js.map → chunk-W5EG6LE3.js.map} +0 -0
  354. /package/dist/{chunk-DEBHXHJR.js.map → chunk-WIPEXG6R.js.map} +0 -0
  355. /package/dist/{chunk-SH6YO3LT.js.map → chunk-WVO4G37W.js.map} +0 -0
  356. /package/dist/{chunk-VVIW3PTA.js.map → chunk-YTQNQ4KB.js.map} +0 -0
  357. /package/dist/{chunk-PKRIQL3V.js.map → chunk-YTSNAUXN.js.map} +0 -0
  358. /package/dist/{chunk-QAOGGNE3.js.map → chunk-ZPSVBSOF.js.map} +0 -0
  359. /package/dist/{forget-HRPLVB3W.js.map → forget-4UY2EOKH.js.map} +0 -0
@@ -0,0 +1,872 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { unlink } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { isDeepStrictEqual } from "node:util";
5
+ import { log } from "../logger.js";
6
+ import type { MemoryFile, MemoryFrontmatter } from "../types.js";
7
+ import { markProjectedMemoryPathInvalid } from "../memory-projection-store.js";
8
+ import { RECALL_FALLBACK_DIRS } from "../utils/category-dir.js";
9
+ import { isErrnoCode } from "../utils/errno.js";
10
+ import {
11
+ readMaybeEncryptedFileFromChunks,
12
+ writeMaybeEncryptedFileFromChunks,
13
+ } from "../secure-store/secure-fs.js";
14
+ import {
15
+ buildExplicitCaptureDedupKey,
16
+ captureIdentityHash,
17
+ tombstoneBlocked,
18
+ isQueuedReviewFrontmatter,
19
+ isQueuedReviewMemory,
20
+ runTombstoneBlockedChunkMutation,
21
+ runTombstoneBlockedMutation,
22
+ type TombstoneBlockedMutationHost,
23
+ } from "./tombstone-blocked-capture-mutation.js";
24
+ import { TombstoneBlockedCaptureIndex } from "./tombstone-blocked-capture-index.js";
25
+ import type { TombstoneBlockedCaptureIndexOptions } from "./tombstone-blocked-capture-index.js";
26
+
27
+ const OFFLINE_SYNC_BUFFER_LIMIT_BYTES = 1_048_576;
28
+ const OFFLINE_SYNC_STAGE_READ_BYTES = 64 * 1024;
29
+ const OFFLINE_SYNC_FRONTMATTER_DELIMITER = Buffer.from("\n---\n", "utf8");
30
+
31
+ type StreamedOfflineSyncIdentity = {
32
+ identityHash: string;
33
+ };
34
+
35
+ type PreparedOfflineSyncChunks = {
36
+ after: MemoryFile | null;
37
+ chunks: AsyncIterable<Buffer>;
38
+ stagePath?: string;
39
+ streamedIdentity?: StreamedOfflineSyncIdentity;
40
+ };
41
+
42
+ function createExplicitContentNormalizer(onChunk: (chunk: string) => void): {
43
+ push: (text: string) => void;
44
+ finish: () => void;
45
+ } {
46
+ let started = false;
47
+ let pendingSpace = false;
48
+ let output = "";
49
+ const flush = () => {
50
+ if (output.length > 0) {
51
+ onChunk(output);
52
+ output = "";
53
+ }
54
+ };
55
+ return {
56
+ push(text) {
57
+ for (const character of text.toLowerCase()) {
58
+ if (/\s/u.test(character)) {
59
+ if (started) pendingSpace = true;
60
+ continue;
61
+ }
62
+ if (pendingSpace) {
63
+ output += " ";
64
+ pendingSpace = false;
65
+ }
66
+ output += character;
67
+ started = true;
68
+ if (output.length >= OFFLINE_SYNC_STAGE_READ_BYTES) flush();
69
+ }
70
+ },
71
+ finish: flush,
72
+ };
73
+ }
74
+
75
+ async function forEachStagedTextChunk(
76
+ stagePath: string,
77
+ offset: number,
78
+ onText: (text: string) => void,
79
+ key: Buffer | null,
80
+ memoryDir: string
81
+ ): Promise<void> {
82
+ const decoder = new TextDecoder("utf-8");
83
+ let remaining = offset;
84
+ for await (const chunk of readMaybeEncryptedFileFromChunks(stagePath, key, memoryDir)) {
85
+ const textChunk =
86
+ remaining >= chunk.length
87
+ ? null
88
+ : chunk.subarray(remaining);
89
+ remaining = Math.max(0, remaining - chunk.length);
90
+ if (textChunk === null || textChunk.length === 0) continue;
91
+ const text = decoder.decode(textChunk, { stream: true });
92
+ if (text.length > 0) onText(text);
93
+ }
94
+ const finalText = decoder.decode();
95
+ if (finalText.length > 0) onText(finalText);
96
+ }
97
+
98
+ async function findStagedBodyOffset(
99
+ stagePath: string,
100
+ key: Buffer | null,
101
+ memoryDir: string
102
+ ): Promise<number | null> {
103
+ let scannedBytes = 0;
104
+ let tail = Buffer.alloc(0);
105
+ for await (const chunk of readMaybeEncryptedFileFromChunks(stagePath, key, memoryDir)) {
106
+ const window = tail.length > 0 ? Buffer.concat([tail, chunk]) : chunk;
107
+ const windowStart = scannedBytes - tail.length;
108
+ for (let searchFrom = 0; ; ) {
109
+ const delimiterStart = window.indexOf(OFFLINE_SYNC_FRONTMATTER_DELIMITER, searchFrom);
110
+ if (delimiterStart < 0) break;
111
+ if (windowStart + delimiterStart >= 4) {
112
+ return windowStart + delimiterStart + OFFLINE_SYNC_FRONTMATTER_DELIMITER.length;
113
+ }
114
+ searchFrom = delimiterStart + 1;
115
+ }
116
+ scannedBytes += chunk.length;
117
+ const tailLength = Math.min(OFFLINE_SYNC_FRONTMATTER_DELIMITER.length - 1, window.length);
118
+ tail = Buffer.from(window.subarray(window.length - tailLength));
119
+ }
120
+ return null;
121
+ }
122
+
123
+ async function readStagedPrefix(
124
+ stagePath: string,
125
+ byteLimit: number,
126
+ key: Buffer | null,
127
+ memoryDir: string
128
+ ): Promise<Buffer> {
129
+ const chunks: Buffer[] = [];
130
+ let bytesRead = 0;
131
+ for await (const chunk of readMaybeEncryptedFileFromChunks(stagePath, key, memoryDir)) {
132
+ const length = Math.min(chunk.length, byteLimit - bytesRead);
133
+ if (length > 0) chunks.push(chunk.subarray(0, length));
134
+ bytesRead += length;
135
+ if (bytesRead >= byteLimit) break;
136
+ }
137
+ return Buffer.concat(chunks, bytesRead);
138
+ }
139
+
140
+ export type OfflineSyncMemoryParser = (filePath: string, content: Buffer) => MemoryFile | null;
141
+
142
+ export function parseTombstoneBlockedOfflineSyncMemory(
143
+ filePath: string,
144
+ content: Buffer,
145
+ memoryDir: string,
146
+ parseFrontmatter: (raw: string) => { frontmatter: MemoryFrontmatter; content: string } | null,
147
+ normalizeFrontmatterForPath: (frontmatter: MemoryFrontmatter, pathRel: string, content?: string) => MemoryFrontmatter,
148
+ toMemoryPathRel: (baseDir: string, filePath: string) => string
149
+ ): MemoryFile | null {
150
+ const parsed = parseFrontmatter(content.toString("utf8"));
151
+ if (!parsed) return null;
152
+ return {
153
+ path: filePath,
154
+ frontmatter: normalizeFrontmatterForPath(parsed.frontmatter, toMemoryPathRel(memoryDir, filePath), parsed.content),
155
+ content: parsed.content,
156
+ };
157
+ }
158
+
159
+ export abstract class TombstoneBlockedCaptureIndexHost {
160
+ private tombstoneBlockedCaptureIndex: TombstoneBlockedCaptureIndex | null = null;
161
+ protected abstract tombstoneBlockedCaptureIndexOptions(): TombstoneBlockedCaptureIndexOptions;
162
+ protected abstract writeStorageSecureFile(
163
+ filePath: string,
164
+ content: string | Buffer,
165
+ forceEncrypt?: boolean
166
+ ): Promise<void>;
167
+ protected abstract assertManagedStoragePath(filePath: string, method: string): string;
168
+ protected abstract notifyCatalogWriteForPath(filePath: string): void;
169
+ protected abstract readMemoryByPath(filePath: string): Promise<MemoryFile | null>;
170
+ protected abstract invalidateAllMemoriesCache(): void;
171
+ protected abstract invalidateKnowledgeIndexCache(): void;
172
+ protected abstract invalidateColdMemoriesCache(): void;
173
+ protected abstract bumpArtifactWriteVersion(): number;
174
+ protected abstract bumpMemoryStatusVersion(): void;
175
+ protected abstract bumpMemoryCorpusVersion(): void;
176
+ protected abstract markFactHashIndexNotAuthoritative(): void;
177
+
178
+ private async writeStorageSecureFileChunks(filePath: string, chunks: AsyncIterable<Buffer>): Promise<void> {
179
+ const options = this.tombstoneBlockedCaptureIndexOptions();
180
+ await writeMaybeEncryptedFileFromChunks(
181
+ filePath,
182
+ chunks,
183
+ options.secureStoreWriteKeyProvider(),
184
+ {},
185
+ options.memoryDir
186
+ );
187
+ this.notifyCatalogWriteForPath(filePath);
188
+ }
189
+
190
+ protected getTombstoneBlockedCaptureIndex(): TombstoneBlockedCaptureIndex {
191
+ if (this.tombstoneBlockedCaptureIndex === null) {
192
+ this.tombstoneBlockedCaptureIndex = new TombstoneBlockedCaptureIndex(this.tombstoneBlockedCaptureIndexOptions());
193
+ }
194
+ return this.tombstoneBlockedCaptureIndex;
195
+ }
196
+ async withTombstoneBlockedCaptureWriteLock<T>(
197
+ task: () => Promise<T>,
198
+ identity?: string | readonly string[]
199
+ ): Promise<T> {
200
+ return await this.getTombstoneBlockedCaptureIndex().withCaptureWriteLock(task, identity);
201
+ }
202
+
203
+ protected async withTombstoneBlockedCaptureWriteLockAndHashes<T>(
204
+ task: () => Promise<T>,
205
+ identity: string | readonly string[] | undefined,
206
+ identityHashes: readonly string[]
207
+ ): Promise<T> {
208
+ return await this.getTombstoneBlockedCaptureIndex().withCaptureWriteLockAndHashes(task, identity, identityHashes);
209
+ }
210
+
211
+ private async writeTombstoneBlockedMutation(
212
+ blocked: boolean,
213
+ pathname: string,
214
+ fileContent: string,
215
+ identity: string | readonly string[],
216
+ updateIndex: (rebuildMarker?: string, current?: MemoryFile | null) => Promise<void>,
217
+ beforeIndexUpdate?: () => Promise<void>,
218
+ coordinate = false
219
+ ): Promise<void> {
220
+ const host: TombstoneBlockedMutationHost = {
221
+ readCurrent: () => this.readMemoryByPath(pathname),
222
+ isBlocked: (memory) => this.isTombstoneBlockedMemory(memory),
223
+ isQueuedReview: (memory) => isQueuedReviewMemory(memory),
224
+ memoryIdentity: (memory) => this.offlineSyncMemoryIdentity(memory),
225
+ prepareWrite: () => this.getTombstoneBlockedCaptureIndex().prepareWrite(),
226
+ commitWrite: (marker) => this.getTombstoneBlockedCaptureIndex().commitWrite(marker),
227
+ discardWrite: (marker) => this.getTombstoneBlockedCaptureIndex().discardWrite(marker),
228
+ markUntrusted: () => this.getTombstoneBlockedCaptureIndex().markUntrusted(),
229
+ writeStorageSecureFile: (target, content) => this.writeStorageSecureFile(target, content),
230
+ withCaptureWriteLock: (task, lockIdentity) =>
231
+ this.withTombstoneBlockedCaptureWriteLock(task, lockIdentity),
232
+ logWarning: (message) => log.warn(message),
233
+ };
234
+ await runTombstoneBlockedMutation(host, {
235
+ blocked,
236
+ pathname,
237
+ fileContent,
238
+ identity,
239
+ updateIndex,
240
+ beforeIndexUpdate,
241
+ coordinate,
242
+ });
243
+ }
244
+
245
+ protected async writeTombstoneBlockedMemory(
246
+ pathname: string,
247
+ fileContent: string,
248
+ frontmatter: MemoryFrontmatter,
249
+ content: string,
250
+ beforeIndexUpdate?: () => Promise<void>
251
+ ): Promise<void> {
252
+ await this.writeTombstoneBlockedMutation(
253
+ tombstoneBlocked(frontmatter),
254
+ pathname,
255
+ fileContent,
256
+ buildExplicitCaptureDedupKey(content, frontmatter.category, frontmatter.sourceConnector),
257
+ (rebuildMarker) =>
258
+ this.getTombstoneBlockedCaptureIndex().addWrittenMemory(pathname, frontmatter, content, rebuildMarker),
259
+ beforeIndexUpdate
260
+ );
261
+ }
262
+
263
+ protected async writeTombstoneBlockedChunk(
264
+ pathname: string,
265
+ fileContent: string,
266
+ frontmatter: MemoryFrontmatter,
267
+ content: string,
268
+ findDuplicate: () => Promise<MemoryFile | null>,
269
+ afterWrite: () => Promise<void>
270
+ ): Promise<string> {
271
+ return await runTombstoneBlockedChunkMutation({
272
+ blocked: tombstoneBlocked(frontmatter),
273
+ id: frontmatter.id,
274
+ identity: buildExplicitCaptureDedupKey(content, frontmatter.category, frontmatter.sourceConnector),
275
+ findDuplicate,
276
+ persistMemory: () => this.writeTombstoneBlockedMemory(pathname, fileContent, frontmatter, content),
277
+ afterWrite,
278
+ withCaptureWriteLock: (task, identity) => this.withTombstoneBlockedCaptureWriteLock(task, identity),
279
+ });
280
+ }
281
+
282
+ protected async writeTombstoneBlockedUpdate(
283
+ before: MemoryFile,
284
+ fileContent: string,
285
+ frontmatter: MemoryFrontmatter,
286
+ content: string,
287
+ beforeIndexUpdate?: () => Promise<void>
288
+ ): Promise<void> {
289
+ await this.writeTombstoneBlockedMutation(
290
+ tombstoneBlocked(before.frontmatter) || tombstoneBlocked(frontmatter),
291
+ before.path,
292
+ fileContent,
293
+ [
294
+ ...(tombstoneBlocked(before.frontmatter)
295
+ ? [
296
+ buildExplicitCaptureDedupKey(
297
+ before.content,
298
+ before.frontmatter.category,
299
+ before.frontmatter.sourceConnector
300
+ ),
301
+ ]
302
+ : []),
303
+ ...(tombstoneBlocked(frontmatter)
304
+ ? [buildExplicitCaptureDedupKey(content, frontmatter.category, frontmatter.sourceConnector)]
305
+ : []),
306
+ ...(isQueuedReviewMemory(before)
307
+ ? [
308
+ buildExplicitCaptureDedupKey(
309
+ before.content,
310
+ before.frontmatter.category,
311
+ before.frontmatter.sourceConnector
312
+ ),
313
+ ]
314
+ : []),
315
+ ...(isQueuedReviewFrontmatter(frontmatter)
316
+ ? [buildExplicitCaptureDedupKey(content, frontmatter.category, frontmatter.sourceConnector)]
317
+ : []),
318
+ ],
319
+ (rebuildMarker, current) =>
320
+ this.getTombstoneBlockedCaptureIndex().syncUpdatedMemory(
321
+ current ?? before,
322
+ frontmatter,
323
+ content,
324
+ rebuildMarker
325
+ ),
326
+ beforeIndexUpdate,
327
+ isQueuedReviewMemory(before) || isQueuedReviewFrontmatter(frontmatter)
328
+ );
329
+ }
330
+
331
+ protected async writeTombstoneBlockedFrontmatter(
332
+ before: MemoryFile,
333
+ fileContent: string,
334
+ frontmatter: MemoryFrontmatter,
335
+ beforeIndexUpdate?: () => Promise<void>
336
+ ): Promise<void> {
337
+ await this.writeTombstoneBlockedMutation(
338
+ tombstoneBlocked(before.frontmatter) || tombstoneBlocked(frontmatter),
339
+ before.path,
340
+ fileContent,
341
+ [
342
+ ...(tombstoneBlocked(before.frontmatter)
343
+ ? [
344
+ buildExplicitCaptureDedupKey(
345
+ before.content,
346
+ before.frontmatter.category,
347
+ before.frontmatter.sourceConnector
348
+ ),
349
+ ]
350
+ : []),
351
+ ...(tombstoneBlocked(frontmatter)
352
+ ? [buildExplicitCaptureDedupKey(before.content, frontmatter.category, frontmatter.sourceConnector)]
353
+ : []),
354
+ ...(isQueuedReviewMemory(before)
355
+ ? [
356
+ buildExplicitCaptureDedupKey(
357
+ before.content,
358
+ before.frontmatter.category,
359
+ before.frontmatter.sourceConnector
360
+ ),
361
+ ]
362
+ : []),
363
+ ...(isQueuedReviewFrontmatter(frontmatter)
364
+ ? [buildExplicitCaptureDedupKey(before.content, frontmatter.category, frontmatter.sourceConnector)]
365
+ : []),
366
+ ],
367
+ (rebuildMarker, current) =>
368
+ this.getTombstoneBlockedCaptureIndex().syncUpdatedFrontmatter(
369
+ current ?? before,
370
+ frontmatter,
371
+ rebuildMarker
372
+ ),
373
+ beforeIndexUpdate,
374
+ isQueuedReviewMemory(before) || isQueuedReviewFrontmatter(frontmatter)
375
+ );
376
+ }
377
+
378
+ private isTombstoneBlockedMemory(memory: MemoryFile | null): memory is MemoryFile {
379
+ return memory !== null && memory.frontmatter.status === "pending_review" && Boolean(memory.frontmatter.blockedBy);
380
+ }
381
+
382
+ private offlineSyncMemoryIdentity(memory: MemoryFile): string {
383
+ return buildExplicitCaptureDedupKey(
384
+ memory.content,
385
+ memory.frontmatter.category,
386
+ memory.frontmatter.sourceConnector
387
+ );
388
+ }
389
+ private async isDuplicateTombstoneBlockedOfflineSync(
390
+ target: string,
391
+ incoming: MemoryFile,
392
+ streamedIdentity?: StreamedOfflineSyncIdentity
393
+ ): Promise<boolean> {
394
+ const before = await this.readMemoryByPath(target);
395
+ const duplicate = streamedIdentity
396
+ ? true
397
+ : await this.getTombstoneBlockedCaptureIndex().has(
398
+ incoming.content,
399
+ incoming.frontmatter.category,
400
+ incoming.frontmatter.sourceConnector
401
+ );
402
+ if (!duplicate) return false;
403
+ const incomingIdentity = streamedIdentity?.identityHash ?? this.offlineSyncMemoryIdentity(incoming);
404
+ const options = this.tombstoneBlockedCaptureIndexOptions();
405
+ const existing = [...(await options.readAllMemories()), ...(await options.readAllColdMemories())];
406
+ const exactMatch = existing.some(
407
+ (memory) =>
408
+ this.isTombstoneBlockedMemory(memory) &&
409
+ (streamedIdentity ? captureIdentityHash(memory) : this.offlineSyncMemoryIdentity(memory)) === incomingIdentity
410
+ );
411
+ if (!exactMatch) return false;
412
+ if (before === null) return true;
413
+ if (!this.isTombstoneBlockedMemory(before)) return false;
414
+ const beforeIdentity = streamedIdentity ? captureIdentityHash(before) : this.offlineSyncMemoryIdentity(before);
415
+ if (beforeIdentity !== incomingIdentity) return false;
416
+ return streamedIdentity
417
+ ? isDeepStrictEqual(before.frontmatter, incoming.frontmatter)
418
+ : before.content === incoming.content && isDeepStrictEqual(before.frontmatter, incoming.frontmatter);
419
+ }
420
+
421
+ private async invalidateAfterOfflineSyncMutation(filePath: string, ownedMarker?: string): Promise<void> {
422
+ this.invalidateAllMemoriesCache();
423
+ this.invalidateKnowledgeIndexCache();
424
+ this.markFactHashIndexNotAuthoritative();
425
+ if (filePath.includes(`${path.sep}cold${path.sep}`)) {
426
+ this.invalidateColdMemoriesCache();
427
+ }
428
+ await this.rebuildTombstoneBlockedCaptureAfterInvalidationForPath(filePath, ownedMarker);
429
+ if (filePath.includes(`${path.sep}artifacts${path.sep}`)) {
430
+ this.bumpArtifactWriteVersion();
431
+ }
432
+ this.bumpMemoryStatusVersion();
433
+ }
434
+
435
+ protected async runTombstoneBlockedArchive(
436
+ memory: MemoryFile,
437
+ task: (current: MemoryFile, markDurable: () => void) => Promise<string | null>
438
+ ): Promise<string | null> {
439
+ if (!this.isTombstoneBlockedMemory(memory) && !isQueuedReviewMemory(memory)) {
440
+ return await task(memory, () => {});
441
+ }
442
+ let archivedPath: string | null = null;
443
+ const archived = await this.runTombstoneBlockedInvalidation(
444
+ memory,
445
+ async (current, rebuildMarker, markDurable) => {
446
+ let archiveDurable = false;
447
+ const markArchiveDurable = () => {
448
+ archiveDurable = true;
449
+ markDurable();
450
+ };
451
+ archivedPath = await task(current, markArchiveDurable);
452
+ if (rebuildMarker !== undefined && (archivedPath !== null || archiveDurable)) {
453
+ await this.rebuildTombstoneBlockedCaptureAfterInvalidation(rebuildMarker);
454
+ } else if (rebuildMarker !== undefined) {
455
+ const index = this.getTombstoneBlockedCaptureIndex();
456
+ try { await index.discardWrite(rebuildMarker); } catch { index.markUntrusted(); }
457
+ }
458
+ return archivedPath !== null;
459
+ }
460
+ );
461
+ return archived ? archivedPath : null;
462
+ }
463
+
464
+ protected async runTombstoneBlockedInvalidation(
465
+ memory: MemoryFile,
466
+ task: (
467
+ current: MemoryFile,
468
+ rebuildMarker: string | undefined,
469
+ markDurable: () => void
470
+ ) => Promise<boolean>,
471
+ propagateErrors = false,
472
+ coordinate = false
473
+ ): Promise<boolean> {
474
+ let lockIdentity =
475
+ coordinate || this.isTombstoneBlockedMemory(memory) || isQueuedReviewMemory(memory)
476
+ ? this.offlineSyncMemoryIdentity(memory)
477
+ : undefined;
478
+ for (;;) {
479
+ const attempt = async () => {
480
+ const options = this.tombstoneBlockedCaptureIndexOptions();
481
+ const current =
482
+ (await this.readMemoryByPath(memory.path)) ??
483
+ (await options.readAllMemories()).find((candidate) => candidate.frontmatter.id === memory.frontmatter.id) ??
484
+ (await options.readAllColdMemories()).find((candidate) => candidate.frontmatter.id === memory.frontmatter.id);
485
+ if (!current) return { result: false };
486
+ const blocked = this.isTombstoneBlockedMemory(current);
487
+ const queuedReview = isQueuedReviewMemory(current);
488
+ if (blocked || queuedReview) {
489
+ const currentIdentity = this.offlineSyncMemoryIdentity(current);
490
+ if (lockIdentity !== currentIdentity) return { retryIdentity: currentIdentity };
491
+ }
492
+ const blockedIndex = blocked ? this.getTombstoneBlockedCaptureIndex() : null;
493
+ let rebuildMarker: string | undefined;
494
+ let durable = false;
495
+ try {
496
+ if (blockedIndex) rebuildMarker = await blockedIndex.prepareWrite();
497
+ const result = await task(current, rebuildMarker, () => {
498
+ durable = true;
499
+ });
500
+ if (!result && rebuildMarker && !durable && blockedIndex) {
501
+ try {
502
+ await blockedIndex.discardWrite(rebuildMarker);
503
+ } catch {
504
+ blockedIndex.markUntrusted();
505
+ }
506
+ }
507
+ return { result };
508
+ } catch (error) {
509
+ if (rebuildMarker && !durable && blockedIndex) {
510
+ try {
511
+ await blockedIndex.discardWrite(rebuildMarker);
512
+ } catch {
513
+ blockedIndex.markUntrusted();
514
+ }
515
+ }
516
+ if (propagateErrors) throw error;
517
+ return { result: false };
518
+ }
519
+ };
520
+ const result =
521
+ lockIdentity === undefined
522
+ ? await attempt()
523
+ : await this.withTombstoneBlockedCaptureWriteLock(attempt, lockIdentity);
524
+ if (result.retryIdentity !== undefined) {
525
+ lockIdentity = result.retryIdentity;
526
+ continue;
527
+ }
528
+ return result.result;
529
+ }
530
+ }
531
+ async deleteMemoryForMaintenance(
532
+ memory: MemoryFile,
533
+ shouldDelete: (current: MemoryFile) => boolean = () => true
534
+ ): Promise<MemoryFile | null> {
535
+ let deleted: MemoryFile | null = null;
536
+ const removed = await this.runTombstoneBlockedInvalidation(
537
+ memory,
538
+ async (current, rebuildMarker, markDurable) => {
539
+ if (!shouldDelete(current)) return false;
540
+ await unlink(current.path);
541
+ markDurable();
542
+ deleted = current;
543
+ const memoryDir = this.tombstoneBlockedCaptureIndexOptions().memoryDir;
544
+ markProjectedMemoryPathInvalid(memoryDir, current.frontmatter.id);
545
+ this.invalidateAllMemoriesCache();
546
+ if (current.path.includes(`${path.sep}cold${path.sep}`)) {
547
+ this.invalidateColdMemoriesCache();
548
+ }
549
+ await this.rebuildTombstoneBlockedCaptureAfterInvalidation(rebuildMarker);
550
+ this.bumpMemoryCorpusVersion();
551
+ this.bumpMemoryStatusVersion();
552
+ return true;
553
+ },
554
+ true,
555
+ true
556
+ );
557
+ return removed ? deleted : null;
558
+ }
559
+
560
+ protected async runTombstoneBlockedOfflineSyncMutation(
561
+ target: string,
562
+ after: MemoryFile | null,
563
+ write: () => Promise<void>,
564
+ coordinate = false,
565
+ streamedIdentity?: StreamedOfflineSyncIdentity
566
+ ): Promise<void> {
567
+ for (;;) {
568
+ const before = await this.readMemoryByPath(target);
569
+ const targetIdentity = this.isTombstoneBlockedMemory(before)
570
+ ? this.offlineSyncMemoryIdentity(before)
571
+ : undefined;
572
+ const blocked = coordinate || targetIdentity !== undefined || this.isTombstoneBlockedMemory(after);
573
+ const identities = [
574
+ ...(targetIdentity === undefined ? [] : [targetIdentity]),
575
+ ...(!streamedIdentity && this.isTombstoneBlockedMemory(after)
576
+ ? [this.offlineSyncMemoryIdentity(after)]
577
+ : []),
578
+ ...(coordinate ? [target] : []),
579
+ ...(coordinate && after === null && streamedIdentity === undefined ? [""] : []),
580
+ ];
581
+ let retryIdentity: string | undefined;
582
+ const mutate = async (): Promise<void> => {
583
+ const current = await this.readMemoryByPath(target);
584
+ if (this.isTombstoneBlockedMemory(current)) {
585
+ const currentIdentity = this.offlineSyncMemoryIdentity(current);
586
+ if (currentIdentity !== targetIdentity) {
587
+ retryIdentity = currentIdentity;
588
+ return;
589
+ }
590
+ }
591
+ if (
592
+ this.isTombstoneBlockedMemory(after) &&
593
+ (await this.isDuplicateTombstoneBlockedOfflineSync(target, after, streamedIdentity))
594
+ ) {
595
+ return;
596
+ }
597
+ const marker = blocked ? await this.getTombstoneBlockedCaptureIndex().prepareWrite() : undefined;
598
+ let durable = false;
599
+ try {
600
+ await write();
601
+ durable = true;
602
+ if (marker) {
603
+ try {
604
+ await this.getTombstoneBlockedCaptureIndex().commitWrite(marker);
605
+ } catch (err) {
606
+ this.getTombstoneBlockedCaptureIndex().markUntrusted();
607
+ log.warn(`storage.offlineSyncFile committed write marker failed: ${err}`);
608
+ }
609
+ }
610
+ await this.invalidateAfterOfflineSyncMutation(target, marker);
611
+ } catch (err) {
612
+ if (marker && !durable) {
613
+ try {
614
+ await this.getTombstoneBlockedCaptureIndex().discardWrite(marker);
615
+ } catch (cleanupError) {
616
+ this.getTombstoneBlockedCaptureIndex().markUntrusted();
617
+ log.warn(`storage.offlineSyncFile failed to clear write marker: ${cleanupError}`);
618
+ }
619
+ }
620
+ throw err;
621
+ }
622
+ };
623
+ if (!blocked) {
624
+ await mutate();
625
+ } else {
626
+ if (streamedIdentity) {
627
+ await this.withTombstoneBlockedCaptureWriteLockAndHashes(
628
+ mutate,
629
+ identities,
630
+ [streamedIdentity.identityHash]
631
+ );
632
+ } else {
633
+ await this.withTombstoneBlockedCaptureWriteLock(mutate, identities);
634
+ }
635
+ }
636
+ if (retryIdentity !== undefined) continue;
637
+ return;
638
+ }
639
+ }
640
+
641
+ protected async writeTombstoneBlockedOfflineSyncFile(target: string, content: Buffer): Promise<void> {
642
+ await this.runTombstoneBlockedOfflineSyncMutation(
643
+ target,
644
+ this.tombstoneBlockedCaptureIndexOptions().parseMemory?.(target, content) ?? null,
645
+ () => this.writeStorageSecureFile(target, content)
646
+ );
647
+ }
648
+
649
+ private async prepareTombstoneBlockedOfflineSyncChunks(
650
+ target: string,
651
+ chunks: AsyncIterable<Buffer>
652
+ ): Promise<PreparedOfflineSyncChunks> {
653
+ const parseMemory = this.tombstoneBlockedCaptureIndexOptions().parseMemory;
654
+ if (!parseMemory) return { after: null, chunks };
655
+
656
+ const iterator = chunks[Symbol.asyncIterator]();
657
+ const buffered: Buffer[] = [];
658
+ let bufferedBytes = 0;
659
+ let firstOverflow: Buffer | undefined;
660
+ for (;;) {
661
+ const next = await iterator.next();
662
+ if (next.done) break;
663
+ if (bufferedBytes + next.value.length > OFFLINE_SYNC_BUFFER_LIMIT_BYTES) {
664
+ firstOverflow = next.value;
665
+ break;
666
+ }
667
+ bufferedBytes += next.value.length;
668
+ buffered.push(next.value);
669
+ }
670
+ const overflow = firstOverflow;
671
+ if (overflow === undefined) {
672
+ let after: MemoryFile | null = null;
673
+ try {
674
+ after = parseMemory(target, Buffer.concat(buffered));
675
+ } catch {
676
+ after = null;
677
+ }
678
+ const replay = (async function* (): AsyncIterable<Buffer> {
679
+ for (const chunk of buffered) yield chunk;
680
+ })();
681
+ return { after, chunks: replay };
682
+ }
683
+
684
+ const options = this.tombstoneBlockedCaptureIndexOptions();
685
+ const stagePath = path.join(
686
+ options.stateDir,
687
+ "tombstone-blocked-capture",
688
+ "offline-sync-staging",
689
+ `${randomUUID()}.stage`
690
+ );
691
+ const stagedChunks = (async function* (): AsyncIterable<Buffer> {
692
+ for (const chunk of buffered) yield chunk;
693
+ yield overflow;
694
+ for (;;) {
695
+ const next = await iterator.next();
696
+ if (next.done) break;
697
+ yield next.value;
698
+ }
699
+ })();
700
+ try {
701
+ await writeMaybeEncryptedFileFromChunks(
702
+ stagePath,
703
+ stagedChunks,
704
+ options.secureStoreWriteKeyProvider(),
705
+ { atomic: false },
706
+ options.memoryDir
707
+ );
708
+ return await this.describeStagedOfflineSyncChunks(target, stagePath, parseMemory);
709
+ } catch (err) {
710
+ await unlink(stagePath).catch(() => {});
711
+ throw err;
712
+ }
713
+ }
714
+
715
+ private async describeStagedOfflineSyncChunks(
716
+ target: string,
717
+ stagePath: string,
718
+ parseMemory: OfflineSyncMemoryParser
719
+ ): Promise<PreparedOfflineSyncChunks> {
720
+ const options = this.tombstoneBlockedCaptureIndexOptions();
721
+ const key = options.secureStoreKeyProvider();
722
+ const bodyOffset = await findStagedBodyOffset(stagePath, key, options.memoryDir);
723
+ if (bodyOffset === null) {
724
+ return { after: null, chunks: this.readStagedOfflineSyncChunks(stagePath), stagePath };
725
+ }
726
+ const prefix = await readStagedPrefix(stagePath, bodyOffset, key, options.memoryDir);
727
+ let after: MemoryFile | null = null;
728
+ try {
729
+ after = parseMemory(target, prefix.subarray(0, bodyOffset));
730
+ } catch {
731
+ after = null;
732
+ }
733
+ if (after === null) {
734
+ return { after: null, chunks: this.readStagedOfflineSyncChunks(stagePath), stagePath };
735
+ }
736
+ const streamedIdentity = await this.computeStagedOfflineSyncIdentity(stagePath, bodyOffset, after);
737
+ return {
738
+ after,
739
+ chunks: this.readStagedOfflineSyncChunks(stagePath),
740
+ stagePath,
741
+ streamedIdentity,
742
+ };
743
+ }
744
+
745
+ private async computeStagedOfflineSyncIdentity(
746
+ stagePath: string,
747
+ bodyOffset: number,
748
+ after: MemoryFile
749
+ ): Promise<StreamedOfflineSyncIdentity> {
750
+ let normalizedLength = 0;
751
+ const countNormalizer = createExplicitContentNormalizer((chunk) => {
752
+ normalizedLength += chunk.length;
753
+ });
754
+ const options = this.tombstoneBlockedCaptureIndexOptions();
755
+ const key = options.secureStoreKeyProvider();
756
+ await forEachStagedTextChunk(
757
+ stagePath,
758
+ bodyOffset,
759
+ (text) => countNormalizer.push(text),
760
+ key,
761
+ options.memoryDir
762
+ );
763
+ countNormalizer.finish();
764
+
765
+ const category = after.frontmatter.category;
766
+ const connector = after.frontmatter.sourceConnector?.trim() ?? "";
767
+ const encode = (label: string, value: string): string => `${label} ${value.length} ${value}`;
768
+ const identityPrefix =
769
+ [encode("category", category), encode("connector", connector), `content ${normalizedLength} `].join(" ");
770
+ const identityHash = createHash("sha256");
771
+ identityHash.update(identityPrefix);
772
+ const identityNormalizer = createExplicitContentNormalizer((chunk) => identityHash.update(chunk));
773
+ await forEachStagedTextChunk(
774
+ stagePath,
775
+ bodyOffset,
776
+ (text) => identityNormalizer.push(text),
777
+ key,
778
+ options.memoryDir
779
+ );
780
+ identityNormalizer.finish();
781
+ return {
782
+ identityHash: identityHash.digest("hex"),
783
+ };
784
+ }
785
+
786
+ private async *readStagedOfflineSyncChunks(stagePath: string): AsyncIterable<Buffer> {
787
+ const options = this.tombstoneBlockedCaptureIndexOptions();
788
+ for await (const chunk of readMaybeEncryptedFileFromChunks(
789
+ stagePath,
790
+ options.secureStoreKeyProvider(),
791
+ options.memoryDir
792
+ )) {
793
+ yield chunk;
794
+ }
795
+ }
796
+
797
+ protected async writeTombstoneBlockedOfflineSyncFileChunks(
798
+ target: string,
799
+ chunks: AsyncIterable<Buffer>
800
+ ): Promise<void> {
801
+ const coordinate = this.getTombstoneBlockedCaptureIndex().shouldRebuildAfterInvalidationForPath(target);
802
+ const prepared: PreparedOfflineSyncChunks = coordinate
803
+ ? await this.prepareTombstoneBlockedOfflineSyncChunks(target, chunks)
804
+ : { after: null, chunks };
805
+ const streamedIdentity = prepared.streamedIdentity;
806
+ const mutate = async (): Promise<void> => {
807
+ try {
808
+ await this.runTombstoneBlockedOfflineSyncMutation(
809
+ target,
810
+ prepared.after,
811
+ () => this.writeStorageSecureFileChunks(target, prepared.chunks),
812
+ coordinate,
813
+ streamedIdentity
814
+ );
815
+ } finally {
816
+ if (prepared.stagePath !== undefined) await unlink(prepared.stagePath).catch(() => {});
817
+ }
818
+ };
819
+ await mutate();
820
+ }
821
+
822
+ async writeOfflineSyncFile(filePath: string, content: Buffer): Promise<void> {
823
+ const target = this.assertManagedStoragePath(filePath, "storage.writeOfflineSyncFile");
824
+ await this.writeTombstoneBlockedOfflineSyncFile(target, content);
825
+ }
826
+
827
+ async writeOfflineSyncFileChunks(filePath: string, chunks: AsyncIterable<Buffer>): Promise<void> {
828
+ const target = this.assertManagedStoragePath(filePath, "storage.writeOfflineSyncFileChunks");
829
+ await this.writeTombstoneBlockedOfflineSyncFileChunks(target, chunks);
830
+ }
831
+
832
+ async deleteOfflineSyncFile(filePath: string): Promise<void> {
833
+ const target = this.assertManagedStoragePath(filePath, "storage.deleteOfflineSyncFile");
834
+ await this.deleteTombstoneBlockedOfflineSyncFile(target);
835
+ }
836
+
837
+ protected async deleteTombstoneBlockedOfflineSyncFile(target: string): Promise<void> {
838
+ await this.runTombstoneBlockedOfflineSyncMutation(target, null, async () => {
839
+ await unlink(target).catch((error: unknown) => {
840
+ if (isErrnoCode(error, "ENOENT")) return;
841
+ throw error;
842
+ });
843
+ });
844
+ }
845
+
846
+ async checkTombstoneBlockedExplicitCapture(
847
+ ...args: [string, string, string?]
848
+ ): Promise<{ has: boolean; authoritative: boolean }> {
849
+ return this.getTombstoneBlockedCaptureIndex().check(...args);
850
+ }
851
+
852
+ async hasTombstoneBlockedExplicitCapture(...args: [string, string, string?]): Promise<boolean> {
853
+ return this.getTombstoneBlockedCaptureIndex().has(...args);
854
+ }
855
+
856
+ async isTombstoneBlockedExplicitCaptureIndexAuthoritative(): Promise<boolean> {
857
+ return this.getTombstoneBlockedCaptureIndex().isAuthoritative();
858
+ }
859
+
860
+ protected async rebuildTombstoneBlockedCaptureAfterInvalidationForPath(
861
+ filePath: string,
862
+ ownedMarker?: string
863
+ ): Promise<void> {
864
+ const index = this.getTombstoneBlockedCaptureIndex();
865
+ if (index.shouldRebuildAfterInvalidationForPath(filePath)) {
866
+ await this.rebuildTombstoneBlockedCaptureAfterInvalidation(ownedMarker);
867
+ }
868
+ }
869
+ protected async rebuildTombstoneBlockedCaptureAfterInvalidation(ownedMarker?: string): Promise<void> {
870
+ await this.getTombstoneBlockedCaptureIndex().rebuildAfterInvalidation(ownedMarker);
871
+ }
872
+ }