@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 @@
1
+ {"version":3,"sources":["../src/storage/content-hash-index.ts","../src/storage/tombstone-blocked-capture-mutation.ts","../src/storage/tombstone-blocked-capture-sync.ts","../src/storage/tombstone-blocked-capture-index.ts"],"sourcesContent":["// Content-hash dedup index for facts, extracted from storage.ts (issue #1909 /\n// PR #2016 write-hot-paths). Self-contained: cross-process locked reconcile /\n// rebuild saves plus the deferred durable-retry machinery. storage.ts imports\n// these back and re-exports the class + companion types that were previously\n// part of its public API.\nimport { mkdir, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { log } from \"../logger.js\";\nimport { isErrnoCode } from \"../utils/errno.js\";\nimport { withHeldFileLock } from \"../utils/serialize-mutations.js\";\nimport { normalizeContent, computeContentHash } from \"../content-hash.js\";\nimport {\n SecureStoreLockedError,\n readMaybeEncryptedFile,\n writeMaybeEncryptedFile,\n} from \"../secure-store/secure-fs.js\";\n\n/**\n * Stale threshold (ms) for the per-file advisory lock guarding a fact-hash\n * index merge-save (issue #1909 review round 7). The critical section is a\n * single read+atomic-rewrite of one index file (≤ a few MB), so a holder older\n * than this is treated as crashed. Generous to tolerate slow/encrypted disks\n * without breaking a legitimately in-progress publish.\n */\nconst CONTENT_HASH_INDEX_LOCK_STALE_MS = 30_000;\n\n/**\n * Durable-retry policy for a deferred content-hash reconcile save whose\n * cross-process lock acquire timed out (issue #1909 / PR #2016). When the lock\n * is contended past `withHeldFileLock`'s bounded wait, the added hashes are held\n * only in this process's memory; relying on \"some later batch save\" is not\n * durable (the run may be the last before idle/shutdown), and a long-lived peer\n * that already built its authoritative in-memory index will not see the write.\n * So we schedule a bounded, exponentially-backed-off background retry that keeps\n * re-attempting the locked publish until it lands. Bounded because the durable\n * fact `.md` is already on disk and a process restart rebuilds the index from\n * the corpus regardless — exhausting retries degrades to that rebuild safety\n * net, never to data loss or a fatal append failure.\n */\nconst CONTENT_HASH_INDEX_RETRY_MAX_ATTEMPTS = 5;\n/** Base backoff (ms) before the first deferred reconcile-save retry; doubles each attempt. */\nconst CONTENT_HASH_INDEX_RETRY_BASE_MS = 500;\n/** Ceiling (ms) for the exponential reconcile-save retry backoff. */\nexport const CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS = 5_000;\n\n/**\n * Tuning for {@link ContentHashIndex} cross-process lock waits and the\n * deferred reconcile-save durable retry (issue #1909 / PR #2016). All optional;\n * production uses the module defaults. Tests inject small values so the\n * lock-timeout + retry path is exercised deterministically without real\n * multi-second waits.\n */\nexport interface ContentHashIndexLockOptions {\n /** Bounded wait to acquire the per-file advisory lock; passed to `withHeldFileLock` (default 5000ms). */\n readonly maxWaitMs?: number;\n /** Poll interval while waiting for a busy lock (default 50ms). */\n readonly pollMs?: number;\n /** Base backoff before the first deferred-save retry (default 500ms). */\n readonly retryBaseMs?: number;\n /** Max deferred-save retries before falling back to the corpus-rebuild safety net (default 5). */\n readonly retryMaxAttempts?: number;\n}\n\n/**\n * Thrown when the fact-hash index cannot be made authoritative — the\n * cross-process rebuild lock could not be acquired within the bounded retry\n * budget (PR #2016). Callers that require an authoritative dedup answer surface\n * this instead of silently trusting a stale loaded snapshot.\n */\nexport class FactHashIndexNotAuthoritativeError extends Error {\n constructor(stateDir: string) {\n super(\n `fact-hash index is not authoritative: the cross-process rebuild lock for ${stateDir} could not be acquired within the bounded retry budget`,\n );\n this.name = \"FactHashIndexNotAuthoritativeError\";\n }\n}\n\ntype DiskFingerprint = {\n mtimeMs: number;\n size: number;\n dev: number;\n ino: number;\n ctimeMs: number;\n birthtimeMs: number;\n};\n/**\n * Content-hash dedup index for facts.\n * Normalizes content (lowercase, strip punctuation, collapse whitespace),\n * computes SHA-256, and stores hashes in a line-delimited file.\n * Prevents writing semantically identical facts.\n */\nexport class ContentHashIndex {\n private hashes: Set<string> = new Set();\n private dirty = false;\n /**\n * Hashes explicitly removed since the last successful save (issue #1909 review\n * round 8 thread 3). Tracked separately so `saveMergingWithDisk` can be\n * removal-AWARE: under the file lock it reads the latest on-disk set, DROPS\n * these, then unions our additions — so a removal batch cannot resurrect a\n * hash (union alone would) and cannot clobber a concurrent extraction's\n * appended hash (blind overwrite would). Consumed + cleared by every save.\n */\n private removed: Set<string> = new Set();\n /**\n * Hashes added by THIS instance since the last successful save (issue #1909\n * review round 9 finding 3). Tracked separately from the loaded snapshot so a\n * reconciling save publishes `(on-disk \\ removed) ∪ added` — it re-applies only\n * OUR additions, never the stale loaded entries. Without this, seeding the\n * merge from `this.hashes` (loaded ∪ added) would resurrect a hash a peer\n * removed on disk while this instance held a stale snapshot.\n */\n private added: Set<string> = new Set();\n private readonly filePath: string;\n private readonly secureStoreKeyProvider: () => Buffer | null;\n private readonly secureStoreWriteKeyProvider: () => Buffer | null;\n private readonly memoryDir: string;\n private readonly lockOptions: ContentHashIndexLockOptions;\n /**\n * Deferred reconcile-save durable-retry state (issue #1909 / PR #2016). At\n * most ONE retry is ever armed (`reconcileRetryTimer` is the reentrancy guard)\n * so successive timed-out saves cannot stack duplicate/parallel retries.\n * `reconcileRetryBarrier` resolves when the chain settles (published or gave\n * up) — awaited by tests and any caller wanting the eventual result.\n */\n private reconcileRetryTimer: NodeJS.Timeout | null = null;\n private reconcileRetryAttempts = 0;\n private reconcileRetryBarrier: Promise<void> | null = null;\n private reconcileRetryResolve: (() => void) | null = null;\n /**\n * On-disk fingerprint captured at the last point THIS instance's in-memory\n * set matched disk — after `load()`, `save()`, or a reconcile publish. A peer\n * process that advances the durable index changes its file generation even\n * when byte size and mtime collide, so the freshness check remains safe\n * without an O(file-size) read on the hot path.\n */\n private lastSyncedFingerprint: DiskFingerprint | null = null;\n\n constructor(\n stateDir: string,\n secureStoreKeyProvider: () => Buffer | null = () => null,\n secureStoreWriteKeyProvider: () => Buffer | null = secureStoreKeyProvider,\n memoryDir: string = path.dirname(stateDir),\n lockOptions: ContentHashIndexLockOptions = {}\n ) {\n this.filePath = path.join(stateDir, \"fact-hashes.txt\");\n this.secureStoreKeyProvider = secureStoreKeyProvider;\n this.secureStoreWriteKeyProvider = secureStoreWriteKeyProvider;\n this.memoryDir = memoryDir;\n this.lockOptions = lockOptions;\n }\n\n /** Load existing hashes from disk. Safe to call multiple times. */\n async load(): Promise<void> {\n try {\n const raw = await readMaybeEncryptedFile(this.filePath, this.secureStoreKeyProvider(), this.memoryDir);\n for (const line of raw.split(\"\\n\")) {\n const trimmed = line.trim();\n if (trimmed.length > 0) {\n this.hashes.add(trimmed);\n }\n }\n log.debug(`content-hash index: loaded ${this.hashes.size} hashes`);\n } catch (err) {\n if (err instanceof SecureStoreLockedError) throw err;\n if (!isErrnoCode(err, \"ENOENT\")) throw err;\n log.debug(\"content-hash index: no existing index — starting fresh\");\n }\n // The loaded set now reflects the on-disk file — baseline the fingerprint so\n // a later peer write is detectable (PR #2016 review).\n await this.captureSyncedFingerprint();\n }\n\n /** Cheap `stat` of the durable index file; null when it does not exist yet. */\n private async statIndexFile(): Promise<DiskFingerprint | null> {\n try {\n const s = await stat(this.filePath);\n return {\n mtimeMs: s.mtimeMs,\n size: s.size,\n dev: s.dev,\n ino: s.ino,\n ctimeMs: s.ctimeMs,\n birthtimeMs: s.birthtimeMs,\n };\n } catch (err) {\n if (isErrnoCode(err, \"ENOENT\")) return null;\n throw err;\n }\n }\n\n /** Record the current on-disk fingerprint as this instance's synced baseline. */\n private async captureSyncedFingerprint(): Promise<void> {\n try {\n this.lastSyncedFingerprint = await this.statIndexFile();\n } catch (err) {\n // A stat failure other than ENOENT leaves no baseline; the next freshness\n // check reports non-current and the caller confirms against the corpus.\n this.lastSyncedFingerprint = null;\n log.debug(`content-hash index: could not fingerprint ${this.filePath}: ${err}`);\n }\n }\n\n /**\n * True when the durable index file is unchanged since this instance last\n * synced it (load / save / reconcile). False when a peer advanced it, when\n * the file appeared or vanished, or when freshness cannot be established\n * (stat error) — the caller then treats the in-memory snapshot as\n * non-authoritative and confirms a dedup miss against the durable corpus.\n * The stat includes file identity and generation metadata so same-size,\n * same-mtime atomic replacements are detected without reading the file body.\n */\n async isDiskFingerprintCurrent(): Promise<boolean> {\n let current: DiskFingerprint | null;\n try {\n current = await this.statIndexFile();\n } catch {\n return false;\n }\n const last = this.lastSyncedFingerprint;\n if (last === null || current === null) {\n return last === null && current === null;\n }\n return (\n current.mtimeMs === last.mtimeMs &&\n current.size === last.size &&\n current.dev === last.dev &&\n current.ino === last.ino &&\n current.ctimeMs === last.ctimeMs &&\n current.birthtimeMs === last.birthtimeMs\n );\n }\n\n /** Check if content already exists in the index. */\n has(content: string): boolean {\n return this.hashes.has(ContentHashIndex.computeHash(content));\n }\n\n /** Add content hash to the index. */\n add(content: string): void {\n const hash = ContentHashIndex.computeHash(content);\n // A re-add supersedes a pending removal of the same hash (round 8 thread 3).\n this.removed.delete(hash);\n // Record OUR durable delta even when the local snapshot already holds the\n // hash (PR #2016 thread SD-nG): local membership can be STALE — a peer\n // removed this hash on disk while this instance kept it in memory. Skipping\n // `added`/`dirty` on that path let the reconciling save compute\n // (on-disk \\ removed) ∪ added and silently drop the reintroduced hash, so\n // the peer never saw it again. Set semantics keep this idempotent (no\n // duplicate entries) and the `removed.delete` above preserves remove/add\n // ordering.\n this.hashes.add(hash);\n this.added.add(hash);\n this.dirty = true;\n }\n\n get size(): number {\n return this.hashes.size;\n }\n\n /** Clear all loaded hashes so the next save rewrites the index from scratch. */\n clear(): void {\n if (this.hashes.size > 0) {\n this.hashes.clear();\n }\n // A full clear is always paired with a plain overwrite save() (rebuild);\n // pending per-hash add/remove deltas are subsumed by writing the rebuilt set.\n // Cancel any armed deferred-reconcile retry (PR #2016) so it cannot fire in\n // the clear→rebuild→save window and union the pre-clear on-disk rows back.\n this.added.clear();\n this.removed.clear();\n this.cancelReconcileRetry();\n this.dirty = true;\n }\n\n /** Persist index to disk if changed (plain whole-file overwrite). */\n async save(): Promise<void> {\n if (!this.dirty) return;\n await mkdir(path.dirname(this.filePath), { recursive: true });\n // Snapshot the deltas + overwrite body THIS save publishes BEFORE the write\n // yields the event loop. `writeMaybeEncryptedFile` materializes `serialized`\n // eagerly, then the disk await lets a concurrent same-process add()/remove()\n // land a late delta (PR #2016 thread PRRT_kwDORJXyws6SEBri). Clearing the\n // LIVE added/removed sets afterward — as the previous unconditional\n // clear()+`dirty = false` did — would drop that late hash from the index AND\n // leave no retry, so peers never see it until a corpus rebuild. Consume only\n // what we published; late deltas stay pending. Mirrors saveMergingWithDisk.\n const publishedAdded = new Set<string>(this.added);\n const publishedRemoved = new Set<string>(this.removed);\n const serialized = [...this.hashes].join(\"\\n\") + \"\\n\";\n await writeMaybeEncryptedFile(\n this.filePath,\n serialized,\n this.secureStoreWriteKeyProvider(),\n {},\n this.memoryDir\n );\n // Consume ONLY the deltas this overwrite published; anything that arrived\n // during the awaits above remains pending for the next save.\n for (const h of publishedAdded) this.added.delete(h);\n for (const h of publishedRemoved) this.removed.delete(h);\n // Honest dirty state: true iff late deltas remain unpersisted.\n this.dirty = this.added.size > 0 || this.removed.size > 0;\n // This overwrite is now the on-disk state — re-baseline the freshness\n // fingerprint so our own write is not later mistaken for a peer's (PR #2016).\n await this.captureSyncedFingerprint();\n if (this.dirty) {\n // Late deltas landed mid-save: arm a bounded durable retry so they reach\n // disk even without a further batch save — never a silent in-memory-only\n // hash. The locked reconcile republishes only OUR delta onto the overwrite.\n this.scheduleReconcileRetry();\n } else {\n // Clean overwrite supersedes any pending deferred reconcile work.\n this.cancelReconcileRetry();\n }\n log.debug(`content-hash index: saved ${this.hashes.size} hashes`);\n }\n\n /**\n * Persist the index by RECONCILING with the latest on-disk state under a\n * cross-process lock (issue #1909). Used by the extraction-persist batch save\n * for BOTH append and removal batches, so appends and removals to the same\n * index serialize against each other:\n * final = (on-disk \\ this.removed) ∪ this.added\n * Note it re-applies only OUR local additions (`added`), NOT the loaded\n * snapshot (review round 9 finding 3): seeding from `this.hashes` would\n * resurrect a hash a peer removed on disk while we held a stale snapshot. This\n * preserves a concurrent writer's appended hashes (a blind overwrite would\n * drop them), never resurrects a removed hash, and never loses our additions.\n *\n * Hardening:\n * - Dirty short-circuit (round 7 finding 3): when this instance neither added\n * nor removed anything this run, skip the O(file) read+rewrite entirely.\n * - Cross-process lock (round 7 finding 2): serialize the read→reconcile→write\n * window on a per-file advisory lock. If the lock is NOT acquired (timeout),\n * do NOT publish (round 9 finding 2): keep `dirty` set AND schedule a bounded\n * durable background retry (PR #2016) that keeps re-attempting the locked\n * publish until the deferred additions land — never an unlocked write that\n * could clobber a peer, and never a silent \"batch save complete\" that leaves\n * the addition in this process's memory only.\n */\n async saveMergingWithDisk(): Promise<void> {\n if (!this.dirty) return;\n // The advisory lock file lives beside the index — ensure the dir exists\n // before acquiring it (the index dir may not exist on a first write).\n await mkdir(path.dirname(this.filePath), { recursive: true });\n const publish = async (): Promise<void> => {\n // Snapshot the deltas this publish is responsible for. The read+rewrite\n // below yields the event loop, so a concurrent add()/remove() can land\n // new deltas while we await disk (PR #2016 thread SD7Tj). Consuming the\n // LIVE sets afterward would drop those late deltas AND clear `dirty`,\n // stranding them in no save at all. We consume only what we published;\n // anything that arrived meanwhile stays pending for the next save.\n const publishedAdded = new Set<string>(this.added);\n const publishedRemoved = new Set<string>(this.removed);\n const merged = new Set<string>(publishedAdded);\n try {\n const raw = await readMaybeEncryptedFile(\n this.filePath,\n this.secureStoreKeyProvider(),\n this.memoryDir\n );\n for (const line of raw.split(\"\\n\")) {\n const trimmed = line.trim();\n // Keep prior/concurrent on-disk hashes EXCEPT the ones we removed.\n if (trimmed.length > 0 && !publishedRemoved.has(trimmed)) merged.add(trimmed);\n }\n } catch (err) {\n if (err instanceof SecureStoreLockedError) throw err;\n if (!isErrnoCode(err, \"ENOENT\")) throw err;\n // ENOENT → no on-disk index yet; write our additions as-is.\n }\n await mkdir(path.dirname(this.filePath), { recursive: true });\n await writeMaybeEncryptedFile(\n this.filePath,\n [...merged].join(\"\\n\") + \"\\n\",\n this.secureStoreWriteKeyProvider(),\n {},\n this.memoryDir\n );\n // Consume ONLY the deltas we just published; deltas that arrived during\n // the awaits above remain pending.\n for (const h of publishedAdded) this.added.delete(h);\n for (const h of publishedRemoved) this.removed.delete(h);\n // Fold any still-pending deltas onto the reconciled set so the in-memory\n // view stays consistent with what the next save will publish.\n for (const h of this.added) merged.add(h);\n for (const h of this.removed) merged.delete(h);\n this.hashes = merged;\n // Honest dirty state: true iff late deltas remain to be persisted. A\n // subsequent batch save or the shutdown flush drains them.\n this.dirty = this.added.size > 0 || this.removed.size > 0;\n // The reconciled set is now the on-disk state — re-baseline the freshness\n // fingerprint (captured under the held lock) so our own publish is not\n // later mistaken for a peer's advance (PR #2016 review).\n await this.captureSyncedFingerprint();\n };\n await withHeldFileLock(\n `${this.filePath}.lock`,\n {\n staleMs: CONTENT_HASH_INDEX_LOCK_STALE_MS,\n ...(this.lockOptions.maxWaitMs !== undefined ? { maxWaitMs: this.lockOptions.maxWaitMs } : {}),\n ...(this.lockOptions.pollMs !== undefined ? { pollMs: this.lockOptions.pollMs } : {}),\n },\n async (acquired) => {\n if (!acquired) {\n // Never publish unlocked (round 9 finding 2): keep dirty AND schedule a\n // durable background retry (PR #2016). Relying on an unguaranteed later\n // batch save silently left the addition in this process's memory only;\n // the retry keeps re-attempting the locked publish until the addition\n // lands on disk.\n log.warn(\n `content-hash index: lock not acquired for ${this.filePath}; deferring reconcile save (dirty retained, scheduling durable retry)`,\n );\n this.scheduleReconcileRetry();\n return;\n }\n await publish();\n if (this.dirty) {\n // A concurrent add()/remove() landed during publish's disk awaits, so\n // deltas remain unpersisted (PR #2016 thread SD7Tj). Arm a bounded\n // durable retry so they reach disk even if no further batch save comes\n // — never leave a settled barrier over pending work.\n this.scheduleReconcileRetry();\n } else {\n // Published cleanly: any pending deferred retry is now satisfied.\n this.cancelReconcileRetry();\n }\n },\n );\n log.debug(`content-hash index: reconcile-saved ${this.hashes.size} hashes`);\n }\n\n /**\n * Rebuild-and-publish the index from an authoritative source (the durable\n * corpus) under the SAME per-file cross-process lock the reconciling\n * append/removal saves (`saveMergingWithDisk`) use (issue #1909 / PR #2016).\n * The `populate` callback runs WHILE the lock is held and MUST repopulate this\n * index from the corpus (`clear()` then `addByHash(...)` for every corpus\n * hash); the rebuilt set is then published with a plain overwrite that is now\n * serialized against every locked writer.\n *\n * This closes the finding where the previous unlocked clear→scan→save() could\n * overwrite a peer's newer lock-merged or deferred additions: a locked writer\n * that committed BEFORE the rebuild acquired the lock has its memory `.md` on\n * disk and is re-scanned; one that commits AFTER the rebuild releases\n * reconciles its additions on top of the freshly-published set. No union with\n * the raw on-disk file is performed, so the rebuild keeps its\n * garbage-collection property (orphaned hashes for deleted memories drop).\n *\n * Lock-acquisition failure is explicit and non-destructive: `populate` is NOT\n * run and nothing is written (never an unlocked overwrite). Returns true when\n * the rebuild was published under the lock, false when the lock timed out —\n * the caller keeps the index non-authoritative and retries on next use. Uses\n * the same non-reentrant file lock as the reconcile path, so it can never\n * deadlock with the deferred reconcile-retry: a concurrent locked writer (or a\n * fired retry) simply yields `acquired=false` after the bounded wait.\n */\n async rebuildUnderLock(populate: () => Promise<void>): Promise<boolean> {\n // The advisory lock file lives beside the index — ensure the dir exists\n // before acquiring it (the index dir may not exist on a first rebuild).\n await mkdir(path.dirname(this.filePath), { recursive: true });\n let published = false;\n await withHeldFileLock(\n `${this.filePath}.lock`,\n {\n staleMs: CONTENT_HASH_INDEX_LOCK_STALE_MS,\n ...(this.lockOptions.maxWaitMs !== undefined ? { maxWaitMs: this.lockOptions.maxWaitMs } : {}),\n ...(this.lockOptions.pollMs !== undefined ? { pollMs: this.lockOptions.pollMs } : {}),\n },\n async (acquired) => {\n if (!acquired) {\n // Never publish unlocked: a concurrent locked writer holds the lock,\n // so overwriting now could clobber its addition. Leave the index as-is\n // and let the caller retry the authoritative rebuild on next use.\n log.warn(\n `content-hash index: lock not acquired for ${this.filePath}; deferring authoritative rebuild (index left non-authoritative, retried on next use)`,\n );\n return;\n }\n await populate();\n // Publish under the held lock. save() takes no lock (no re-entrancy) and\n // is a full overwrite that supersedes any pending add/remove deltas.\n await this.save();\n published = true;\n },\n );\n return published;\n }\n\n /**\n * True while a background durable reconcile-save retry is armed (PR #2016).\n * Test/introspection hook.\n */\n get hasPendingReconcileRetry(): boolean {\n return this.reconcileRetryTimer !== null;\n }\n\n /** True while this index has in-memory changes not yet published to disk. */\n get hasPendingChanges(): boolean {\n return this.dirty;\n }\n\n /**\n * Resolve once any armed background reconcile-save retry chain has settled —\n * either it published the deferred additions, or it exhausted its bounded\n * attempts and fell back to the corpus-rebuild safety net. Resolves\n * immediately when nothing is armed. Lets a caller (or a test) await the\n * eventual durability outcome of a lock-timed-out save.\n */\n async whenReconcileRetrySettled(): Promise<void> {\n await (this.reconcileRetryBarrier ?? Promise.resolve());\n }\n\n /**\n * Drive any deferred lock-timeout reconcile save to completion INLINE at a\n * lifecycle boundary (PR #2016 finding 3). The background retry timer is\n * `unref`'d so it never keeps a long-lived daemon alive — but a short-lived\n * writer (a one-shot CLI) can exit before it fires, leaving a durable fact\n * `.md` whose hash never reached `fact-hashes.txt` for peers that already\n * built their in-memory index. `orchestrator.destroy()` calls this so the\n * addition publishes before the process exits; long-lived hosts only reach it\n * at their own shutdown, so their in-flight retries keep running in the\n * background until then. Bounded by the same attempt ceiling as the background\n * retry; a permanently contended lock falls back to the corpus-rebuild-on-\n * restart safety net (the fact `.md` is already durable). No deadlock: each\n * attempt is the same non-reentrant, bounded-wait file lock.\n */\n async flushReconcileRetry(): Promise<void> {\n if (!this.dirty) return;\n const maxAttempts = this.lockOptions.retryMaxAttempts ?? CONTENT_HASH_INDEX_RETRY_MAX_ATTEMPTS;\n const baseMs = this.lockOptions.retryBaseMs ?? CONTENT_HASH_INDEX_RETRY_BASE_MS;\n for (let attempt = 0; this.dirty && attempt < maxAttempts; attempt += 1) {\n // Cancel the unref'd background timer — we drive the locked publish inline.\n if (this.reconcileRetryTimer) {\n clearTimeout(this.reconcileRetryTimer);\n this.reconcileRetryTimer = null;\n }\n await this.saveMergingWithDisk();\n if (!this.dirty) break;\n if (attempt < maxAttempts - 1) {\n const wait = Math.min(baseMs * 2 ** attempt, CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS);\n await new Promise<void>((resolve) => setTimeout(resolve, wait));\n }\n }\n // Clear any timer the final saveMergingWithDisk armed and settle the barrier\n // so awaiters unblock — the bounded inline drain has done its best.\n if (this.reconcileRetryTimer) {\n clearTimeout(this.reconcileRetryTimer);\n this.reconcileRetryTimer = null;\n }\n this.reconcileRetryAttempts = 0;\n this.settleReconcileRetry();\n }\n\n /**\n * Schedule ONE bounded, exponentially-backed-off background retry of the\n * deferred reconcile save (PR #2016). Reentrant-safe: an already-armed timer\n * short-circuits, so successive timed-out saves never stack duplicate retries.\n * When attempts are exhausted the chain settles without publishing — the\n * durable fact `.md` is on disk and a process restart rebuilds the index from\n * the corpus, so this degrades to that safety net, never a fatal failure.\n */\n private scheduleReconcileRetry(): void {\n if (this.reconcileRetryTimer) return; // one retry in flight — no duplicate/reentrant retries\n const maxAttempts = this.lockOptions.retryMaxAttempts ?? CONTENT_HASH_INDEX_RETRY_MAX_ATTEMPTS;\n if (this.reconcileRetryAttempts >= maxAttempts) {\n log.warn(\n `content-hash index: exhausted ${maxAttempts} deferred reconcile-save retries for ${this.filePath}; ` +\n `${this.added.size} addition(s) remain dirty in-memory (a process restart rebuilds the index from the durable corpus)`,\n );\n this.reconcileRetryAttempts = 0;\n this.settleReconcileRetry();\n return;\n }\n if (!this.reconcileRetryBarrier) {\n this.reconcileRetryBarrier = new Promise<void>((resolve) => {\n this.reconcileRetryResolve = resolve;\n });\n }\n const attempt = this.reconcileRetryAttempts + 1;\n const baseMs = this.lockOptions.retryBaseMs ?? CONTENT_HASH_INDEX_RETRY_BASE_MS;\n const delay = Math.min(baseMs * 2 ** (attempt - 1), CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS);\n this.reconcileRetryTimer = setTimeout(() => {\n this.reconcileRetryTimer = null;\n this.reconcileRetryAttempts = attempt;\n void this.saveMergingWithDisk()\n .catch((err) => {\n // A publish-time I/O/encryption error (not a lock timeout) is best-effort:\n // log and let the corpus-rebuild safety net cover it rather than loop.\n log.warn(`content-hash index: deferred reconcile-save retry failed for ${this.filePath}: ${err}`);\n })\n .finally(() => {\n // If the attempt neither published (dirty cleared → cancelReconcileRetry)\n // nor re-armed a further retry, the chain has ended — settle the barrier.\n if (this.dirty && !this.reconcileRetryTimer) this.settleReconcileRetry();\n });\n }, delay);\n this.reconcileRetryTimer.unref?.();\n }\n\n /** Cancel any armed reconcile-save retry and settle the barrier (a publish landed). */\n private cancelReconcileRetry(): void {\n if (this.reconcileRetryTimer) {\n clearTimeout(this.reconcileRetryTimer);\n this.reconcileRetryTimer = null;\n }\n this.reconcileRetryAttempts = 0;\n this.settleReconcileRetry();\n }\n\n /** Resolve and clear the retry barrier if one is outstanding. */\n private settleReconcileRetry(): void {\n const resolve = this.reconcileRetryResolve;\n this.reconcileRetryBarrier = null;\n this.reconcileRetryResolve = null;\n resolve?.();\n }\n\n /** Remove a hash from the index (used when archiving/deleting). */\n remove(content: string): void {\n const hash = ContentHashIndex.computeHash(content);\n // Record the removal (round 8 thread 3) even when the hash is not in our\n // in-memory set — a reconciling save must still drop it from the latest\n // on-disk state. A removal supersedes a pending add. Always mark dirty.\n this.added.delete(hash);\n this.removed.add(hash);\n this.hashes.delete(hash);\n this.dirty = true;\n }\n\n /**\n * Remove a pre-computed SHA-256 hash directly from the index without\n * re-hashing. Use this when the caller already holds the stored hash\n * (e.g. `memory.frontmatter.contentHash`) to avoid the double-hash bug\n * where `remove(hash)` would compute `hash(hash)` and never match the\n * entry.\n */\n removeByHash(hash: string): void {\n this.added.delete(hash);\n this.removed.add(hash);\n this.hashes.delete(hash);\n this.dirty = true;\n }\n\n /**\n * Add a pre-computed SHA-256 hash directly to the index without re-hashing.\n * Use this when the caller already holds the stored hash\n * (e.g. `memory.frontmatter.contentHash`) so that the index records the raw\n * content hash rather than re-hashing the citation-annotated body.\n *\n * @internal Only called from `StorageManager.ensureFactHashIndexAuthoritative`.\n * Not part of the public API — prefer `add(content)` for external callers.\n */\n addByHash(hash: string): void {\n // A re-add supersedes a pending removal of the same hash.\n this.removed.delete(hash);\n // Record OUR durable delta even when the local snapshot already holds the\n // hash (PR #2016 thread PRRT_kwDORJXyws6SEHve). Outside a rebuild\n // (StorageManager.addActiveFactContentHash on reactivation) local\n // membership can be STALE: a peer removed this hash on disk while this\n // instance kept it in memory. Skipping added/dirty on that path let the\n // reconciling save compute (on-disk \\ removed) ∪ added and silently drop\n // the reintroduced hash, so the peer never saw it again. Set semantics keep\n // this idempotent (no duplicate entries) and the removed.delete above\n // preserves remove/add ordering. Mirrors add().\n this.hashes.add(hash);\n this.added.add(hash);\n this.dirty = true;\n }\n\n /** Normalize content (delegates to content-hash.ts for a single source of truth). */\n static normalizeContent(content: string): string {\n return normalizeContent(content);\n }\n\n /** Compute SHA-256 hash (delegates to content-hash.ts for a single source of truth). */\n static computeHash(content: string): string {\n return computeContentHash(content);\n }\n}\n","import { AsyncLocalStorage } from \"node:async_hooks\";\nimport { createHash } from \"node:crypto\";\nimport { mkdir } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { withHeldFileLock } from \"../utils/serialize-mutations.js\";\nimport type { MemoryFile, MemoryFrontmatter } from \"../types.js\";\nconst TOMBSTONE_CAPTURE_WRITE_LOCK_STALE_MS = 60_000;\nconst CAPTURE_WRITE_LOCK_MAX_ATTEMPTS = 3;\nconst CAPTURE_WRITE_LOCK_RETRY_BASE_MS = 25;\nconst CAPTURE_WRITE_LOCK_BUSY_MESSAGE = \"tombstone-blocked capture write lock remained busy\";\n\nexport function isTombstoneBlockedCaptureWriteLockBusy(error: unknown): boolean {\n return error instanceof Error && error.message === CAPTURE_WRITE_LOCK_BUSY_MESSAGE;\n}\n\nexport function normalizeExplicitCaptureContent(value: string): string {\n return value.toLowerCase().replace(/\\s+/g, \" \").trim();\n}\n\nexport function buildExplicitCaptureDedupKey(\n content: string,\n category: string,\n sourceConnector: string | undefined\n): string {\n const encode = (label: string, value: string): string => `${label} ${value.length} ${value}`;\n return [\n encode(\"category\", category),\n encode(\"connector\", sourceConnector?.trim() ?? \"\"),\n encode(\"content\", normalizeExplicitCaptureContent(content)),\n ].join(\" \");\n}\n\nexport function captureIdentityHash(memory: MemoryFile): string {\n return createHash(\"sha256\")\n .update(buildExplicitCaptureDedupKey(memory.content, memory.frontmatter.category, memory.frontmatter.sourceConnector))\n .digest(\"hex\");\n}\n\nexport type TombstoneBlockedCaptureWriteLockOptions = {\n readonly stateDir: string;\n readonly withHeldFileLock?: typeof withHeldFileLock;\n};\n\nexport class TombstoneBlockedCaptureWriteLock {\n private readonly context = new AsyncLocalStorage<ReadonlySet<string>>();\n\n constructor(private readonly options: TombstoneBlockedCaptureWriteLockOptions) {}\n\n private captureWriteLockPathForHash(identityHash: string): string {\n const filename =\n identityHash.length === 0\n ? \"explicit-capture-write.lock\"\n : `explicit-capture-write-${identityHash}.lock`;\n return path.join(this.options.stateDir, \"tombstone-blocked-capture\", filename);\n }\n\n private async withSingleCaptureWriteLock<T>(lockPath: string, task: () => Promise<T>): Promise<T> {\n await mkdir(path.dirname(lockPath), { recursive: true });\n const retry = Symbol(\"retry\");\n const permanentFailure = Symbol(\"permanent-failure\");\n const runWithHeldFileLock = this.options.withHeldFileLock ?? withHeldFileLock;\n for (let attempt = 0; attempt < CAPTURE_WRITE_LOCK_MAX_ATTEMPTS; attempt += 1) {\n const result = await runWithHeldFileLock<T | typeof retry | typeof permanentFailure>(\n lockPath,\n {\n staleMs: TOMBSTONE_CAPTURE_WRITE_LOCK_STALE_MS,\n maxWaitMs: 1_000,\n pollMs: 25,\n },\n async (acquired, controller) => {\n if (acquired) return await task();\n return controller.failure === \"error\" ? permanentFailure : retry;\n }\n );\n if (result === permanentFailure) {\n throw new Error(\"tombstone-blocked capture write lock acquisition failed\");\n }\n if (result !== retry) return result;\n if (attempt === CAPTURE_WRITE_LOCK_MAX_ATTEMPTS - 1) break;\n const delayState = Promise.withResolvers<void>();\n setTimeout(delayState.resolve, CAPTURE_WRITE_LOCK_RETRY_BASE_MS * 2 ** attempt);\n await delayState.promise;\n }\n throw new Error(CAPTURE_WRITE_LOCK_BUSY_MESSAGE);\n }\n private async withPhysicalCaptureWriteLocks<T>(\n task: () => Promise<T>,\n identityHashes: readonly string[]\n ): Promise<T> {\n const lockPaths = [...new Set(identityHashes.map((hash) => this.captureWriteLockPathForHash(hash)))].sort();\n const acquire = async (index: number): Promise<T> => {\n if (index === lockPaths.length) return await task();\n return await this.withSingleCaptureWriteLock(lockPaths[index], () => acquire(index + 1));\n };\n return await acquire(0);\n }\n\n\n async withCaptureWriteLock<T>(\n task: () => Promise<T>,\n identity?: string | readonly string[]\n ): Promise<T> {\n const identities =\n Array.isArray(identity) && identity.length > 0 ? identity : [typeof identity === \"string\" ? identity : \"\"];\n return await this.withCaptureWriteLocks(task, identities, []);\n }\n\n async withCaptureWriteLockHashes<T>(\n task: () => Promise<T>,\n identityHashes: readonly string[]\n ): Promise<T> {\n return await this.withCaptureWriteLocks(task, [], identityHashes);\n }\n\n async withCaptureWriteLockAndHashes<T>(\n task: () => Promise<T>,\n identity: string | readonly string[] | undefined,\n identityHashes: readonly string[]\n ): Promise<T> {\n const identities =\n Array.isArray(identity) && identity.length > 0 ? identity : [typeof identity === \"string\" ? identity : \"\"];\n return await this.withCaptureWriteLocks(task, identities, identityHashes);\n }\n\n private async withCaptureWriteLocks<T>(\n task: () => Promise<T>,\n identities: readonly string[],\n identityHashes: readonly string[]\n ): Promise<T> {\n const current = this.context.getStore();\n const hashToken = (key: string): string =>\n `\\u0000${key.length === 0 ? \"\" : createHash(\"sha256\").update(key).digest(\"hex\")}`;\n const identityHash = (key: string): string =>\n key.length === 0 ? \"\" : createHash(\"sha256\").update(key).digest(\"hex\");\n const identityKeys = [...new Set(identities)];\n const hashes = [...new Set(identityHashes)];\n const missingIdentityHashes = identityKeys\n .filter((key) => !current || (!current.has(key) && !current.has(hashToken(key))))\n .map(identityHash);\n const missingHashes = hashes.filter((hash) => !current || !current.has(`\\u0000${hash}`));\n const physicalMissing = [...new Set([...missingIdentityHashes, ...missingHashes])];\n if (current && physicalMissing.length > 0) {\n const heldLockPaths = [...current]\n .filter((token) => token.startsWith(\"\\u0000\"))\n .map((token) => this.captureWriteLockPathForHash(token.slice(1)));\n const requestedLockPaths = physicalMissing.map((hash) => this.captureWriteLockPathForHash(hash));\n if (heldLockPaths.some((held) => requestedLockPaths.some((requested) => requested < held))) {\n throw new Error(CAPTURE_WRITE_LOCK_BUSY_MESSAGE);\n }\n }\n const next = new Set(current);\n for (const key of identityKeys) {\n next.add(key);\n next.add(hashToken(key));\n }\n for (const hash of hashes) next.add(`\\u0000${hash}`);\n const run = () => this.context.run(next, task);\n if (physicalMissing.length === 0) return await run();\n return await this.withPhysicalCaptureWriteLocks(run, physicalMissing);\n }\n}\n\n\nexport type TombstoneBlockedMutationHost = {\n readCurrent: () => Promise<MemoryFile | null>;\n isBlocked: (memory: MemoryFile | null) => boolean;\n isQueuedReview: (memory: MemoryFile | null) => boolean;\n memoryIdentity: (memory: MemoryFile) => string;\n prepareWrite: () => Promise<string>;\n commitWrite: (marker: string) => Promise<void>;\n discardWrite: (marker: string) => Promise<void>;\n markUntrusted: () => void;\n writeStorageSecureFile: (pathname: string, fileContent: string) => Promise<void>;\n withCaptureWriteLock: (task: () => Promise<void>, identity: string | readonly string[]) => Promise<void>;\n logWarning: (message: string) => void;\n};\n\nexport type TombstoneBlockedMutation = {\n blocked: boolean;\n pathname: string;\n fileContent: string;\n identity: string | readonly string[];\n updateIndex: (rebuildMarker?: string, current?: MemoryFile | null) => Promise<void>;\n beforeIndexUpdate?: () => Promise<void>;\n coordinate?: boolean;\n};\n\nexport async function runTombstoneBlockedMutation(\n host: TombstoneBlockedMutationHost,\n mutation: TombstoneBlockedMutation,\n): Promise<void> {\n let lockIdentity: string | readonly string[] = mutation.identity;\n let mustLock = mutation.blocked || mutation.coordinate === true;\n for (;;) {\n let retryIdentity: string | undefined;\n const mutate = async (): Promise<void> => {\n const current = await host.readCurrent();\n const currentBlocked = host.isBlocked(current);\n const currentQueuedReview = host.isQueuedReview(current);\n if (current !== null && (currentBlocked || currentQueuedReview)) {\n const currentIdentity = host.memoryIdentity(current);\n const heldIdentities = Array.isArray(lockIdentity) ? lockIdentity : [lockIdentity];\n if (!heldIdentities.includes(currentIdentity)) {\n retryIdentity = currentIdentity;\n return;\n }\n }\n const rebuildMarker = mutation.blocked || currentBlocked ? await host.prepareWrite() : undefined;\n try {\n await host.writeStorageSecureFile(mutation.pathname, mutation.fileContent);\n } catch (err) {\n if (rebuildMarker) {\n try {\n await host.discardWrite(rebuildMarker);\n } catch (cleanupError) {\n host.markUntrusted();\n host.logWarning(`storage.tombstoneBlocked failed to clear write marker: ${cleanupError}`);\n }\n }\n throw err;\n }\n if (rebuildMarker) {\n try {\n await host.commitWrite(rebuildMarker);\n } catch (err) {\n host.markUntrusted();\n host.logWarning(`storage.tombstoneBlocked committed write marker failed: ${err}`);\n }\n }\n if (rebuildMarker) await mutation.beforeIndexUpdate?.();\n await mutation.updateIndex(rebuildMarker, currentBlocked && current !== null ? current : undefined);\n };\n if (mustLock) {\n await host.withCaptureWriteLock(mutate, lockIdentity);\n } else {\n await mutate();\n }\n if (retryIdentity === undefined) return;\n const identities = Array.isArray(lockIdentity)\n ? [...lockIdentity, retryIdentity]\n : [lockIdentity, retryIdentity];\n lockIdentity = [...new Set(identities)];\n mustLock = true;\n }\n}\n\nexport function tombstoneBlocked(frontmatter: MemoryFrontmatter): boolean {\n return frontmatter.status === \"pending_review\" && Boolean(frontmatter.blockedBy);\n}\n\nexport function isQueuedReviewFrontmatter(frontmatter: MemoryFrontmatter): boolean {\n return frontmatter.status === \"pending_review\" && (frontmatter.tags ?? []).includes(\"queued-review\");\n}\n\nexport function isQueuedReviewMemory(memory: MemoryFile | null): memory is MemoryFile {\n return memory !== null && isQueuedReviewFrontmatter(memory.frontmatter);\n}\n\nexport type TombstoneBlockedChunkMutation = {\n blocked: boolean;\n id: string;\n identity: string;\n findDuplicate: () => Promise<MemoryFile | null>;\n persistMemory: () => Promise<void>;\n afterWrite: () => Promise<void>;\n withCaptureWriteLock: (task: () => Promise<string>, identity: string) => Promise<string>;\n};\n\nexport async function runTombstoneBlockedChunkMutation(\n mutation: TombstoneBlockedChunkMutation,\n): Promise<string> {\n const persist = async (): Promise<string> => {\n if (mutation.blocked) {\n const duplicate = await mutation.findDuplicate();\n if (duplicate) return duplicate.frontmatter.id;\n }\n await mutation.persistMemory();\n await mutation.afterWrite();\n return mutation.id;\n };\n return mutation.blocked\n ? await mutation.withCaptureWriteLock(persist, mutation.identity)\n : await persist();\n}\n","import { createHash, randomUUID } from \"node:crypto\";\nimport { unlink } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { isDeepStrictEqual } from \"node:util\";\nimport { log } from \"../logger.js\";\nimport type { MemoryFile, MemoryFrontmatter } from \"../types.js\";\nimport { markProjectedMemoryPathInvalid } from \"../memory-projection-store.js\";\nimport { RECALL_FALLBACK_DIRS } from \"../utils/category-dir.js\";\nimport { isErrnoCode } from \"../utils/errno.js\";\nimport {\n readMaybeEncryptedFileFromChunks,\n writeMaybeEncryptedFileFromChunks,\n} from \"../secure-store/secure-fs.js\";\nimport {\n buildExplicitCaptureDedupKey,\n captureIdentityHash,\n tombstoneBlocked,\n isQueuedReviewFrontmatter,\n isQueuedReviewMemory,\n runTombstoneBlockedChunkMutation,\n runTombstoneBlockedMutation,\n type TombstoneBlockedMutationHost,\n} from \"./tombstone-blocked-capture-mutation.js\";\nimport { TombstoneBlockedCaptureIndex } from \"./tombstone-blocked-capture-index.js\";\nimport type { TombstoneBlockedCaptureIndexOptions } from \"./tombstone-blocked-capture-index.js\";\n\nconst OFFLINE_SYNC_BUFFER_LIMIT_BYTES = 1_048_576;\nconst OFFLINE_SYNC_STAGE_READ_BYTES = 64 * 1024;\nconst OFFLINE_SYNC_FRONTMATTER_DELIMITER = Buffer.from(\"\\n---\\n\", \"utf8\");\n\ntype StreamedOfflineSyncIdentity = {\n identityHash: string;\n};\n\ntype PreparedOfflineSyncChunks = {\n after: MemoryFile | null;\n chunks: AsyncIterable<Buffer>;\n stagePath?: string;\n streamedIdentity?: StreamedOfflineSyncIdentity;\n};\n\nfunction createExplicitContentNormalizer(onChunk: (chunk: string) => void): {\n push: (text: string) => void;\n finish: () => void;\n} {\n let started = false;\n let pendingSpace = false;\n let output = \"\";\n const flush = () => {\n if (output.length > 0) {\n onChunk(output);\n output = \"\";\n }\n };\n return {\n push(text) {\n for (const character of text.toLowerCase()) {\n if (/\\s/u.test(character)) {\n if (started) pendingSpace = true;\n continue;\n }\n if (pendingSpace) {\n output += \" \";\n pendingSpace = false;\n }\n output += character;\n started = true;\n if (output.length >= OFFLINE_SYNC_STAGE_READ_BYTES) flush();\n }\n },\n finish: flush,\n };\n}\n\nasync function forEachStagedTextChunk(\n stagePath: string,\n offset: number,\n onText: (text: string) => void,\n key: Buffer | null,\n memoryDir: string\n): Promise<void> {\n const decoder = new TextDecoder(\"utf-8\");\n let remaining = offset;\n for await (const chunk of readMaybeEncryptedFileFromChunks(stagePath, key, memoryDir)) {\n const textChunk =\n remaining >= chunk.length\n ? null\n : chunk.subarray(remaining);\n remaining = Math.max(0, remaining - chunk.length);\n if (textChunk === null || textChunk.length === 0) continue;\n const text = decoder.decode(textChunk, { stream: true });\n if (text.length > 0) onText(text);\n }\n const finalText = decoder.decode();\n if (finalText.length > 0) onText(finalText);\n}\n\nasync function findStagedBodyOffset(\n stagePath: string,\n key: Buffer | null,\n memoryDir: string\n): Promise<number | null> {\n let scannedBytes = 0;\n let tail = Buffer.alloc(0);\n for await (const chunk of readMaybeEncryptedFileFromChunks(stagePath, key, memoryDir)) {\n const window = tail.length > 0 ? Buffer.concat([tail, chunk]) : chunk;\n const windowStart = scannedBytes - tail.length;\n for (let searchFrom = 0; ; ) {\n const delimiterStart = window.indexOf(OFFLINE_SYNC_FRONTMATTER_DELIMITER, searchFrom);\n if (delimiterStart < 0) break;\n if (windowStart + delimiterStart >= 4) {\n return windowStart + delimiterStart + OFFLINE_SYNC_FRONTMATTER_DELIMITER.length;\n }\n searchFrom = delimiterStart + 1;\n }\n scannedBytes += chunk.length;\n const tailLength = Math.min(OFFLINE_SYNC_FRONTMATTER_DELIMITER.length - 1, window.length);\n tail = Buffer.from(window.subarray(window.length - tailLength));\n }\n return null;\n}\n\nasync function readStagedPrefix(\n stagePath: string,\n byteLimit: number,\n key: Buffer | null,\n memoryDir: string\n): Promise<Buffer> {\n const chunks: Buffer[] = [];\n let bytesRead = 0;\n for await (const chunk of readMaybeEncryptedFileFromChunks(stagePath, key, memoryDir)) {\n const length = Math.min(chunk.length, byteLimit - bytesRead);\n if (length > 0) chunks.push(chunk.subarray(0, length));\n bytesRead += length;\n if (bytesRead >= byteLimit) break;\n }\n return Buffer.concat(chunks, bytesRead);\n}\n\nexport type OfflineSyncMemoryParser = (filePath: string, content: Buffer) => MemoryFile | null;\n\nexport function parseTombstoneBlockedOfflineSyncMemory(\n filePath: string,\n content: Buffer,\n memoryDir: string,\n parseFrontmatter: (raw: string) => { frontmatter: MemoryFrontmatter; content: string } | null,\n normalizeFrontmatterForPath: (frontmatter: MemoryFrontmatter, pathRel: string, content?: string) => MemoryFrontmatter,\n toMemoryPathRel: (baseDir: string, filePath: string) => string\n): MemoryFile | null {\n const parsed = parseFrontmatter(content.toString(\"utf8\"));\n if (!parsed) return null;\n return {\n path: filePath,\n frontmatter: normalizeFrontmatterForPath(parsed.frontmatter, toMemoryPathRel(memoryDir, filePath), parsed.content),\n content: parsed.content,\n };\n}\n\nexport abstract class TombstoneBlockedCaptureIndexHost {\n private tombstoneBlockedCaptureIndex: TombstoneBlockedCaptureIndex | null = null;\n protected abstract tombstoneBlockedCaptureIndexOptions(): TombstoneBlockedCaptureIndexOptions;\n protected abstract writeStorageSecureFile(\n filePath: string,\n content: string | Buffer,\n forceEncrypt?: boolean\n ): Promise<void>;\n protected abstract assertManagedStoragePath(filePath: string, method: string): string;\n protected abstract notifyCatalogWriteForPath(filePath: string): void;\n protected abstract readMemoryByPath(filePath: string): Promise<MemoryFile | null>;\n protected abstract invalidateAllMemoriesCache(): void;\n protected abstract invalidateKnowledgeIndexCache(): void;\n protected abstract invalidateColdMemoriesCache(): void;\n protected abstract bumpArtifactWriteVersion(): number;\n protected abstract bumpMemoryStatusVersion(): void;\n protected abstract bumpMemoryCorpusVersion(): void;\n protected abstract markFactHashIndexNotAuthoritative(): void;\n\n private async writeStorageSecureFileChunks(filePath: string, chunks: AsyncIterable<Buffer>): Promise<void> {\n const options = this.tombstoneBlockedCaptureIndexOptions();\n await writeMaybeEncryptedFileFromChunks(\n filePath,\n chunks,\n options.secureStoreWriteKeyProvider(),\n {},\n options.memoryDir\n );\n this.notifyCatalogWriteForPath(filePath);\n }\n\n protected getTombstoneBlockedCaptureIndex(): TombstoneBlockedCaptureIndex {\n if (this.tombstoneBlockedCaptureIndex === null) {\n this.tombstoneBlockedCaptureIndex = new TombstoneBlockedCaptureIndex(this.tombstoneBlockedCaptureIndexOptions());\n }\n return this.tombstoneBlockedCaptureIndex;\n }\n async withTombstoneBlockedCaptureWriteLock<T>(\n task: () => Promise<T>,\n identity?: string | readonly string[]\n ): Promise<T> {\n return await this.getTombstoneBlockedCaptureIndex().withCaptureWriteLock(task, identity);\n }\n\n protected async withTombstoneBlockedCaptureWriteLockAndHashes<T>(\n task: () => Promise<T>,\n identity: string | readonly string[] | undefined,\n identityHashes: readonly string[]\n ): Promise<T> {\n return await this.getTombstoneBlockedCaptureIndex().withCaptureWriteLockAndHashes(task, identity, identityHashes);\n }\n\n private async writeTombstoneBlockedMutation(\n blocked: boolean,\n pathname: string,\n fileContent: string,\n identity: string | readonly string[],\n updateIndex: (rebuildMarker?: string, current?: MemoryFile | null) => Promise<void>,\n beforeIndexUpdate?: () => Promise<void>,\n coordinate = false\n ): Promise<void> {\n const host: TombstoneBlockedMutationHost = {\n readCurrent: () => this.readMemoryByPath(pathname),\n isBlocked: (memory) => this.isTombstoneBlockedMemory(memory),\n isQueuedReview: (memory) => isQueuedReviewMemory(memory),\n memoryIdentity: (memory) => this.offlineSyncMemoryIdentity(memory),\n prepareWrite: () => this.getTombstoneBlockedCaptureIndex().prepareWrite(),\n commitWrite: (marker) => this.getTombstoneBlockedCaptureIndex().commitWrite(marker),\n discardWrite: (marker) => this.getTombstoneBlockedCaptureIndex().discardWrite(marker),\n markUntrusted: () => this.getTombstoneBlockedCaptureIndex().markUntrusted(),\n writeStorageSecureFile: (target, content) => this.writeStorageSecureFile(target, content),\n withCaptureWriteLock: (task, lockIdentity) =>\n this.withTombstoneBlockedCaptureWriteLock(task, lockIdentity),\n logWarning: (message) => log.warn(message),\n };\n await runTombstoneBlockedMutation(host, {\n blocked,\n pathname,\n fileContent,\n identity,\n updateIndex,\n beforeIndexUpdate,\n coordinate,\n });\n }\n\n protected async writeTombstoneBlockedMemory(\n pathname: string,\n fileContent: string,\n frontmatter: MemoryFrontmatter,\n content: string,\n beforeIndexUpdate?: () => Promise<void>\n ): Promise<void> {\n await this.writeTombstoneBlockedMutation(\n tombstoneBlocked(frontmatter),\n pathname,\n fileContent,\n buildExplicitCaptureDedupKey(content, frontmatter.category, frontmatter.sourceConnector),\n (rebuildMarker) =>\n this.getTombstoneBlockedCaptureIndex().addWrittenMemory(pathname, frontmatter, content, rebuildMarker),\n beforeIndexUpdate\n );\n }\n\n protected async writeTombstoneBlockedChunk(\n pathname: string,\n fileContent: string,\n frontmatter: MemoryFrontmatter,\n content: string,\n findDuplicate: () => Promise<MemoryFile | null>,\n afterWrite: () => Promise<void>\n ): Promise<string> {\n return await runTombstoneBlockedChunkMutation({\n blocked: tombstoneBlocked(frontmatter),\n id: frontmatter.id,\n identity: buildExplicitCaptureDedupKey(content, frontmatter.category, frontmatter.sourceConnector),\n findDuplicate,\n persistMemory: () => this.writeTombstoneBlockedMemory(pathname, fileContent, frontmatter, content),\n afterWrite,\n withCaptureWriteLock: (task, identity) => this.withTombstoneBlockedCaptureWriteLock(task, identity),\n });\n }\n\n protected async writeTombstoneBlockedUpdate(\n before: MemoryFile,\n fileContent: string,\n frontmatter: MemoryFrontmatter,\n content: string,\n beforeIndexUpdate?: () => Promise<void>\n ): Promise<void> {\n await this.writeTombstoneBlockedMutation(\n tombstoneBlocked(before.frontmatter) || tombstoneBlocked(frontmatter),\n before.path,\n fileContent,\n [\n ...(tombstoneBlocked(before.frontmatter)\n ? [\n buildExplicitCaptureDedupKey(\n before.content,\n before.frontmatter.category,\n before.frontmatter.sourceConnector\n ),\n ]\n : []),\n ...(tombstoneBlocked(frontmatter)\n ? [buildExplicitCaptureDedupKey(content, frontmatter.category, frontmatter.sourceConnector)]\n : []),\n ...(isQueuedReviewMemory(before)\n ? [\n buildExplicitCaptureDedupKey(\n before.content,\n before.frontmatter.category,\n before.frontmatter.sourceConnector\n ),\n ]\n : []),\n ...(isQueuedReviewFrontmatter(frontmatter)\n ? [buildExplicitCaptureDedupKey(content, frontmatter.category, frontmatter.sourceConnector)]\n : []),\n ],\n (rebuildMarker, current) =>\n this.getTombstoneBlockedCaptureIndex().syncUpdatedMemory(\n current ?? before,\n frontmatter,\n content,\n rebuildMarker\n ),\n beforeIndexUpdate,\n isQueuedReviewMemory(before) || isQueuedReviewFrontmatter(frontmatter)\n );\n }\n\n protected async writeTombstoneBlockedFrontmatter(\n before: MemoryFile,\n fileContent: string,\n frontmatter: MemoryFrontmatter,\n beforeIndexUpdate?: () => Promise<void>\n ): Promise<void> {\n await this.writeTombstoneBlockedMutation(\n tombstoneBlocked(before.frontmatter) || tombstoneBlocked(frontmatter),\n before.path,\n fileContent,\n [\n ...(tombstoneBlocked(before.frontmatter)\n ? [\n buildExplicitCaptureDedupKey(\n before.content,\n before.frontmatter.category,\n before.frontmatter.sourceConnector\n ),\n ]\n : []),\n ...(tombstoneBlocked(frontmatter)\n ? [buildExplicitCaptureDedupKey(before.content, frontmatter.category, frontmatter.sourceConnector)]\n : []),\n ...(isQueuedReviewMemory(before)\n ? [\n buildExplicitCaptureDedupKey(\n before.content,\n before.frontmatter.category,\n before.frontmatter.sourceConnector\n ),\n ]\n : []),\n ...(isQueuedReviewFrontmatter(frontmatter)\n ? [buildExplicitCaptureDedupKey(before.content, frontmatter.category, frontmatter.sourceConnector)]\n : []),\n ],\n (rebuildMarker, current) =>\n this.getTombstoneBlockedCaptureIndex().syncUpdatedFrontmatter(\n current ?? before,\n frontmatter,\n rebuildMarker\n ),\n beforeIndexUpdate,\n isQueuedReviewMemory(before) || isQueuedReviewFrontmatter(frontmatter)\n );\n }\n\n private isTombstoneBlockedMemory(memory: MemoryFile | null): memory is MemoryFile {\n return memory !== null && memory.frontmatter.status === \"pending_review\" && Boolean(memory.frontmatter.blockedBy);\n }\n\n private offlineSyncMemoryIdentity(memory: MemoryFile): string {\n return buildExplicitCaptureDedupKey(\n memory.content,\n memory.frontmatter.category,\n memory.frontmatter.sourceConnector\n );\n }\n private async isDuplicateTombstoneBlockedOfflineSync(\n target: string,\n incoming: MemoryFile,\n streamedIdentity?: StreamedOfflineSyncIdentity\n ): Promise<boolean> {\n const before = await this.readMemoryByPath(target);\n const duplicate = streamedIdentity\n ? true\n : await this.getTombstoneBlockedCaptureIndex().has(\n incoming.content,\n incoming.frontmatter.category,\n incoming.frontmatter.sourceConnector\n );\n if (!duplicate) return false;\n const incomingIdentity = streamedIdentity?.identityHash ?? this.offlineSyncMemoryIdentity(incoming);\n const options = this.tombstoneBlockedCaptureIndexOptions();\n const existing = [...(await options.readAllMemories()), ...(await options.readAllColdMemories())];\n const exactMatch = existing.some(\n (memory) =>\n this.isTombstoneBlockedMemory(memory) &&\n (streamedIdentity ? captureIdentityHash(memory) : this.offlineSyncMemoryIdentity(memory)) === incomingIdentity\n );\n if (!exactMatch) return false;\n if (before === null) return true;\n if (!this.isTombstoneBlockedMemory(before)) return false;\n const beforeIdentity = streamedIdentity ? captureIdentityHash(before) : this.offlineSyncMemoryIdentity(before);\n if (beforeIdentity !== incomingIdentity) return false;\n return streamedIdentity\n ? isDeepStrictEqual(before.frontmatter, incoming.frontmatter)\n : before.content === incoming.content && isDeepStrictEqual(before.frontmatter, incoming.frontmatter);\n }\n\n private async invalidateAfterOfflineSyncMutation(filePath: string, ownedMarker?: string): Promise<void> {\n this.invalidateAllMemoriesCache();\n this.invalidateKnowledgeIndexCache();\n this.markFactHashIndexNotAuthoritative();\n if (filePath.includes(`${path.sep}cold${path.sep}`)) {\n this.invalidateColdMemoriesCache();\n }\n await this.rebuildTombstoneBlockedCaptureAfterInvalidationForPath(filePath, ownedMarker);\n if (filePath.includes(`${path.sep}artifacts${path.sep}`)) {\n this.bumpArtifactWriteVersion();\n }\n this.bumpMemoryStatusVersion();\n }\n\n protected async runTombstoneBlockedArchive(\n memory: MemoryFile,\n task: (current: MemoryFile, markDurable: () => void) => Promise<string | null>\n ): Promise<string | null> {\n if (!this.isTombstoneBlockedMemory(memory) && !isQueuedReviewMemory(memory)) {\n return await task(memory, () => {});\n }\n let archivedPath: string | null = null;\n const archived = await this.runTombstoneBlockedInvalidation(\n memory,\n async (current, rebuildMarker, markDurable) => {\n let archiveDurable = false;\n const markArchiveDurable = () => {\n archiveDurable = true;\n markDurable();\n };\n archivedPath = await task(current, markArchiveDurable);\n if (rebuildMarker !== undefined && (archivedPath !== null || archiveDurable)) {\n await this.rebuildTombstoneBlockedCaptureAfterInvalidation(rebuildMarker);\n } else if (rebuildMarker !== undefined) {\n const index = this.getTombstoneBlockedCaptureIndex();\n try { await index.discardWrite(rebuildMarker); } catch { index.markUntrusted(); }\n }\n return archivedPath !== null;\n }\n );\n return archived ? archivedPath : null;\n }\n\n protected async runTombstoneBlockedInvalidation(\n memory: MemoryFile,\n task: (\n current: MemoryFile,\n rebuildMarker: string | undefined,\n markDurable: () => void\n ) => Promise<boolean>,\n propagateErrors = false,\n coordinate = false\n ): Promise<boolean> {\n let lockIdentity =\n coordinate || this.isTombstoneBlockedMemory(memory) || isQueuedReviewMemory(memory)\n ? this.offlineSyncMemoryIdentity(memory)\n : undefined;\n for (;;) {\n const attempt = async () => {\n const options = this.tombstoneBlockedCaptureIndexOptions();\n const current =\n (await this.readMemoryByPath(memory.path)) ??\n (await options.readAllMemories()).find((candidate) => candidate.frontmatter.id === memory.frontmatter.id) ??\n (await options.readAllColdMemories()).find((candidate) => candidate.frontmatter.id === memory.frontmatter.id);\n if (!current) return { result: false };\n const blocked = this.isTombstoneBlockedMemory(current);\n const queuedReview = isQueuedReviewMemory(current);\n if (blocked || queuedReview) {\n const currentIdentity = this.offlineSyncMemoryIdentity(current);\n if (lockIdentity !== currentIdentity) return { retryIdentity: currentIdentity };\n }\n const blockedIndex = blocked ? this.getTombstoneBlockedCaptureIndex() : null;\n let rebuildMarker: string | undefined;\n let durable = false;\n try {\n if (blockedIndex) rebuildMarker = await blockedIndex.prepareWrite();\n const result = await task(current, rebuildMarker, () => {\n durable = true;\n });\n if (!result && rebuildMarker && !durable && blockedIndex) {\n try {\n await blockedIndex.discardWrite(rebuildMarker);\n } catch {\n blockedIndex.markUntrusted();\n }\n }\n return { result };\n } catch (error) {\n if (rebuildMarker && !durable && blockedIndex) {\n try {\n await blockedIndex.discardWrite(rebuildMarker);\n } catch {\n blockedIndex.markUntrusted();\n }\n }\n if (propagateErrors) throw error;\n return { result: false };\n }\n };\n const result =\n lockIdentity === undefined\n ? await attempt()\n : await this.withTombstoneBlockedCaptureWriteLock(attempt, lockIdentity);\n if (result.retryIdentity !== undefined) {\n lockIdentity = result.retryIdentity;\n continue;\n }\n return result.result;\n }\n }\n async deleteMemoryForMaintenance(\n memory: MemoryFile,\n shouldDelete: (current: MemoryFile) => boolean = () => true\n ): Promise<MemoryFile | null> {\n let deleted: MemoryFile | null = null;\n const removed = await this.runTombstoneBlockedInvalidation(\n memory,\n async (current, rebuildMarker, markDurable) => {\n if (!shouldDelete(current)) return false;\n await unlink(current.path);\n markDurable();\n deleted = current;\n const memoryDir = this.tombstoneBlockedCaptureIndexOptions().memoryDir;\n markProjectedMemoryPathInvalid(memoryDir, current.frontmatter.id);\n this.invalidateAllMemoriesCache();\n if (current.path.includes(`${path.sep}cold${path.sep}`)) {\n this.invalidateColdMemoriesCache();\n }\n await this.rebuildTombstoneBlockedCaptureAfterInvalidation(rebuildMarker);\n this.bumpMemoryCorpusVersion();\n this.bumpMemoryStatusVersion();\n return true;\n },\n true,\n true\n );\n return removed ? deleted : null;\n }\n\n protected async runTombstoneBlockedOfflineSyncMutation(\n target: string,\n after: MemoryFile | null,\n write: () => Promise<void>,\n coordinate = false,\n streamedIdentity?: StreamedOfflineSyncIdentity\n ): Promise<void> {\n for (;;) {\n const before = await this.readMemoryByPath(target);\n const targetIdentity = this.isTombstoneBlockedMemory(before)\n ? this.offlineSyncMemoryIdentity(before)\n : undefined;\n const blocked = coordinate || targetIdentity !== undefined || this.isTombstoneBlockedMemory(after);\n const identities = [\n ...(targetIdentity === undefined ? [] : [targetIdentity]),\n ...(!streamedIdentity && this.isTombstoneBlockedMemory(after)\n ? [this.offlineSyncMemoryIdentity(after)]\n : []),\n ...(coordinate ? [target] : []),\n ...(coordinate && after === null && streamedIdentity === undefined ? [\"\"] : []),\n ];\n let retryIdentity: string | undefined;\n const mutate = async (): Promise<void> => {\n const current = await this.readMemoryByPath(target);\n if (this.isTombstoneBlockedMemory(current)) {\n const currentIdentity = this.offlineSyncMemoryIdentity(current);\n if (currentIdentity !== targetIdentity) {\n retryIdentity = currentIdentity;\n return;\n }\n }\n if (\n this.isTombstoneBlockedMemory(after) &&\n (await this.isDuplicateTombstoneBlockedOfflineSync(target, after, streamedIdentity))\n ) {\n return;\n }\n const marker = blocked ? await this.getTombstoneBlockedCaptureIndex().prepareWrite() : undefined;\n let durable = false;\n try {\n await write();\n durable = true;\n if (marker) {\n try {\n await this.getTombstoneBlockedCaptureIndex().commitWrite(marker);\n } catch (err) {\n this.getTombstoneBlockedCaptureIndex().markUntrusted();\n log.warn(`storage.offlineSyncFile committed write marker failed: ${err}`);\n }\n }\n await this.invalidateAfterOfflineSyncMutation(target, marker);\n } catch (err) {\n if (marker && !durable) {\n try {\n await this.getTombstoneBlockedCaptureIndex().discardWrite(marker);\n } catch (cleanupError) {\n this.getTombstoneBlockedCaptureIndex().markUntrusted();\n log.warn(`storage.offlineSyncFile failed to clear write marker: ${cleanupError}`);\n }\n }\n throw err;\n }\n };\n if (!blocked) {\n await mutate();\n } else {\n if (streamedIdentity) {\n await this.withTombstoneBlockedCaptureWriteLockAndHashes(\n mutate,\n identities,\n [streamedIdentity.identityHash]\n );\n } else {\n await this.withTombstoneBlockedCaptureWriteLock(mutate, identities);\n }\n }\n if (retryIdentity !== undefined) continue;\n return;\n }\n }\n\n protected async writeTombstoneBlockedOfflineSyncFile(target: string, content: Buffer): Promise<void> {\n await this.runTombstoneBlockedOfflineSyncMutation(\n target,\n this.tombstoneBlockedCaptureIndexOptions().parseMemory?.(target, content) ?? null,\n () => this.writeStorageSecureFile(target, content)\n );\n }\n\n private async prepareTombstoneBlockedOfflineSyncChunks(\n target: string,\n chunks: AsyncIterable<Buffer>\n ): Promise<PreparedOfflineSyncChunks> {\n const parseMemory = this.tombstoneBlockedCaptureIndexOptions().parseMemory;\n if (!parseMemory) return { after: null, chunks };\n\n const iterator = chunks[Symbol.asyncIterator]();\n const buffered: Buffer[] = [];\n let bufferedBytes = 0;\n let firstOverflow: Buffer | undefined;\n for (;;) {\n const next = await iterator.next();\n if (next.done) break;\n if (bufferedBytes + next.value.length > OFFLINE_SYNC_BUFFER_LIMIT_BYTES) {\n firstOverflow = next.value;\n break;\n }\n bufferedBytes += next.value.length;\n buffered.push(next.value);\n }\n const overflow = firstOverflow;\n if (overflow === undefined) {\n let after: MemoryFile | null = null;\n try {\n after = parseMemory(target, Buffer.concat(buffered));\n } catch {\n after = null;\n }\n const replay = (async function* (): AsyncIterable<Buffer> {\n for (const chunk of buffered) yield chunk;\n })();\n return { after, chunks: replay };\n }\n\n const options = this.tombstoneBlockedCaptureIndexOptions();\n const stagePath = path.join(\n options.stateDir,\n \"tombstone-blocked-capture\",\n \"offline-sync-staging\",\n `${randomUUID()}.stage`\n );\n const stagedChunks = (async function* (): AsyncIterable<Buffer> {\n for (const chunk of buffered) yield chunk;\n yield overflow;\n for (;;) {\n const next = await iterator.next();\n if (next.done) break;\n yield next.value;\n }\n })();\n try {\n await writeMaybeEncryptedFileFromChunks(\n stagePath,\n stagedChunks,\n options.secureStoreWriteKeyProvider(),\n { atomic: false },\n options.memoryDir\n );\n return await this.describeStagedOfflineSyncChunks(target, stagePath, parseMemory);\n } catch (err) {\n await unlink(stagePath).catch(() => {});\n throw err;\n }\n }\n\n private async describeStagedOfflineSyncChunks(\n target: string,\n stagePath: string,\n parseMemory: OfflineSyncMemoryParser\n ): Promise<PreparedOfflineSyncChunks> {\n const options = this.tombstoneBlockedCaptureIndexOptions();\n const key = options.secureStoreKeyProvider();\n const bodyOffset = await findStagedBodyOffset(stagePath, key, options.memoryDir);\n if (bodyOffset === null) {\n return { after: null, chunks: this.readStagedOfflineSyncChunks(stagePath), stagePath };\n }\n const prefix = await readStagedPrefix(stagePath, bodyOffset, key, options.memoryDir);\n let after: MemoryFile | null = null;\n try {\n after = parseMemory(target, prefix.subarray(0, bodyOffset));\n } catch {\n after = null;\n }\n if (after === null) {\n return { after: null, chunks: this.readStagedOfflineSyncChunks(stagePath), stagePath };\n }\n const streamedIdentity = await this.computeStagedOfflineSyncIdentity(stagePath, bodyOffset, after);\n return {\n after,\n chunks: this.readStagedOfflineSyncChunks(stagePath),\n stagePath,\n streamedIdentity,\n };\n }\n\n private async computeStagedOfflineSyncIdentity(\n stagePath: string,\n bodyOffset: number,\n after: MemoryFile\n ): Promise<StreamedOfflineSyncIdentity> {\n let normalizedLength = 0;\n const countNormalizer = createExplicitContentNormalizer((chunk) => {\n normalizedLength += chunk.length;\n });\n const options = this.tombstoneBlockedCaptureIndexOptions();\n const key = options.secureStoreKeyProvider();\n await forEachStagedTextChunk(\n stagePath,\n bodyOffset,\n (text) => countNormalizer.push(text),\n key,\n options.memoryDir\n );\n countNormalizer.finish();\n\n const category = after.frontmatter.category;\n const connector = after.frontmatter.sourceConnector?.trim() ?? \"\";\n const encode = (label: string, value: string): string => `${label} ${value.length} ${value}`;\n const identityPrefix =\n [encode(\"category\", category), encode(\"connector\", connector), `content ${normalizedLength} `].join(\" \");\n const identityHash = createHash(\"sha256\");\n identityHash.update(identityPrefix);\n const identityNormalizer = createExplicitContentNormalizer((chunk) => identityHash.update(chunk));\n await forEachStagedTextChunk(\n stagePath,\n bodyOffset,\n (text) => identityNormalizer.push(text),\n key,\n options.memoryDir\n );\n identityNormalizer.finish();\n return {\n identityHash: identityHash.digest(\"hex\"),\n };\n }\n\n private async *readStagedOfflineSyncChunks(stagePath: string): AsyncIterable<Buffer> {\n const options = this.tombstoneBlockedCaptureIndexOptions();\n for await (const chunk of readMaybeEncryptedFileFromChunks(\n stagePath,\n options.secureStoreKeyProvider(),\n options.memoryDir\n )) {\n yield chunk;\n }\n }\n\n protected async writeTombstoneBlockedOfflineSyncFileChunks(\n target: string,\n chunks: AsyncIterable<Buffer>\n ): Promise<void> {\n const coordinate = this.getTombstoneBlockedCaptureIndex().shouldRebuildAfterInvalidationForPath(target);\n const prepared: PreparedOfflineSyncChunks = coordinate\n ? await this.prepareTombstoneBlockedOfflineSyncChunks(target, chunks)\n : { after: null, chunks };\n const streamedIdentity = prepared.streamedIdentity;\n const mutate = async (): Promise<void> => {\n try {\n await this.runTombstoneBlockedOfflineSyncMutation(\n target,\n prepared.after,\n () => this.writeStorageSecureFileChunks(target, prepared.chunks),\n coordinate,\n streamedIdentity\n );\n } finally {\n if (prepared.stagePath !== undefined) await unlink(prepared.stagePath).catch(() => {});\n }\n };\n await mutate();\n }\n\n async writeOfflineSyncFile(filePath: string, content: Buffer): Promise<void> {\n const target = this.assertManagedStoragePath(filePath, \"storage.writeOfflineSyncFile\");\n await this.writeTombstoneBlockedOfflineSyncFile(target, content);\n }\n\n async writeOfflineSyncFileChunks(filePath: string, chunks: AsyncIterable<Buffer>): Promise<void> {\n const target = this.assertManagedStoragePath(filePath, \"storage.writeOfflineSyncFileChunks\");\n await this.writeTombstoneBlockedOfflineSyncFileChunks(target, chunks);\n }\n\n async deleteOfflineSyncFile(filePath: string): Promise<void> {\n const target = this.assertManagedStoragePath(filePath, \"storage.deleteOfflineSyncFile\");\n await this.deleteTombstoneBlockedOfflineSyncFile(target);\n }\n\n protected async deleteTombstoneBlockedOfflineSyncFile(target: string): Promise<void> {\n await this.runTombstoneBlockedOfflineSyncMutation(target, null, async () => {\n await unlink(target).catch((error: unknown) => {\n if (isErrnoCode(error, \"ENOENT\")) return;\n throw error;\n });\n });\n }\n\n async checkTombstoneBlockedExplicitCapture(\n ...args: [string, string, string?]\n ): Promise<{ has: boolean; authoritative: boolean }> {\n return this.getTombstoneBlockedCaptureIndex().check(...args);\n }\n\n async hasTombstoneBlockedExplicitCapture(...args: [string, string, string?]): Promise<boolean> {\n return this.getTombstoneBlockedCaptureIndex().has(...args);\n }\n\n async isTombstoneBlockedExplicitCaptureIndexAuthoritative(): Promise<boolean> {\n return this.getTombstoneBlockedCaptureIndex().isAuthoritative();\n }\n\n protected async rebuildTombstoneBlockedCaptureAfterInvalidationForPath(\n filePath: string,\n ownedMarker?: string\n ): Promise<void> {\n const index = this.getTombstoneBlockedCaptureIndex();\n if (index.shouldRebuildAfterInvalidationForPath(filePath)) {\n await this.rebuildTombstoneBlockedCaptureAfterInvalidation(ownedMarker);\n }\n }\n protected async rebuildTombstoneBlockedCaptureAfterInvalidation(ownedMarker?: string): Promise<void> {\n await this.getTombstoneBlockedCaptureIndex().rebuildAfterInvalidation(ownedMarker);\n }\n}\n","import { execFile } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, readdir, rename, stat, unlink, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { log } from \"../logger.js\";\nimport type { MemoryFile, MemoryFrontmatter } from \"../types.js\";\nimport { RECALL_FALLBACK_DIRS } from \"../utils/category-dir.js\";\nimport { isErrnoCode } from \"../utils/errno.js\";\nimport { withHeldFileLock } from \"../utils/serialize-mutations.js\";\nimport {\n CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS,\n ContentHashIndex,\n type ContentHashIndexLockOptions,\n} from \"./content-hash-index.js\";\nimport {\n buildExplicitCaptureDedupKey,\n TombstoneBlockedCaptureWriteLock,\n} from \"./tombstone-blocked-capture-mutation.js\";\nimport type { OfflineSyncMemoryParser } from \"./tombstone-blocked-capture-sync.js\";\nexport {\n buildExplicitCaptureDedupKey,\n isTombstoneBlockedCaptureWriteLockBusy,\n normalizeExplicitCaptureContent,\n} from \"./tombstone-blocked-capture-mutation.js\";\nexport {\n parseTombstoneBlockedOfflineSyncMemory,\n type OfflineSyncMemoryParser,\n TombstoneBlockedCaptureIndexHost,\n} from \"./tombstone-blocked-capture-sync.js\";\n\nconst REBUILD_MAX_ATTEMPTS = 3;\nconst execFileAsync = promisify(execFile);\nconst REBUILD_RETRY_BASE_MS = 50;\n\nconst ABANDONED_MARKER_MIN_AGE_MS = 60_000;\nconst MARKER_PROCESS_START_TOLERANCE_MS = 2_000;\nconst DOTNET_UNIX_EPOCH_TICKS = 621_355_968_000_000_000;\nconst MARKER_PROCESS_STARTED_AT_MS = Date.now() - process.uptime() * 1000;\n\n\ntype RebuildMarker = {\n path: string;\n committed: boolean;\n pid?: number;\n ownerId?: string;\n createdAt?: number;\n processStartedAtMs?: number;\n malformed?: boolean;\n};\n\n\nexport type TombstoneBlockedCaptureIndexOptions = {\n readonly stateDir: string;\n readonly memoryDir: string;\n readonly secureStoreKeyProvider: () => Buffer | null;\n readonly secureStoreWriteKeyProvider: () => Buffer | null;\n readonly lockOptions: () => ContentHashIndexLockOptions;\n readonly readAllMemories: () => Promise<MemoryFile[]>;\n readonly readAllColdMemories: () => Promise<MemoryFile[]>;\n readonly parseMemory?: OfflineSyncMemoryParser;\n readonly withHeldFileLock?: typeof withHeldFileLock;\n};\n\n/**\n * Durable targeted deduplication for tombstone-blocked explicit captures.\n *\n * The active fact-hash index intentionally excludes blocked rows. This small\n * companion index keeps the blocked identity visible without making every\n * authoritative active-index miss scan the full corpus.\n */\nexport class TombstoneBlockedCaptureIndex {\n private readonly options: TombstoneBlockedCaptureIndexOptions;\n private index: ContentHashIndex | null = null;\n private loadPromise: Promise<ContentHashIndex> | null = null;\n private refreshPromise: Promise<ContentHashIndex> | null = null;\n private authoritative = false;\n private readonly captureWriteLock: TombstoneBlockedCaptureWriteLock;\n\n constructor(options: TombstoneBlockedCaptureIndexOptions) {\n this.options = options;\n this.captureWriteLock = new TombstoneBlockedCaptureWriteLock(options);\n }\n\n private createIndex(): ContentHashIndex {\n return new ContentHashIndex(\n path.join(this.options.stateDir, \"tombstone-blocked-capture\"),\n this.options.secureStoreKeyProvider,\n this.options.secureStoreWriteKeyProvider,\n this.options.memoryDir,\n this.options.lockOptions()\n );\n }\n\n private indexPath(): string {\n return path.join(this.options.stateDir, \"tombstone-blocked-capture\", \"fact-hashes.txt\");\n }\n\n private rebuildMarkerPath(): string {\n return path.join(this.options.stateDir, \"tombstone-blocked-capture\", \"rebuild-required\");\n }\n\n private async readRebuildMarkers(): Promise<RebuildMarker[]> {\n try {\n const entries = (await readdir(this.rebuildMarkerPath())).filter((entry) => !entry.startsWith(\".\"));\n return (\n await Promise.all(\n entries.map(async (entry) => {\n const markerPath = path.join(this.rebuildMarkerPath(), entry);\n try {\n const raw = (await readFile(markerPath, \"utf8\")).trim();\n if (raw === \"committed\") return { path: markerPath, committed: true };\n let value: Record<string, unknown>;\n let malformed = false;\n try {\n const parsed = JSON.parse(raw) as unknown;\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n malformed = true;\n value = {};\n } else {\n value = parsed as Record<string, unknown>;\n }\n } catch {\n malformed = true;\n value = {};\n }\n const state = value.state;\n if (state !== \"pending\" && state !== \"committed\") malformed = true;\n const markerStat = await stat(markerPath).catch(() => null);\n // A torn payload can lose its JSON metadata, but the atomic\n // marker filename and filesystem mtime still identify its owner\n // and age. Preserve both so stale malformed markers can be\n // reaped without treating a fresh pre-commit gap as abandoned.\n const createdAt =\n typeof value.createdAt === \"number\" && Number.isFinite(value.createdAt)\n ? value.createdAt\n : markerStat?.mtimeMs;\n if (typeof value.pid !== \"number\" || !Number.isInteger(value.pid)) malformed = true;\n const ownerId =\n typeof value.ownerId === \"string\" && value.ownerId.length > 0\n ? value.ownerId\n : path.basename(markerPath);\n if (typeof value.ownerId !== \"string\" || value.ownerId.length === 0) malformed = true;\n const processStartedAtMs =\n typeof value.processStartedAtMs === \"number\" &&\n Number.isFinite(value.processStartedAtMs) &&\n value.processStartedAtMs > 0\n ? value.processStartedAtMs\n : undefined;\n return {\n path: markerPath,\n committed: state === \"committed\",\n ...(typeof value.pid === \"number\" && Number.isInteger(value.pid) ? { pid: value.pid } : {}),\n ...(ownerId.length > 0 ? { ownerId } : {}),\n ...(typeof createdAt === \"number\" && Number.isFinite(createdAt) ? { createdAt } : {}),\n ...(processStartedAtMs === undefined ? {} : { processStartedAtMs }),\n ...(malformed ? { malformed: true } : {}),\n };\n } catch (err) {\n if (isErrnoCode(err, \"ENOENT\")) return null;\n throw err;\n }\n })\n )\n ).filter((marker): marker is RebuildMarker => marker !== null);\n } catch (err) {\n if (isErrnoCode(err, \"ENOENT\")) return [];\n throw err;\n }\n }\n private async isAbandonedMarker(marker: RebuildMarker): Promise<boolean> {\n if (marker.committed || marker.createdAt === undefined) return false;\n if (Date.now() - marker.createdAt < ABANDONED_MARKER_MIN_AGE_MS) return false;\n if (marker.malformed) return true;\n return marker.pid !== undefined && !(await this.isProcessAlive(marker.pid, marker.processStartedAtMs));\n }\n\n private async isProcessAlive(pid: number, processStartedAtMs?: number): Promise<boolean> {\n try {\n process.kill(pid, 0);\n } catch (err) {\n return !isErrnoCode(err, \"ESRCH\");\n }\n if (processStartedAtMs === undefined) return true;\n const runningStartedAtMs = await this.readProcessStartedAtMs(pid);\n if (runningStartedAtMs === null) return true;\n return runningStartedAtMs <= processStartedAtMs + MARKER_PROCESS_START_TOLERANCE_MS;\n }\n private async readProcessStartedAtMs(pid: number): Promise<number | null> {\n try {\n if (process.platform === \"win32\") {\n const { stdout } = await execFileAsync(\n \"powershell.exe\",\n [\n \"-NoLogo\",\n \"-NoProfile\",\n \"-NonInteractive\",\n \"-Command\",\n `(Get-Process -Id ${pid} -ErrorAction Stop).StartTime.ToUniversalTime().Ticks`,\n ],\n { encoding: \"utf8\", timeout: 1_000 }\n );\n const ticks = Number(stdout.trim());\n return Number.isFinite(ticks) ? (ticks - DOTNET_UNIX_EPOCH_TICKS) / 10_000 : null;\n }\n const { stdout } = await execFileAsync(\"ps\", [\"-p\", String(pid), \"-o\", \"lstart=\"], {\n encoding: \"utf8\",\n timeout: 1_000,\n });\n const output = stdout.trim();\n if (!output) return null;\n const startedAtMs = Date.parse(output);\n return Number.isFinite(startedAtMs) ? startedAtMs : null;\n } catch {\n return null;\n }\n }\n\n private async getClearableRebuildMarkerPaths(markers: readonly RebuildMarker[]): Promise<string[]> {\n const paths = await Promise.all(\n markers.map(async (marker) => (marker.committed || (await this.isAbandonedMarker(marker)) ? marker.path : null))\n );\n return paths.filter((markerPath): markerPath is string => markerPath !== null);\n }\n\n private async getRebuildMarkers(): Promise<RebuildMarker[]> {\n return await this.readRebuildMarkers();\n }\n\n // A pending marker protects the pre-commit gap; only committed markers are safe for another writer to clear.\n private async writeMarkerAtomically(markerPath: string, payload: string): Promise<void> {\n const temporaryPath = `${markerPath}.tmp-${process.pid}-${randomUUID()}`;\n try {\n await writeFile(temporaryPath, payload, \"utf8\");\n await rename(temporaryPath, markerPath);\n } finally {\n await unlink(temporaryPath).catch((err: unknown) => {\n if (!isErrnoCode(err, \"ENOENT\")) throw err;\n });\n }\n }\n\n private async markRebuildRequired(committed = false): Promise<string> {\n const markerDir = this.rebuildMarkerPath();\n await mkdir(markerDir, { recursive: true });\n const ownerId = randomUUID();\n const markerPath = path.join(markerDir, ownerId);\n await this.writeMarkerAtomically(\n markerPath,\n `${JSON.stringify({\n state: committed ? \"committed\" : \"pending\",\n pid: process.pid,\n ownerId,\n createdAt: Date.now(),\n processStartedAtMs: MARKER_PROCESS_STARTED_AT_MS,\n })}\\n`\n );\n return markerPath;\n }\n\n private async markRebuildCommitted(markerPath: string): Promise<void> {\n await this.writeMarkerAtomically(\n markerPath,\n `${JSON.stringify({\n state: \"committed\",\n pid: process.pid,\n ownerId: path.basename(markerPath),\n createdAt: Date.now(),\n processStartedAtMs: MARKER_PROCESS_STARTED_AT_MS,\n })}\\n`\n );\n }\n\n private async clearRebuildRequired(markerPaths: readonly string[]): Promise<void> {\n await Promise.all(\n markerPaths.map(async (markerPath) => {\n try {\n await unlink(markerPath);\n } catch (err) {\n if (!isErrnoCode(err, \"ENOENT\")) throw err;\n }\n })\n );\n }\n\n private async hasRebuildRequired(excludedMarkers: readonly string[] = []): Promise<boolean> {\n const excluded = new Set(excludedMarkers);\n return (await this.getRebuildMarkers()).some((marker) => !excluded.has(marker.path));\n }\n\n private async setAuthoritative(rebuilt: boolean): Promise<void> {\n this.authoritative = rebuilt && !(await this.getRebuildMarkers()).some((marker) => !marker.committed);\n }\n private isBlocked(memory: MemoryFile): boolean {\n return memory.frontmatter.status === \"pending_review\" && Boolean(memory.frontmatter.blockedBy);\n }\n\n private async rebuild(index: ContentHashIndex): Promise<boolean> {\n return await index.rebuildUnderLock(async () => {\n index.clear();\n const existing = [...(await this.options.readAllMemories()), ...(await this.options.readAllColdMemories())];\n for (const memory of existing) {\n if (!this.isBlocked(memory)) continue;\n index.add(\n buildExplicitCaptureDedupKey(memory.content, memory.frontmatter.category, memory.frontmatter.sourceConnector)\n );\n }\n });\n }\n\n private async hasPersistedIndexOrMarkers(): Promise<boolean> {\n try {\n await stat(this.indexPath());\n return true;\n } catch (err) {\n if (!isErrnoCode(err, \"ENOENT\")) throw err;\n return (await this.readRebuildMarkers()).length > 0;\n }\n }\n\n private async getIndex(): Promise<ContentHashIndex> {\n if (this.index) return this.index;\n if (!this.loadPromise) {\n const index = this.createIndex();\n this.loadPromise = (async () => {\n await index.load();\n const rebuildMarkers = await this.getRebuildMarkers();\n const clearableRebuildMarkers = await this.getClearableRebuildMarkerPaths(rebuildMarkers);\n let persisted = true;\n try {\n await stat(this.indexPath());\n if (rebuildMarkers.length > 0) persisted = false;\n } catch (err) {\n if (isErrnoCode(err, \"ENOENT\")) persisted = false;\n else throw err;\n }\n if (!persisted) {\n const lockOptions = this.options.lockOptions();\n const maxAttempts = lockOptions.retryMaxAttempts ?? REBUILD_MAX_ATTEMPTS;\n const baseMs = lockOptions.retryBaseMs ?? REBUILD_RETRY_BASE_MS;\n let rebuilt = false;\n for (let attempt = 0; attempt < maxAttempts; attempt += 1) {\n rebuilt = await this.rebuild(index);\n if (rebuilt || attempt === maxAttempts - 1) break;\n const wait = Math.min(baseMs * 2 ** attempt, CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS);\n const delayState = Promise.withResolvers<void>();\n setTimeout(delayState.resolve, wait);\n await delayState.promise;\n }\n if (!rebuilt) {\n throw new Error(\"tombstone-blocked capture index rebuild lock unavailable\");\n }\n await this.clearRebuildRequired(clearableRebuildMarkers);\n }\n await this.setAuthoritative(true);\n this.index = index;\n return index;\n })().catch((err) => {\n this.loadPromise = null;\n throw err;\n });\n }\n return this.loadPromise;\n }\n\n async withCaptureWriteLock<T>(task: () => Promise<T>, identity?: string | readonly string[]): Promise<T> {\n return await this.captureWriteLock.withCaptureWriteLock(task, identity);\n }\n\n async withCaptureWriteLockHashes<T>(task: () => Promise<T>, identityHashes: readonly string[]): Promise<T> {\n return await this.captureWriteLock.withCaptureWriteLockHashes(task, identityHashes);\n }\n async withCaptureWriteLockAndHashes<T>(\n task: () => Promise<T>,\n identity: string | readonly string[] | undefined,\n identityHashes: readonly string[]\n ): Promise<T> {\n return await this.captureWriteLock.withCaptureWriteLockAndHashes(task, identity, identityHashes);\n }\n\n private async reload(): Promise<ContentHashIndex> {\n if (!this.refreshPromise) {\n this.refreshPromise = (async () => {\n this.index = null;\n this.loadPromise = null;\n return await this.getIndex();\n })().finally(() => {\n this.refreshPromise = null;\n });\n }\n return this.refreshPromise;\n }\n\n /** Return blocked-row membership and authority from one index snapshot. */\n async check(\n content: string,\n category: string,\n sourceConnector?: string\n ): Promise<{ has: boolean; authoritative: boolean }> {\n const key = buildExplicitCaptureDedupKey(content, category, sourceConnector);\n let index = await this.getIndex();\n if ((await this.hasRebuildRequired()) || !(await index.isDiskFingerprintCurrent())) {\n index = await this.reload();\n }\n return { has: index.has(key), authoritative: this.authoritative };\n }\n\n /** Return whether a blocked row matches this explicit-capture identity. */\n async has(content: string, category: string, sourceConnector?: string): Promise<boolean> {\n return (await this.check(content, category, sourceConnector)).has;\n }\n\n /** Return whether the in-memory index is safe to answer an authoritative miss. */\n async isAuthoritative(): Promise<boolean> {\n const index = await this.getIndex();\n if ((await this.hasRebuildRequired()) || !(await index.isDiskFingerprintCurrent())) {\n await this.reload();\n }\n return this.authoritative;\n }\n\n /** Reserve durable rebuild intent before a blocked memory file is committed. */\n async prepareWrite(): Promise<string> {\n return this.markRebuildRequired();\n }\n\n /** Mark a prepared write committed once its memory file is durable. */\n async commitWrite(rebuildMarker: string): Promise<void> {\n await this.markRebuildCommitted(rebuildMarker);\n }\n\n /** Drop a pending marker when the protected memory write never commits. */\n async discardWrite(rebuildMarker: string): Promise<void> {\n await this.clearRebuildRequired([rebuildMarker]);\n }\n\n /** Add a newly persisted blocked row to the durable targeted index. */\n async add(memory: MemoryFile, rebuildMarker?: string): Promise<void> {\n if (!this.isBlocked(memory)) return;\n let index = await this.getIndex();\n if (\n (await this.hasRebuildRequired(rebuildMarker ? [rebuildMarker] : [])) ||\n !(await index.isDiskFingerprintCurrent())\n ) {\n index = await this.reload();\n }\n index.add(\n buildExplicitCaptureDedupKey(memory.content, memory.frontmatter.category, memory.frontmatter.sourceConnector)\n );\n const marker = rebuildMarker ?? (await this.markRebuildRequired(true));\n if (rebuildMarker) await this.markRebuildCommitted(rebuildMarker);\n await index.saveMergingWithDisk();\n await index.flushReconcileRetry();\n if (index.hasPendingChanges) {\n this.markUntrusted();\n return;\n }\n await this.clearRebuildRequired([marker]);\n await this.setAuthoritative(true);\n }\n\n /** Rebuild the index after a blocked row changes or is removed. */\n async rebuildIfLoaded(ownedMarker?: string): Promise<void> {\n const index = this.index ?? ((await this.hasPersistedIndexOrMarkers()) ? this.getIndex() : null);\n if (!index) return;\n const rebuildMarkers = await this.getRebuildMarkers();\n const rebuildMarker = await this.markRebuildRequired(true);\n const clearableRebuildMarkers = await this.getClearableRebuildMarkerPaths(rebuildMarkers);\n const rebuilt = await this.rebuild(await index);\n if (rebuilt) {\n await this.clearRebuildRequired([\n ...clearableRebuildMarkers,\n ...(ownedMarker === undefined || clearableRebuildMarkers.includes(ownedMarker) ? [] : [ownedMarker]),\n rebuildMarker,\n ]);\n }\n await this.setAuthoritative(rebuilt);\n }\n\n /** Rebuild when either side of a write is blocked and its identity changed. */\n async sync(before: MemoryFile, after: MemoryFile, rebuildMarker?: string): Promise<void> {\n const beforeBlocked = this.isBlocked(before);\n const afterBlocked = this.isBlocked(after);\n if (!beforeBlocked && !afterBlocked) return;\n const beforeKey = buildExplicitCaptureDedupKey(\n before.content,\n before.frontmatter.category,\n before.frontmatter.sourceConnector\n );\n const afterKey = buildExplicitCaptureDedupKey(\n after.content,\n after.frontmatter.category,\n after.frontmatter.sourceConnector\n );\n if (beforeBlocked && afterBlocked && beforeKey === afterKey) {\n if (rebuildMarker) {\n await this.markRebuildCommitted(rebuildMarker);\n await this.clearRebuildRequired([rebuildMarker]);\n }\n return;\n }\n const existingMarkers = await this.getRebuildMarkers();\n const marker = rebuildMarker ?? (await this.markRebuildRequired(true));\n const clearableExistingMarkers = await this.getClearableRebuildMarkerPaths(existingMarkers);\n if (rebuildMarker) await this.markRebuildCommitted(rebuildMarker);\n const rebuilt = await this.rebuild(await this.getIndex());\n if (rebuilt) {\n await this.clearRebuildRequired([\n ...clearableExistingMarkers,\n marker,\n ]);\n }\n await this.setAuthoritative(rebuilt);\n }\n markUntrusted(): void {\n this.authoritative = false;\n }\n\n private async failOpen(operation: string, action: () => Promise<void>): Promise<void> {\n try {\n await action();\n } catch (err) {\n this.markUntrusted();\n log.warn(`storage.${operation} completed but failed to update tombstone-blocked capture index: ${err}`);\n }\n }\n\n async addWrittenMemory(\n pathname: string,\n frontmatter: MemoryFrontmatter,\n content: string,\n rebuildMarker?: string\n ): Promise<void> {\n await this.failOpen(\"writeMemory\", () => this.add({ path: pathname, frontmatter, content }, rebuildMarker));\n }\n\n async rebuildAfterInvalidation(ownedMarker?: string): Promise<void> {\n await this.failOpen(\"invalidateMemory\", () => this.rebuildIfLoaded(ownedMarker));\n }\n\n async syncUpdatedMemory(\n before: MemoryFile,\n frontmatter: MemoryFrontmatter,\n content: string,\n rebuildMarker?: string\n ): Promise<void> {\n await this.failOpen(\"updateMemory\", () => this.sync(before, { ...before, frontmatter, content }, rebuildMarker));\n }\n\n async syncUpdatedFrontmatter(\n before: MemoryFile,\n frontmatter: MemoryFrontmatter,\n rebuildMarker?: string\n ): Promise<void> {\n await this.failOpen(\"writeMemoryFrontmatter\", () => this.sync(before, { ...before, frontmatter }, rebuildMarker));\n }\n shouldRebuildAfterInvalidationForPath(filePath: string): boolean {\n const relative = path.relative(path.resolve(this.options.memoryDir), path.resolve(filePath));\n const category = relative.split(path.sep)[0];\n return category === \"cold\" || RECALL_FALLBACK_DIRS.includes(category);\n }\n}\n"],"mappings":"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","names":["mkdir","path","path","mkdir","createHash","randomUUID","unlink","path","mkdir","stat","path","path","stat","stdout","mkdir","path","result","unlink","randomUUID","createHash"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runPostConsolidationMaterialize
3
- } from "./chunk-JDZRYH2N.js";
3
+ } from "./chunk-NHWB6HG2.js";
4
4
  import {
5
5
  discoverMemoryExtensions,
6
6
  renderExtensionsBlock,
@@ -220,4 +220,4 @@ export {
220
220
  buildExtensionsBlockForConsolidation,
221
221
  materializeAfterSemanticConsolidation
222
222
  };
223
- //# sourceMappingURL=chunk-WONS4RZ2.js.map
223
+ //# sourceMappingURL=chunk-FDAMS4PK.js.map
@@ -99,10 +99,11 @@ async function withHeldFileLock(lockPath, opts, task) {
99
99
  };
100
100
  const ownerId = randomUUID();
101
101
  const lockDir = path.dirname(lockPath);
102
- const held = await acquireLock(lockPath, lockDir, ownerId, opts, maxWaitMs, pollMs);
103
- if (!held) {
104
- return task(false, { refresh: async () => false });
102
+ const acquisition = await acquireLock(lockPath, lockDir, ownerId, opts, maxWaitMs, pollMs);
103
+ if ("failure" in acquisition) {
104
+ return task(false, { refresh: async () => false, failure: acquisition.failure });
105
105
  }
106
+ const held = acquisition;
106
107
  const heartbeat = setInterval(() => {
107
108
  lockHeldBySelf(held).then((ours) => {
108
109
  if (!ours) return;
@@ -149,7 +150,7 @@ async function acquireLock(lockPath, lockDir, ownerId, opts, maxWaitMs, pollMs)
149
150
  try {
150
151
  await mkdir(lockDir, { recursive: true });
151
152
  } catch {
152
- return void 0;
153
+ return { failure: "error" };
153
154
  }
154
155
  const deadline = Date.now() + maxWaitMs;
155
156
  for (; ; ) {
@@ -170,15 +171,15 @@ async function acquireLock(lockPath, lockDir, ownerId, opts, maxWaitMs, pollMs)
170
171
  }
171
172
  if (!wroteMeta) {
172
173
  await unlink(lockPath).catch(() => void 0);
173
- return void 0;
174
+ return { failure: "error" };
174
175
  }
175
176
  return { path: lockPath, ownerId };
176
177
  } catch (err) {
177
178
  if (err?.code !== "EEXIST") {
178
- return void 0;
179
+ return { failure: "error" };
179
180
  }
180
181
  await breakStaleLock(lockPath, opts.staleMs, opts.onBeforeBreakStaleUnlinkForTest);
181
- if (Date.now() >= deadline) return void 0;
182
+ if (Date.now() >= deadline) return { failure: "timeout" };
182
183
  await sleep(Math.min(pollMs, deadline - Date.now()));
183
184
  }
184
185
  }
@@ -297,4 +298,4 @@ export {
297
298
  serializeMutations,
298
299
  withHeldFileLock
299
300
  };
300
- //# sourceMappingURL=chunk-WL5EBRDH.js.map
301
+ //# sourceMappingURL=chunk-FFAZ2XFU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/serialize-mutations.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Shared serialized-mutation utilities for TOCTOU hotspots (issue #1524).\n//\n// Two complementary primitives that the namespace catalog (`queueCritical` +\n// `withHeldCatalogLock`), the storage router's resolve-hook serialization, and\n// the summary-snapshot writer each re-implement today:\n//\n// 1. `serializeMutations(key, task)` — keyed IN-PROCESS async serialization\n// that recovers after a rejection (CLAUDE.md rule #40). One failed task\n// never poisons the tasks queued behind it; the failed task's error is\n// still surfaced to ITS caller.\n//\n// 2. `withHeldFileLock(lockPath, opts, task)` — a held CROSS-PROCESS file\n// lock with replacement-safe stale breaking (the NG7Bg invariant from\n// #1506 round 28) and ownership-checked release.\n//\n// This is the UTILITY module only. Per-issue PR split: one PR for the utility\n// + tests (this file), then one PR per adoption hotspot (catalog, router\n// provenance, summary snapshot). No adoptions live here.\n// ---------------------------------------------------------------------------\n\nimport { randomUUID } from \"node:crypto\";\nimport { link, mkdir, open, readFile, rename, stat, unlink, utimes } from \"node:fs/promises\";\nimport path from \"node:path\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// 1. serializeMutations — keyed async serialization with rejection recovery\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * One entry in the per-key serialization map. `tail` is the recovered promise\n * the next queued task chains off of; it never rejects (both settle handlers\n * swallow), so a prior task's failure can never break subsequent ones.\n */\ninterface MutationChainEntry {\n tail: Promise<void>;\n}\n\n/**\n * Instance-scoped keyed serializer. Holds the per-key chain map so that all\n * tasks queued under the same key on the SAME serializer run strictly in order.\n *\n * The map is instance-scoped (not module-level) so tests can construct a fresh\n * serializer per case and avoid cross-test contamination, and so adopters that\n * want isolation (e.g. one serializer per storage root) can have it. The free\n * {@link serializeMutations} export delegates to a single shared default\n * instance for callers that want process-wide serialization.\n */\nexport class MutationSerializer {\n private readonly chains = new Map<string, MutationChainEntry>();\n\n /**\n * Run `task` strictly after every other task already queued under `key` on\n * this serializer has settled.\n *\n * Rejection recovery (rule #40, mirroring the catalog's `queueCritical`):\n * if a prior task rejects, later tasks STILL RUN, while the rejecting task's\n * error is surfaced to ITS OWN caller. Concretely, the recovered tail is\n * `run.then(noop, noop)` — never a bare `.then(fn)`, which would let one\n * failure kill every queued task behind it.\n *\n * No unbounded growth: when a chain's last task settles and no newer task\n * chained onto it, its entry is deleted (the storage router's\n * `inFlightResolved` marker-then-clear discipline).\n */\n serialize<T>(key: string, task: () => Promise<T>): Promise<T> {\n if (typeof key !== \"string\" || key.length === 0) {\n throw new TypeError(\"MutationSerializer.serialize: key must be a non-empty string\");\n }\n if (typeof task !== \"function\") {\n throw new TypeError(\"MutationSerializer.serialize: task must be a function returning a promise\");\n }\n\n let entry = this.chains.get(key);\n if (!entry) {\n entry = { tail: Promise.resolve() };\n this.chains.set(key, entry);\n }\n\n // Chain this task off the prior tail. `tail.then(task)` runs task only once\n // the previous task has settled, preserving read-modify-write ordering.\n const run = entry.tail.then(task);\n\n // Recover the tail after a rejection so a failed task never poisons later\n // ones. Both handlers swallow; `run` still carries the original resolution\n // (or rejection) to THIS caller. This is the line a naive `.then(fn)`\n // implementation omits — see the \"naive poison chain\" prove-fail test.\n const recovered = run.then(settleNoop, settleNoop);\n entry.tail = recovered;\n\n // Self-cleaning: once our recovered tail settles, if no newer task chained\n // onto us the entry still points at `recovered` and is safe to delete. A\n // concurrent `serialize()` call enqueues synchronously and would have\n // replaced `entry.tail` BEFORE this microtask runs, so the identity check\n // is race-free (no newer task's entry can be wrongly removed).\n //\n // `recovered` cannot reject in correct operation (both handlers above\n // swallow) and the cleanup body cannot throw — but we attach a rejection\n // handler anyway so that IF the recovery invariant is ever broken, the\n // failure surfaces as a behavioral assertion (skipped tasks) rather than an\n // unhandled-rejection storm that masks which task failed. The handler is a\n // no-op: cleanup only runs on fulfillment.\n void recovered.then(\n () => {\n if (entry && entry.tail === recovered) {\n this.chains.delete(key);\n }\n },\n () => undefined,\n );\n\n return run;\n }\n\n /**\n * Test-only: the number of keys with a not-yet-cleaned chain. Used to assert\n * the no-unbounded-growth invariant. Not part of the public contract.\n */\n pendingKeysForTest(): number {\n return this.chains.size;\n }\n}\n\n/**\n * Recovery handler shared by both settle arms. Named (not inline\n * `() => undefined`) so the chain assignment stays self-documenting in stack\n * traces and the review-patterns poison-chain check can see the chain is\n * recovered, not bare `.then(fn)`.\n */\nfunction settleNoop(): void {\n /* swallow — the original resolution/rejection is carried by `run` */\n}\n\n/**\n * Process-wide default serializer backing the free {@link serializeMutations}\n * export. Lazy so it is only created when first used (tests that construct\n * their own `MutationSerializer` pay nothing).\n */\nlet defaultSerializer: MutationSerializer | undefined;\n\n/**\n * Free-function entry point (issue #1524 signature). Serializes `task` against\n * every other task queued under `key` across the whole process, via a shared\n * default {@link MutationSerializer}. For isolated/testable serialization,\n * construct a `MutationSerializer` directly.\n */\nexport function serializeMutations<T>(key: string, task: () => Promise<T>): Promise<T> {\n if (!defaultSerializer) defaultSerializer = new MutationSerializer();\n return defaultSerializer.serialize(key, task);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// 2. withHeldFileLock — cross-process held file lock with stale breaking\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Options for {@link withHeldFileLock}. */\nexport interface HeldFileLockOptions {\n /**\n * A lock whose mtime is older than this (in ms) is treated as a crashed\n * holder and broken. Required — there is no safe default, since the right\n * value depends on how long the guarded critical section can legitimately\n * run.\n */\n readonly staleMs: number;\n /**\n * Bounded acquisition: give up trying to acquire a busy lock after this long\n * (ms) and invoke `task(false)` best-effort WITHOUT holding the lock, rather\n * than blocking forever or crashing the primary op. Default 5000ms (matches\n * the namespace catalog's `REBUILD_LOCK_MAX_WAIT_MS`).\n */\n readonly maxWaitMs?: number;\n /**\n * Poll interval (ms) while waiting for a busy lock to clear. Default 50ms.\n */\n readonly pollMs?: number;\n /**\n * While WE hold the lock, refresh its mtime on this cadence (ms) so a\n * legitimately long task is not mistaken for a crashed holder and broken out\n * from under. Default `floor(staleMs / 3)` (at least 100ms), mirroring the\n * catalog heartbeat ratio. Must be comfortably below `staleMs`.\n */\n readonly heartbeatMs?: number;\n /**\n * Test seam (NG7Bg, #1506 round 28): fires AFTER a lock is judged stale and\n * BEFORE the re-verify + unlink, simulating a replacement lock being created\n * in the race window. No-op in production.\n */\n readonly onBeforeBreakStaleUnlinkForTest?: () => Promise<void> | void;\n /**\n * Test seam (codex P2): fires AFTER the release rename moves the lock to a\n * trash path and BEFORE the ownership re-verify/restore — simulating a third\n * contender acquiring the (now-empty) lockPath in the race window. No-op in\n * production. Used to prove the pre-check prevents the rename entirely.\n */\n readonly onAfterReleaseRenameForTest?: () => Promise<void> | void;\n /**\n * Test seam (#2033): fires inside {@link HeldFileLockController.refresh} AFTER\n * the ownership pre-check passes and BEFORE the `utimes` mtime bump —\n * simulating a peer that deletes the lock (making the bump throw) or replaces\n * it (a different owner the post-bump re-check must catch). No-op in\n * production; proves refresh reports LOST rather than clobbering a peer.\n */\n readonly onBeforeRefreshUtimesForTest?: () => Promise<void> | void;\n /**\n * Best-effort hook for non-fatal lock warnings (heartbeat refresh failure,\n * release-time ownership check failure). Never throws into the caller. If\n * omitted, warnings are swallowed (the lock is advisory; release/heartbeat\n * failures must never crash the guarded op).\n */\n readonly onLockWarning?: (message: string, err: unknown) => void;\n}\n\n/**\n * Control surface handed to a held-lock task so a long, CPU-bound critical\n * section can re-assert ownership immediately before its destructive write.\n */\nexport interface HeldFileLockController {\n /**\n * Re-verify THIS acquirer still owns the lock and, when it does, refresh the\n * lock's mtime. Returns `true` only while we still hold it.\n *\n * The timer heartbeat cannot fire while a synchronous, CPU-bound section\n * (a large parse/merge/sort/serialize) blocks the event loop, so a peer can\n * judge the lock stale, break it, and start its own write within the stale\n * window. A caller about to perform a destructive rewrite MUST call this\n * first: `false` means the lock was stale-broken/replaced and the caller MUST\n * abort rather than clobber the peer that now holds it; `true` also re-stamps\n * the mtime so the bounded write that immediately follows cannot itself be\n * judged stale mid-write. On the best-effort unlocked path (`acquired` was\n * `false`) this always resolves `false` — there is no lock to hold.\n */\n refresh(): Promise<boolean>;\n /**\n * Why the lock was not acquired. `timeout` means a busy peer consumed the\n * bounded wait; `error` means acquisition failed due to filesystem setup or\n * I/O and must not be retried as contention.\n */\n readonly failure?: \"timeout\" | \"error\";\n}\n\n/** Default bounded acquisition wait, mirroring the catalog. */\nconst DEFAULT_MAX_WAIT_MS = 5_000;\n/** Default busy-lock poll interval, mirroring the catalog. */\nconst DEFAULT_POLL_MS = 50;\n/** Floor for the derived heartbeat cadence. */\nconst MIN_HEARTBEAT_MS = 100;\n/** Node's setTimeout/setInterval 32-bit signed-int ceiling (2^31 − 1 ms ≈ 24.8\n * days). Delays above this are silently clamped to 1ms by the Node timer, so\n * timer-backed options (pollMs, heartbeatMs) must be rejected at this boundary\n * (chatgpt-codex-connector P2). */\nconst MAX_TIMER_DELAY_MS = 2_147_483_647;\n\n/** Internal handle for a lock we successfully acquired. */\ninterface HeldLock {\n readonly path: string;\n readonly ownerId: string;\n}\ntype LockAcquisitionFailure = { readonly failure: \"timeout\" | \"error\" };\n\n/**\n * Run `task` under an exclusive on-disk lock at `lockPath`.\n *\n * Cross-process mutex via `open(lockPath, \"wx\")` (atomic exclusive create).\n * While held, a heartbeat timer refreshes the lock's mtime so a legitimately\n * long task is not mistaken for a crashed holder and broken out from under. A\n * lock older than `opts.staleMs` is treated as stale and broken — but\n * REPLACEMENT-SAFE (NG7Bg): we capture the stale lock's identity (full content\n * line: `<pid> <owner-uuid> <iso>`) when judging it stale, then RE-READ and\n * RE-STAT immediately before `unlink`, deleting only if byte-identical AND\n * still stale. A replacement lock created in the window has a different owner\n * id / timestamp, so its content differs and is left untouched.\n *\n * `task` receives `acquired: boolean` — `true` when we hold the lock, `false`\n * when acquisition is unavailable. The controller's `failure` field\n * distinguishes a bounded busy-lock timeout from a permanent filesystem\n * acquisition error.\n * This explicit callback keeps the lock utility as the single lock home while\n * allowing callers to choose their timeout and failure policy.\n *\n * Release is ownership-checked: we only `unlink` a lock whose content still\n * identifies THIS acquirer (same owner id), so a replacement created after we\n * stopped heartbeating is never destroyed — mirroring the catalog's\n * `rebuildLockHeldBySelf`.\n *\n * ADOPTION NOTE: lock only the brief final read-merge-write window, never a\n * long scan — a scan-length lock makes concurrent writers time out and\n * silently drop work (catalog round 5, codex/cursor P2).\n */\nexport async function withHeldFileLock<T>(\n lockPath: string,\n opts: HeldFileLockOptions,\n task: (acquired: boolean, lock: HeldFileLockController) => Promise<T>,\n): Promise<T> {\n if (typeof lockPath !== \"string\" || lockPath.length === 0) {\n throw new TypeError(\"withHeldFileLock: lockPath must be a non-empty string\");\n }\n if (typeof opts?.staleMs !== \"number\" || !Number.isFinite(opts.staleMs) || opts.staleMs <= 0) {\n throw new TypeError(\n `withHeldFileLock: opts.staleMs must be a positive finite number ` +\n `(valid range: > 0 ms, finite; got ${formatInvalidNumber(opts?.staleMs)}).`,\n );\n }\n\n // Validate optional timings: a NaN/Infinity here is a real hazard (e.g.\n // `Date.now() + NaN` === NaN, so `Date.now() >= deadline` is always false and\n // the bounded acquire loop would wait forever instead of falling back to\n // best-effort). Reject invalid input rather than silently defaulting it\n // (codex P2 review). Omitting an option still picks its default.\n const maxWaitMs = optionalPositiveMs(opts.maxWaitMs, \"maxWaitMs\", DEFAULT_MAX_WAIT_MS, MAX_TIMER_DELAY_MS);\n const pollMs = optionalPositiveMs(opts.pollMs, \"pollMs\", DEFAULT_POLL_MS, MAX_TIMER_DELAY_MS);\n const heartbeatMs = optionalPositiveMs(\n opts.heartbeatMs,\n \"heartbeatMs\",\n Math.max(MIN_HEARTBEAT_MS, Math.floor(opts.staleMs / 3)),\n MAX_TIMER_DELAY_MS,\n );\n if (heartbeatMs >= opts.staleMs) {\n throw new TypeError(\n `withHeldFileLock: heartbeatMs (${heartbeatMs}) must be below staleMs (${opts.staleMs}) ` +\n `(valid range: > 0 and < staleMs ms) so at least one heartbeat lands per stale window.`,\n );\n }\n if (heartbeatMs > MAX_TIMER_DELAY_MS) {\n throw new TypeError(\n `withHeldFileLock: derived heartbeatMs (${heartbeatMs} = floor(staleMs/3)) exceeds ` +\n `Node's setTimeout ceiling (${MAX_TIMER_DELAY_MS} ms). Use an explicit opts.heartbeatMs ` +\n `at or below ${MAX_TIMER_DELAY_MS} ms.`,\n );\n }\n // Wrap the consumer's warning hook so a throwing callback never turns a\n // non-fatal advisory lock warning into an unhandled rejection (heartbeat\n // catch handler) or overrides the task's result (release path). The option\n // is documented as never throwing into the caller; enforce that here\n // (codex P2 review).\n const rawWarn = opts.onLockWarning;\n const warn = (message: string, err: unknown): void => {\n if (!rawWarn) return;\n try {\n rawWarn(message, err);\n } catch {\n /* swallow — a throwing advisory hook must not crash the guarded op */\n }\n };\n\n // Per-call owner identity. Two withHeldFileLock calls in the SAME process\n // get different ids, so neither mistakes the other's lock for its own\n // (stronger than the catalog's per-instance id, which is what we want for a\n // stateless utility).\n const ownerId = randomUUID();\n const lockDir = path.dirname(lockPath);\n\n const acquisition = await acquireLock(lockPath, lockDir, ownerId, opts, maxWaitMs, pollMs);\n if (\"failure\" in acquisition) {\n return task(false, { refresh: async () => false, failure: acquisition.failure });\n }\n const held = acquisition;\n\n // Heartbeat: while WE hold the lock, refresh its mtime so age-based stale\n // detection sees an active holder and does not break us out from under\n // (catalog round 5). Failures are swallowed (advisory lock); the timer is\n // always cleared in the finally.\n //\n // OWNERSHIP CHECK (codex P2): if our event loop was paused long enough that\n // another process judged us stale, broke our lock, and created a replacement,\n // we must NOT refresh the replacement's mtime — that would keep a (possibly\n // crashed) replacement looking fresh. Verify lockHeldBySelf before each\n // utimes; if ownership is lost, stop heartbeating (our lock is gone).\n const heartbeat = setInterval(() => {\n lockHeldBySelf(held)\n .then((ours) => {\n if (!ours) return; // broken/replaced — stop refreshing\n return utimes(held.path, new Date(), new Date());\n })\n .catch((err: unknown) => {\n warn(\"withHeldFileLock heartbeat refresh failed\", err);\n });\n }, heartbeatMs);\n // Don't keep the event loop alive solely for the heartbeat.\n heartbeat.unref?.();\n const controller: HeldFileLockController = {\n refresh: () => refreshHeldLock(held, warn, opts.onBeforeRefreshUtimesForTest),\n };\n try {\n return await task(true, controller);\n } finally {\n clearInterval(heartbeat);\n await releaseLock(held, warn, opts.onAfterReleaseRenameForTest);\n }\n}\n\n/**\n * Resolve an optional millisecond timing option, REJECTING invalid values\n * (NaN, Infinity, non-positive, or above `maxMs`) rather than silently defaulting\n * them. A NaN or Infinity maxWaitMs would make the bounded acquire loop wait\n * forever (`Date.now() + NaN` is NaN); a non-positive poll/heartbeat makes no\n * sense. Timer-backed options (pollMs, heartbeatMs) are bounded to Node's\n * setTimeout ceiling (`MAX_TIMER_DELAY_MS`): a value above 2^31−1 is silently\n * clamped to 1ms by the timer, turning a typo into tight polling (codex P2).\n * Omitting the option (`undefined`) picks `fallback`. Non-number types are also\n * rejected (defensive against config/env coercion).\n */\nfunction optionalPositiveMs(\n value: number | undefined,\n name: \"maxWaitMs\" | \"pollMs\" | \"heartbeatMs\",\n fallback: number,\n maxMs: number,\n): number {\n if (value === undefined) return fallback;\n if (typeof value !== \"number\" || !Number.isFinite(value) || value <= 0) {\n throw new TypeError(\n `withHeldFileLock: opts.${name} must be a positive finite number ` +\n `(valid range: > 0 ms, finite; got ${formatInvalidNumber(value)}). ` +\n `Omit the option to use the default of ${fallback} ms.`,\n );\n }\n if (value > maxMs) {\n throw new TypeError(\n `withHeldFileLock: opts.${name} (${value} ms) exceeds the ${maxMs} ms ` +\n `ceiling (Node's setTimeout clamps larger delays to 1ms, turning a ` +\n `typo into tight polling). Omit the option to use the default of ${fallback} ms.`,\n );\n }\n return value;\n}\n\n/**\n * Human-readable label for a rejected numeric input. Makes the error message\n * immediately actionable for NaN/Infinity (which print as \"NaN\"/\"Infinity\" via\n * String() but are easier to triage with an explicit sign), and surfaces the\n * actual type for non-number values (defensive against config/env coercion).\n */\nfunction formatInvalidNumber(value: unknown): string {\n if (typeof value === \"number\") {\n if (Number.isNaN(value)) return \"NaN\";\n if (value === Infinity) return \"+Infinity\";\n if (value === -Infinity) return \"-Infinity\";\n return String(value);\n }\n return `${typeof value} ${JSON.stringify(value)}`;\n}\n\n/**\n * Atomically create the lock file, looping until acquired or the bounded\n * deadline. Reports `timeout` for a busy lock and `error` for permanent\n * filesystem setup/I/O failures so callers can choose the safe policy.\n */\nasync function acquireLock(\n lockPath: string,\n lockDir: string,\n ownerId: string,\n opts: HeldFileLockOptions,\n maxWaitMs: number,\n pollMs: number,\n): Promise<HeldLock | LockAcquisitionFailure> {\n try {\n await mkdir(lockDir, { recursive: true });\n } catch {\n // Lock-directory setup failure is a permanent acquisition error.\n return { failure: \"error\" };\n }\n const deadline = Date.now() + maxWaitMs;\n for (;;) {\n try {\n const handle = await open(lockPath, \"wx\");\n let wroteMeta = true;\n try {\n await handle.writeFile(`${process.pid} ${ownerId} ${new Date().toISOString()}\\n`, \"utf8\");\n } catch {\n // The metadata write failed; the lock file may be empty or partial.\n // Our ownership check on release would NOT find this ownerId, leaving\n // a malformed lock that lingers until stale and blocks other callers\n // out of the mutex (codex P2). Undo our exclusive create and report\n // acquisition failure so the caller runs best-effort instead.\n wroteMeta = false;\n } finally {\n try {\n await handle.close();\n } catch {\n // close() can report a deferred I/O error (e.g. write that appeared\n // to succeed but failed on flush). The lock file may be malformed —\n // treat it as a metadata-write failure so the cleanup path unlinks\n // the orphaned lock (codex P2 review).\n wroteMeta = false;\n }\n }\n if (!wroteMeta) {\n await unlink(lockPath).catch(() => undefined);\n return { failure: \"error\" };\n }\n return { path: lockPath, ownerId };\n } catch (err) {\n if ((err as NodeJS.ErrnoException | undefined)?.code !== \"EEXIST\") {\n // Unexpected filesystem errors are permanent acquisition failures.\n return { failure: \"error\" };\n }\n // Lock exists: break it if stale, then poll. breakStaleLock is\n // replacement-safe (NG7Bg) and never throws.\n await breakStaleLock(lockPath, opts.staleMs, opts.onBeforeBreakStaleUnlinkForTest);\n if (Date.now() >= deadline) return { failure: \"timeout\" };\n // Cap the sleep to the remaining budget so a large pollMs cannot block\n // acquisition far past maxWaitMs (e.g. maxWaitMs=1000, pollMs=60000\n // would otherwise block ~60s instead of 1s — codex P2).\n await sleep(Math.min(pollMs, deadline - Date.now()));\n }\n }\n}\n\n/**\n * Replacement-safe stale-lock breaking (NG7Bg, #1506 round 28). Capture the\n * lock's identity when judging it stale, then ATOMICALLY rename it to a unique\n * trash path and verify the moved content matches. A replacement lock created\n * in the race window is either left untouched (different identity at\n * lockPath, so the rename moves the stale lock — not the replacement) or\n * restored (if the rename accidentally moves a replacement, the verify\n * detects the mismatch and renames it back).\n *\n * ATOMICITY (codex P2): `rename` is atomic on POSIX — only ONE contender can\n * successfully rename a given file. This eliminates the TOCTOU between the\n * identity/stat checks and the deletion that a bare `unlink` leaves open:\n * without rename, contender A could verify identity X, pause, then unlink\n * contender B's freshly acquired replacement Y. With rename, A moves whatever\n * is at lockPath, then checks: if it is X, A broke the stale lock; if it is\n * not X (a replacement appeared between A's last check and the rename), A\n * restores it.\n */\nasync function breakStaleLock(\n lockPath: string,\n staleMs: number,\n onBeforeBreakStaleUnlinkForTest: (() => Promise<void> | void) | undefined,\n): Promise<void> {\n let staleIdentity: string;\n try {\n const info = await stat(lockPath);\n if (Date.now() - info.mtimeMs <= staleMs) {\n // Not stale (a live holder's heartbeat keeps it fresh) — leave it.\n return;\n }\n staleIdentity = await readFile(lockPath, \"utf8\");\n } catch {\n // Lock vanished (released by holder) or stat/read failed — nothing to do.\n return;\n }\n // Test seam: simulate a replacement lock being created in the race window\n // between the staleness judgment and the atomic break. No-op in production.\n if (onBeforeBreakStaleUnlinkForTest) {\n await onBeforeBreakStaleUnlinkForTest();\n }\n try {\n // Re-validate immediately before breaking: the lock must still carry the\n // SAME identity AND still be stale.\n const current = await readFile(lockPath, \"utf8\");\n if (current !== staleIdentity) return; // replaced — leave the fresh lock\n const recheck = await stat(lockPath);\n if (Date.now() - recheck.mtimeMs <= staleMs) return; // heartbeat refreshed it\n\n // ATOMIC BREAK: rename is atomic on POSIX. Only one contender succeeds;\n // others get ENOENT (the file is already gone). After the rename, verify\n // the moved content: if it matches staleIdentity we broke the right lock;\n // if it does not, a replacement appeared in the window and we restore it.\n const trashPath = `${lockPath}.breaking.${process.pid}.${Date.now()}`;\n await rename(lockPath, trashPath);\n try {\n const moved = await readFile(trashPath, \"utf8\");\n if (moved !== staleIdentity) {\n // We accidentally moved a replacement lock (created between our last\n // check and the rename). Restore it so the replacement holder's lock\n // survives. Use link (not rename) to AVOID overwriting a fresh lock\n // that a third contender may have acquired at lockPath while the file\n // was in trash: link fails with EEXIST if lockPath exists, leaving\n // the third contender's lock intact (codex P2 review).\n try {\n await link(trashPath, lockPath);\n // link succeeded — remove the redundant trash hard link. The lock\n // now lives only at lockPath.\n await unlink(trashPath).catch(() => undefined);\n } catch {\n // lockPath already exists (a third contender acquired it). Do NOT\n // unlink the moved file — it may be a LIVE lock whose holder is\n // still in its critical section. Destroying it would leave the\n // holder running with no visible lock, breaking mutual exclusion\n // (codex P2). Leave it in trash as a breadcrumb; it is not at\n // lockPath so it does not block other contenders.\n }\n } else {\n // Content matches — but verify the moved file is STILL stale. The\n // original holder may have resumed and heartbeated between our\n // pre-rename stat() and the rename, refreshing the mtime. If so, the\n // holder is live: restore the lock instead of deleting it (codex P2).\n const movedStat = await stat(trashPath);\n if (Date.now() - movedStat.mtimeMs <= staleMs) {\n // Mtime was refreshed — the holder resumed. Restore the lock.\n try {\n await link(trashPath, lockPath);\n await unlink(trashPath).catch(() => undefined);\n } catch {\n // lockPath already exists — another contender acquired it. Do NOT\n // unlink the moved file (it may be a live lock). Leave it in trash.\n }\n } else {\n // Still stale — we broke the right lock. Clean up the trash.\n await unlink(trashPath).catch(() => undefined);\n }\n }\n } catch {\n // Could not read the trash file — clean it up best-effort.\n await unlink(trashPath).catch(() => undefined);\n }\n } catch {\n // The lock changed/vanished between checks — another process handled it.\n }\n}\n\n/**\n * Release the lock ONLY if its content still identifies THIS acquirer (same\n * owner id). Two-stage ownership check:\n *\n * 1. PRE-CHECK (chatgpt-codex-connector P2): read lockPath BEFORE renaming.\n * If the lock is already a replacement (a contender broke our stale lock),\n * return WITHOUT renaming — renaming a replacement out of lockPath leaves\n * it empty, letting a third contender acquire while the replacement holder\n * is still active. The replacement is safe at lockPath; leave it alone.\n *\n * 2. ATOMIC CLAIM: if the pre-check saw our ownerId, rename lockPath→trash\n * (POSIX-atomic) and re-verify on the moved file. A replacement could\n * appear between the pre-check and the rename; if the moved file is no\n * longer ours, restore it via link (non-overwriting). This ties the\n * ownership check to the deletion so a bare readFile-then-unlink TOCTOU\n * cannot delete a fresh replacement (codex P2).\n */\nasync function releaseLock(\n held: HeldLock,\n warn: (message: string, err: unknown) => void,\n onAfterReleaseRenameForTest: (() => Promise<void> | void) | undefined,\n): Promise<void> {\n try {\n // PRE-CHECK (chatgpt-codex-connector P2): read lockPath before renaming. If\n // the lock is no longer ours, a contender broke our stale lock and created a\n // replacement. Return WITHOUT renaming — renaming the replacement out of\n // lockPath leaves it empty, so a third contender could acquire while the\n // replacement holder is still active. The replacement is safe at lockPath.\n let precheck: string;\n try {\n precheck = await readFile(held.path, \"utf8\");\n } catch {\n return; // lock vanished — nothing to release.\n }\n if (!precheck.includes(held.ownerId)) {\n return; // replacement lock — leave it untouched for its holder.\n }\n // It was ours when we read it. Atomically claim via rename, then re-verify\n // on the moved file: a replacement could appear between the pre-check read\n // above and this rename.\n const trashPath = `${held.path}.releasing.${process.pid}.${Date.now()}`;\n await rename(held.path, trashPath);\n // Test seam: simulate a third contender acquiring the now-empty lockPath\n // in the rename-to-restore window. No-op in production.\n if (onAfterReleaseRenameForTest) {\n await onAfterReleaseRenameForTest();\n }\n try {\n const moved = await readFile(trashPath, \"utf8\");\n if (moved.includes(held.ownerId)) {\n // Still our lock — safe to delete.\n await unlink(trashPath).catch(() => undefined);\n } else {\n // Not ours: a replacement appeared between the pre-check and the rename.\n // Restore it via link (non-overwriting — if lockPath already has a newer\n // lock, leave it).\n try {\n await link(trashPath, held.path);\n } catch {\n // lockPath already exists — a newer holder is active. Leave the\n // moved file in trash rather than destroying a live lock (codex P2).\n return;\n }\n await unlink(trashPath).catch(() => undefined);\n }\n } catch {\n // Could not read the moved file — clean it up best-effort.\n await unlink(trashPath).catch(() => undefined);\n }\n } catch (err) {\n // Best-effort release; a stale lock will be broken on the next acquire.\n warn(\"withHeldFileLock release failed\", err);\n }\n}\n\n/**\n * Whether the lock file at `held.path` was written by THIS acquirer (same owner\n * id). Reads the content and matches the `<pid> <owner-uuid>` prefix; the iso\n * timestamp varies so it is not part of the identity check.\n */\nasync function lockHeldBySelf(held: HeldLock): Promise<boolean> {\n try {\n const body = await readFile(held.path, \"utf8\");\n const parts = body.trim().split(/\\s+/);\n const fileOwner = parts[1];\n return typeof fileOwner === \"string\" && fileOwner === held.ownerId;\n } catch {\n return false;\n }\n}\n\n/**\n * Re-assert ownership and refresh the lock mtime for {@link HeldFileLockController.refresh}.\n * Returns `true` only while THIS acquirer still owns the lock. When ownership\n * was lost (a peer stale-broke and replaced the lock while a CPU-bound section\n * blocked the event loop), returns `false` WITHOUT touching the replacement's\n * mtime — mirroring the heartbeat's ownership guard.\n *\n * FAIL-CLOSED on the mtime bump (#2033): a `utimes` failure means we cannot\n * prove the lock is still ours AND fresh — the file may have been stale-broken,\n * deleted, or replaced between the ownership check and the bump — so we report\n * lost ownership rather than let the caller rewrite the ledger on an unrefreshed\n * (soon-stale) or already-replaced lock. After a successful bump we RE-CHECK\n * ownership: a peer could have broken and replaced the lock in the window, in\n * which case `utimes` just refreshed the replacement's mtime and we must not\n * report held.\n */\nasync function refreshHeldLock(\n held: HeldLock,\n warn: (message: string, err: unknown) => void,\n onBeforeRefreshUtimesForTest?: () => Promise<void> | void,\n): Promise<boolean> {\n if (!(await lockHeldBySelf(held))) return false;\n if (onBeforeRefreshUtimesForTest) await onBeforeRefreshUtimesForTest();\n try {\n await utimes(held.path, new Date(), new Date());\n } catch (err) {\n warn(\"withHeldFileLock manual refresh failed\", err);\n return false;\n }\n return lockHeldBySelf(held);\n}\n\nfunction sleep(ms: number): Promise<void> {\n // Manual deferred instead of Promise.withResolvers (ES2024) — plugin-openclaw's\n // standalone tsconfig targets ES2022 lib and this module is reachable from its\n // type graph, so withResolvers would TS2550 there (same fix as\n // extraction-faithfulness.ts:467).\n return new Promise<void>((resolve) => {\n // NOT unref'd: this polls inside an awaited acquire loop, so the caller's\n // await chain keeps the loop alive; unref would let Node exit mid-poll when\n // nothing else is pending (the heartbeat interval IS unref'd separately).\n setTimeout(resolve, ms);\n });\n}\n"],"mappings":";AAqBA,SAAS,kBAAkB;AAC3B,SAAS,MAAM,OAAO,MAAM,UAAU,QAAQ,MAAM,QAAQ,cAAc;AAC1E,OAAO,UAAU;AAyBV,IAAM,qBAAN,MAAyB;AAAA,EACb,SAAS,oBAAI,IAAgC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgB9D,UAAa,KAAa,MAAoC;AAC5D,QAAI,OAAO,QAAQ,YAAY,IAAI,WAAW,GAAG;AAC/C,YAAM,IAAI,UAAU,8DAA8D;AAAA,IACpF;AACA,QAAI,OAAO,SAAS,YAAY;AAC9B,YAAM,IAAI,UAAU,2EAA2E;AAAA,IACjG;AAEA,QAAI,QAAQ,KAAK,OAAO,IAAI,GAAG;AAC/B,QAAI,CAAC,OAAO;AACV,cAAQ,EAAE,MAAM,QAAQ,QAAQ,EAAE;AAClC,WAAK,OAAO,IAAI,KAAK,KAAK;AAAA,IAC5B;AAIA,UAAM,MAAM,MAAM,KAAK,KAAK,IAAI;AAMhC,UAAM,YAAY,IAAI,KAAK,YAAY,UAAU;AACjD,UAAM,OAAO;AAcb,SAAK,UAAU;AAAA,MACb,MAAM;AACJ,YAAI,SAAS,MAAM,SAAS,WAAW;AACrC,eAAK,OAAO,OAAO,GAAG;AAAA,QACxB;AAAA,MACF;AAAA,MACA,MAAM;AAAA,IACR;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,qBAA6B;AAC3B,WAAO,KAAK,OAAO;AAAA,EACrB;AACF;AAQA,SAAS,aAAmB;AAE5B;AAOA,IAAI;AAQG,SAAS,mBAAsB,KAAa,MAAoC;AACrF,MAAI,CAAC,kBAAmB,qBAAoB,IAAI,mBAAmB;AACnE,SAAO,kBAAkB,UAAU,KAAK,IAAI;AAC9C;AA4FA,IAAM,sBAAsB;AAE5B,IAAM,kBAAkB;AAExB,IAAM,mBAAmB;AAKzB,IAAM,qBAAqB;AAsC3B,eAAsB,iBACpB,UACA,MACA,MACY;AACZ,MAAI,OAAO,aAAa,YAAY,SAAS,WAAW,GAAG;AACzD,UAAM,IAAI,UAAU,uDAAuD;AAAA,EAC7E;AACA,MAAI,OAAO,MAAM,YAAY,YAAY,CAAC,OAAO,SAAS,KAAK,OAAO,KAAK,KAAK,WAAW,GAAG;AAC5F,UAAM,IAAI;AAAA,MACR,qGACuC,oBAAoB,MAAM,OAAO,CAAC;AAAA,IAC3E;AAAA,EACF;AAOA,QAAM,YAAY,mBAAmB,KAAK,WAAW,aAAa,qBAAqB,kBAAkB;AACzG,QAAM,SAAS,mBAAmB,KAAK,QAAQ,UAAU,iBAAiB,kBAAkB;AAC5F,QAAM,cAAc;AAAA,IAClB,KAAK;AAAA,IACL;AAAA,IACA,KAAK,IAAI,kBAAkB,KAAK,MAAM,KAAK,UAAU,CAAC,CAAC;AAAA,IACvD;AAAA,EACF;AACA,MAAI,eAAe,KAAK,SAAS;AAC/B,UAAM,IAAI;AAAA,MACR,kCAAkC,WAAW,4BAA4B,KAAK,OAAO;AAAA,IAEvF;AAAA,EACF;AACA,MAAI,cAAc,oBAAoB;AACpC,UAAM,IAAI;AAAA,MACR,0CAA0C,WAAW,2DACrB,kBAAkB,sDACjC,kBAAkB;AAAA,IACrC;AAAA,EACF;AAMA,QAAM,UAAU,KAAK;AACrB,QAAM,OAAO,CAAC,SAAiB,QAAuB;AACpD,QAAI,CAAC,QAAS;AACd,QAAI;AACF,cAAQ,SAAS,GAAG;AAAA,IACtB,QAAQ;AAAA,IAER;AAAA,EACF;AAMA,QAAM,UAAU,WAAW;AAC3B,QAAM,UAAU,KAAK,QAAQ,QAAQ;AAErC,QAAM,cAAc,MAAM,YAAY,UAAU,SAAS,SAAS,MAAM,WAAW,MAAM;AACzF,MAAI,aAAa,aAAa;AAC5B,WAAO,KAAK,OAAO,EAAE,SAAS,YAAY,OAAO,SAAS,YAAY,QAAQ,CAAC;AAAA,EACjF;AACA,QAAM,OAAO;AAYb,QAAM,YAAY,YAAY,MAAM;AAClC,mBAAe,IAAI,EAChB,KAAK,CAAC,SAAS;AACd,UAAI,CAAC,KAAM;AACX,aAAO,OAAO,KAAK,MAAM,oBAAI,KAAK,GAAG,oBAAI,KAAK,CAAC;AAAA,IACjD,CAAC,EACA,MAAM,CAAC,QAAiB;AACvB,WAAK,6CAA6C,GAAG;AAAA,IACvD,CAAC;AAAA,EACL,GAAG,WAAW;AAEd,YAAU,QAAQ;AAClB,QAAM,aAAqC;AAAA,IACzC,SAAS,MAAM,gBAAgB,MAAM,MAAM,KAAK,4BAA4B;AAAA,EAC9E;AACA,MAAI;AACF,WAAO,MAAM,KAAK,MAAM,UAAU;AAAA,EACpC,UAAE;AACA,kBAAc,SAAS;AACvB,UAAM,YAAY,MAAM,MAAM,KAAK,2BAA2B;AAAA,EAChE;AACF;AAaA,SAAS,mBACP,OACA,MACA,UACA,OACQ;AACR,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,GAAG;AACtE,UAAM,IAAI;AAAA,MACR,0BAA0B,IAAI,uEACS,oBAAoB,KAAK,CAAC,4CACtB,QAAQ;AAAA,IACrD;AAAA,EACF;AACA,MAAI,QAAQ,OAAO;AACjB,UAAM,IAAI;AAAA,MACR,0BAA0B,IAAI,KAAK,KAAK,oBAAoB,KAAK,yIAEI,QAAQ;AAAA,IAC/E;AAAA,EACF;AACA,SAAO;AACT;AAQA,SAAS,oBAAoB,OAAwB;AACnD,MAAI,OAAO,UAAU,UAAU;AAC7B,QAAI,OAAO,MAAM,KAAK,EAAG,QAAO;AAChC,QAAI,UAAU,SAAU,QAAO;AAC/B,QAAI,UAAU,UAAW,QAAO;AAChC,WAAO,OAAO,KAAK;AAAA,EACrB;AACA,SAAO,GAAG,OAAO,KAAK,IAAI,KAAK,UAAU,KAAK,CAAC;AACjD;AAOA,eAAe,YACb,UACA,SACA,SACA,MACA,WACA,QAC4C;AAC5C,MAAI;AACF,UAAM,MAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AAAA,EAC1C,QAAQ;AAEN,WAAO,EAAE,SAAS,QAAQ;AAAA,EAC5B;AACA,QAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,aAAS;AACP,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,UAAU,IAAI;AACxC,UAAI,YAAY;AAChB,UAAI;AACF,cAAM,OAAO,UAAU,GAAG,QAAQ,GAAG,IAAI,OAAO,KAAI,oBAAI,KAAK,GAAE,YAAY,CAAC;AAAA,GAAM,MAAM;AAAA,MAC1F,QAAQ;AAMN,oBAAY;AAAA,MACd,UAAE;AACA,YAAI;AACF,gBAAM,OAAO,MAAM;AAAA,QACrB,QAAQ;AAKN,sBAAY;AAAA,QACd;AAAA,MACF;AACA,UAAI,CAAC,WAAW;AACd,cAAM,OAAO,QAAQ,EAAE,MAAM,MAAM,MAAS;AAC5C,eAAO,EAAE,SAAS,QAAQ;AAAA,MAC5B;AACA,aAAO,EAAE,MAAM,UAAU,QAAQ;AAAA,IACnC,SAAS,KAAK;AACZ,UAAK,KAA2C,SAAS,UAAU;AAEjE,eAAO,EAAE,SAAS,QAAQ;AAAA,MAC5B;AAGA,YAAM,eAAe,UAAU,KAAK,SAAS,KAAK,+BAA+B;AACjF,UAAI,KAAK,IAAI,KAAK,SAAU,QAAO,EAAE,SAAS,UAAU;AAIxD,YAAM,MAAM,KAAK,IAAI,QAAQ,WAAW,KAAK,IAAI,CAAC,CAAC;AAAA,IACrD;AAAA,EACF;AACF;AAoBA,eAAe,eACb,UACA,SACA,iCACe;AACf,MAAI;AACJ,MAAI;AACF,UAAM,OAAO,MAAM,KAAK,QAAQ;AAChC,QAAI,KAAK,IAAI,IAAI,KAAK,WAAW,SAAS;AAExC;AAAA,IACF;AACA,oBAAgB,MAAM,SAAS,UAAU,MAAM;AAAA,EACjD,QAAQ;AAEN;AAAA,EACF;AAGA,MAAI,iCAAiC;AACnC,UAAM,gCAAgC;AAAA,EACxC;AACA,MAAI;AAGF,UAAM,UAAU,MAAM,SAAS,UAAU,MAAM;AAC/C,QAAI,YAAY,cAAe;AAC/B,UAAM,UAAU,MAAM,KAAK,QAAQ;AACnC,QAAI,KAAK,IAAI,IAAI,QAAQ,WAAW,QAAS;AAM7C,UAAM,YAAY,GAAG,QAAQ,aAAa,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC;AACnE,UAAM,OAAO,UAAU,SAAS;AAChC,QAAI;AACF,YAAM,QAAQ,MAAM,SAAS,WAAW,MAAM;AAC9C,UAAI,UAAU,eAAe;AAO3B,YAAI;AACF,gBAAM,KAAK,WAAW,QAAQ;AAG9B,gBAAM,OAAO,SAAS,EAAE,MAAM,MAAM,MAAS;AAAA,QAC/C,QAAQ;AAAA,QAOR;AAAA,MACF,OAAO;AAKL,cAAM,YAAY,MAAM,KAAK,SAAS;AACtC,YAAI,KAAK,IAAI,IAAI,UAAU,WAAW,SAAS;AAE7C,cAAI;AACF,kBAAM,KAAK,WAAW,QAAQ;AAC9B,kBAAM,OAAO,SAAS,EAAE,MAAM,MAAM,MAAS;AAAA,UAC/C,QAAQ;AAAA,UAGR;AAAA,QACF,OAAO;AAEL,gBAAM,OAAO,SAAS,EAAE,MAAM,MAAM,MAAS;AAAA,QAC/C;AAAA,MACF;AAAA,IACF,QAAQ;AAEN,YAAM,OAAO,SAAS,EAAE,MAAM,MAAM,MAAS;AAAA,IAC/C;AAAA,EACF,QAAQ;AAAA,EAER;AACF;AAmBA,eAAe,YACb,MACA,MACA,6BACe;AACf,MAAI;AAMF,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,SAAS,KAAK,MAAM,MAAM;AAAA,IAC7C,QAAQ;AACN;AAAA,IACF;AACA,QAAI,CAAC,SAAS,SAAS,KAAK,OAAO,GAAG;AACpC;AAAA,IACF;AAIA,UAAM,YAAY,GAAG,KAAK,IAAI,cAAc,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC;AACrE,UAAM,OAAO,KAAK,MAAM,SAAS;AAGjC,QAAI,6BAA6B;AAC/B,YAAM,4BAA4B;AAAA,IACpC;AACA,QAAI;AACF,YAAM,QAAQ,MAAM,SAAS,WAAW,MAAM;AAC9C,UAAI,MAAM,SAAS,KAAK,OAAO,GAAG;AAEhC,cAAM,OAAO,SAAS,EAAE,MAAM,MAAM,MAAS;AAAA,MAC/C,OAAO;AAIL,YAAI;AACF,gBAAM,KAAK,WAAW,KAAK,IAAI;AAAA,QACjC,QAAQ;AAGN;AAAA,QACF;AACA,cAAM,OAAO,SAAS,EAAE,MAAM,MAAM,MAAS;AAAA,MAC/C;AAAA,IACF,QAAQ;AAEN,YAAM,OAAO,SAAS,EAAE,MAAM,MAAM,MAAS;AAAA,IAC/C;AAAA,EACF,SAAS,KAAK;AAEZ,SAAK,mCAAmC,GAAG;AAAA,EAC7C;AACF;AAOA,eAAe,eAAe,MAAkC;AAC9D,MAAI;AACF,UAAM,OAAO,MAAM,SAAS,KAAK,MAAM,MAAM;AAC7C,UAAM,QAAQ,KAAK,KAAK,EAAE,MAAM,KAAK;AACrC,UAAM,YAAY,MAAM,CAAC;AACzB,WAAO,OAAO,cAAc,YAAY,cAAc,KAAK;AAAA,EAC7D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAkBA,eAAe,gBACb,MACA,MACA,8BACkB;AAClB,MAAI,CAAE,MAAM,eAAe,IAAI,EAAI,QAAO;AAC1C,MAAI,6BAA8B,OAAM,6BAA6B;AACrE,MAAI;AACF,UAAM,OAAO,KAAK,MAAM,oBAAI,KAAK,GAAG,oBAAI,KAAK,CAAC;AAAA,EAChD,SAAS,KAAK;AACZ,SAAK,0CAA0C,GAAG;AAClD,WAAO;AAAA,EACT;AACA,SAAO,eAAe,IAAI;AAC5B;AAEA,SAAS,MAAM,IAA2B;AAKxC,SAAO,IAAI,QAAc,CAAC,YAAY;AAIpC,eAAW,SAAS,EAAE;AAAA,EACxB,CAAC;AACH;","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  listNamespaces
3
- } from "./chunk-NBDCZWP4.js";
3
+ } from "./chunk-I5NLEENJ.js";
4
4
  import {
5
5
  capabilityAllowsNamespace
6
6
  } from "./chunk-MZWSOLMT.js";
@@ -265,4 +265,4 @@ export {
265
265
  computeServiceCorpusWatermarks,
266
266
  computeServiceCorpusCensus
267
267
  };
268
- //# sourceMappingURL=chunk-Q6VFVAKZ.js.map
268
+ //# sourceMappingURL=chunk-GIBMDSJ2.js.map
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  resolveDefaultNamespaceRoot,
6
6
  resolveNamespaceStorageRoot
7
- } from "./chunk-XEQ7OT7U.js";
7
+ } from "./chunk-IC6CBHCA.js";
8
8
  import {
9
9
  namespaceIdentityFromToken,
10
10
  namespaceIdentityToken,
@@ -13,7 +13,7 @@ import {
13
13
  import {
14
14
  MutationSerializer,
15
15
  withHeldFileLock
16
- } from "./chunk-WL5EBRDH.js";
16
+ } from "./chunk-FFAZ2XFU.js";
17
17
  import {
18
18
  displayErrorDetail
19
19
  } from "./chunk-RL6NEVXT.js";
@@ -2204,4 +2204,4 @@ export {
2204
2204
  runNamespaceMaintenancePlan,
2205
2205
  runNamespaceMaintenanceBatchPlan
2206
2206
  };
2207
- //# sourceMappingURL=chunk-ZXAGJFHJ.js.map
2207
+ //# sourceMappingURL=chunk-H5CPS5JP.js.map
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-D2ANTDGO.js";
4
4
  import {
5
5
  NamespaceStorageRouter
6
- } from "./chunk-XEQ7OT7U.js";
6
+ } from "./chunk-IC6CBHCA.js";
7
7
  import {
8
8
  namespaceIdentityFromToken,
9
9
  namespaceIdentityToken
@@ -207,4 +207,4 @@ export {
207
207
  verifyNamespaces,
208
208
  runNamespaceMigration
209
209
  };
210
- //# sourceMappingURL=chunk-NBDCZWP4.js.map
210
+ //# sourceMappingURL=chunk-I5NLEENJ.js.map
@@ -60,7 +60,7 @@ import {
60
60
  } from "./chunk-JUC24CTX.js";
61
61
  import {
62
62
  parseOfflineSyncExcludes
63
- } from "./chunk-E4OCKU67.js";
63
+ } from "./chunk-7XC2HX75.js";
64
64
  import {
65
65
  log
66
66
  } from "./chunk-3WCASNQZ.js";
@@ -3962,4 +3962,4 @@ export {
3962
3962
  VALID_MEMORY_CATEGORIES,
3963
3963
  parseConfig
3964
3964
  };
3965
- //# sourceMappingURL=chunk-DRBXHRXE.js.map
3965
+ //# sourceMappingURL=chunk-IB26HVAJ.js.map
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  StorageManager
3
- } from "./chunk-FCDWWCLX.js";
3
+ } from "./chunk-RGIWINDN.js";
4
4
  import {
5
5
  namespaceIdentityToken,
6
6
  normalizeNamespaceIdentity
7
7
  } from "./chunk-EGGE52UU.js";
8
8
  import {
9
9
  MutationSerializer
10
- } from "./chunk-WL5EBRDH.js";
10
+ } from "./chunk-FFAZ2XFU.js";
11
11
  import {
12
12
  keyring_exports,
13
13
  secureStoreDir
@@ -388,4 +388,4 @@ export {
388
388
  resolveNamespaceStorageRoot,
389
389
  NamespaceStorageRouter
390
390
  };
391
- //# sourceMappingURL=chunk-XEQ7OT7U.js.map
391
+ //# sourceMappingURL=chunk-IC6CBHCA.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  StorageManager
3
- } from "./chunk-FCDWWCLX.js";
3
+ } from "./chunk-RGIWINDN.js";
4
4
  import {
5
5
  composeMemoryEnvelope
6
6
  } from "./chunk-CGASUJVP.js";
@@ -546,4 +546,4 @@ export {
546
546
  setSemanticRulePromotionTestHooks,
547
547
  promoteSemanticRuleFromMemory
548
548
  };
549
- //# sourceMappingURL=chunk-CMRQ34J5.js.map
549
+ //# sourceMappingURL=chunk-JCBWCYEJ.js.map