@remnic/core 10.0.48 → 10.0.50

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 (449) hide show
  1. package/dist/access-admin-ops-surface.js +73 -70
  2. package/dist/access-audit.js +2 -2
  3. package/dist/access-authorization-probe.js +74 -71
  4. package/dist/access-boundary.js +73 -70
  5. package/dist/access-cli.js +170 -167
  6. package/dist/access-cli.js.map +1 -1
  7. package/dist/access-extraction-force-flush.js +73 -70
  8. package/dist/access-http-briefing.d.ts +2 -0
  9. package/dist/access-http-briefing.d.ts.map +1 -1
  10. package/dist/access-http-briefing.js +74 -71
  11. package/dist/access-http-query.js +75 -72
  12. package/dist/access-http.d.ts.map +1 -1
  13. package/dist/access-http.js +96 -93
  14. package/dist/access-identity-continuity-surface.js +73 -70
  15. package/dist/access-lcm-surface.js +73 -70
  16. package/dist/access-mcp.js +85 -82
  17. package/dist/access-namespace-preflight.js +73 -70
  18. package/dist/access-observe-write-category.d.ts +1 -1
  19. package/dist/access-observe-write-category.d.ts.map +1 -1
  20. package/dist/access-observe-write-category.js +15 -14
  21. package/dist/access-observe-write-surface.d.ts.map +1 -1
  22. package/dist/access-observe-write-surface.js +73 -70
  23. package/dist/access-offline-manifest.js +9 -8
  24. package/dist/access-operations-batch.js +76 -73
  25. package/dist/access-operations.js +83 -80
  26. package/dist/access-recall-concurrency.js +73 -70
  27. package/dist/access-recall-response.js +73 -70
  28. package/dist/access-recall-surface.js +73 -70
  29. package/dist/access-schema.js +11 -10
  30. package/dist/access-service.d.ts +5 -0
  31. package/dist/access-service.d.ts.map +1 -1
  32. package/dist/access-service.js +73 -70
  33. package/dist/access-surface-catalog.js +75 -72
  34. package/dist/access-surface-catalog.js.map +1 -1
  35. package/dist/active-recall.js +32 -30
  36. package/dist/active-recall.js.map +1 -1
  37. package/dist/adapters/index.js +4 -4
  38. package/dist/adapters/registry.js +2 -2
  39. package/dist/{auto-sync-BNCBMU6H.js → auto-sync-UQQ6YJSB.js} +3 -3
  40. package/dist/briefing-deadline.d.ts +7 -0
  41. package/dist/briefing-deadline.d.ts.map +1 -0
  42. package/dist/briefing-deadline.js +13 -0
  43. package/dist/briefing-window.d.ts +12 -4
  44. package/dist/briefing-window.d.ts.map +1 -1
  45. package/dist/briefing-window.js +3 -1
  46. package/dist/briefing.d.ts +2 -0
  47. package/dist/briefing.d.ts.map +1 -1
  48. package/dist/briefing.js +22 -20
  49. package/dist/buffer.js +6 -6
  50. package/dist/calibration.js +2 -2
  51. package/dist/{capsule-crypto-GWVG7LGC.js → capsule-crypto-7FJQINUR.js} +2 -2
  52. package/dist/{capsule-merge-RNQUP2VL.js → capsule-merge-D4BTIJGK.js} +3 -3
  53. package/dist/capture-write-lock-scope.d.ts +7 -0
  54. package/dist/capture-write-lock-scope.d.ts.map +1 -0
  55. package/dist/capture-write-lock-scope.js +10 -0
  56. package/dist/causal-behavior.js +2 -2
  57. package/dist/causal-chain.js +2 -2
  58. package/dist/causal-consolidation.js +23 -22
  59. package/dist/causal-consolidation.js.map +1 -1
  60. package/dist/causal-retrieval.js +2 -2
  61. package/dist/causal-trajectory-graph.js +3 -3
  62. package/dist/causal-trajectory.js +1 -1
  63. package/dist/{chunk-FAKRTS3Y.js → chunk-2AKIVSHP.js} +2 -2
  64. package/dist/{chunk-EIYJK7OD.js → chunk-2M36PR7R.js} +2 -2
  65. package/dist/{chunk-OWHERGF2.js → chunk-2NLLXCJG.js} +2 -2
  66. package/dist/{chunk-6P6MZDBL.js → chunk-3NL4ALUP.js} +2 -2
  67. package/dist/{chunk-6CC2YCKG.js → chunk-43TUWQNR.js} +3 -3
  68. package/dist/{chunk-H7BZBSP6.js → chunk-52II7PB6.js} +4 -4
  69. package/dist/{chunk-2NLCDNGA.js → chunk-564YP3DW.js} +2 -2
  70. package/dist/{chunk-5KGYR5VN.js → chunk-5DVOBBTV.js} +2 -2
  71. package/dist/{chunk-RT2GN6KQ.js → chunk-5FUDSK6Z.js} +2 -2
  72. package/dist/{chunk-B4XSXFKI.js → chunk-5G53JSRO.js} +2 -2
  73. package/dist/{chunk-BGK3SFCC.js → chunk-5X2H3X4J.js} +2 -2
  74. package/dist/{chunk-OXSTKCYK.js → chunk-6AEKSOWV.js} +33 -5
  75. package/dist/chunk-6AEKSOWV.js.map +1 -0
  76. package/dist/{chunk-4J7BNL7B.js → chunk-6ZQX3R66.js} +2 -2
  77. package/dist/{chunk-JNLYHJG7.js → chunk-74AY3S7U.js} +4 -4
  78. package/dist/{chunk-RUAIO5UA.js → chunk-7J6A5MV2.js} +2 -2
  79. package/dist/{chunk-A45QXZMU.js → chunk-7SAE6T36.js} +2 -2
  80. package/dist/chunk-7VLBZKNB.js +26 -0
  81. package/dist/chunk-7VLBZKNB.js.map +1 -0
  82. package/dist/{chunk-QE2XQM3S.js → chunk-7YVER7ZO.js} +2 -2
  83. package/dist/{chunk-IJH6METJ.js → chunk-A3UFV4K3.js} +4 -4
  84. package/dist/{chunk-LWRIGKFM.js → chunk-A5L5KGDI.js} +19 -1
  85. package/dist/chunk-A5L5KGDI.js.map +1 -0
  86. package/dist/{chunk-LXN737FX.js → chunk-AAANTXCQ.js} +2 -2
  87. package/dist/{chunk-I3IPXGFQ.js → chunk-AJJPXUE6.js} +4 -4
  88. package/dist/{chunk-2DPJFJFH.js → chunk-BNJAVRNJ.js} +11 -11
  89. package/dist/{chunk-YL564QS2.js → chunk-CCL2OXFO.js} +3 -3
  90. package/dist/{chunk-CHSRA76M.js → chunk-CRN3EPE2.js} +4 -3
  91. package/dist/chunk-CRN3EPE2.js.map +1 -0
  92. package/dist/{chunk-ZX5N5D6M.js → chunk-DAOMWGO2.js} +4 -4
  93. package/dist/{chunk-YUE7J2HZ.js → chunk-DMKTOCC7.js} +2 -2
  94. package/dist/chunk-DMKTOCC7.js.map +1 -0
  95. package/dist/{chunk-FRXS6SC7.js → chunk-EKUI7I2X.js} +20 -4
  96. package/dist/chunk-EKUI7I2X.js.map +1 -0
  97. package/dist/{chunk-PKPOO6X5.js → chunk-EKYWZYZJ.js} +25 -17
  98. package/dist/chunk-EKYWZYZJ.js.map +1 -0
  99. package/dist/{chunk-23ZMQONL.js → chunk-ERMT5WQM.js} +2 -2
  100. package/dist/{chunk-VTCNVGM6.js → chunk-EWBXMGXR.js} +2 -2
  101. package/dist/{chunk-YNDLCWXS.js → chunk-EZ25VE3G.js} +4 -4
  102. package/dist/{chunk-XTN54NML.js → chunk-G3T6YMBO.js} +4 -4
  103. package/dist/{chunk-2JNBPOZ2.js → chunk-GDYLZ6FR.js} +3 -3
  104. package/dist/{chunk-DZPTR2XQ.js → chunk-GFAEKSFR.js} +2 -2
  105. package/dist/{chunk-L46MG2GE.js → chunk-GQIYL7OD.js} +400 -301
  106. package/dist/chunk-GQIYL7OD.js.map +1 -0
  107. package/dist/{chunk-M7LA4QBY.js → chunk-GXVXBYRA.js} +3 -3
  108. package/dist/{chunk-UXAF4RQM.js → chunk-HKOMHP6D.js} +27 -15
  109. package/dist/chunk-HKOMHP6D.js.map +1 -0
  110. package/dist/{chunk-JGMJZBFV.js → chunk-HPNXS6W6.js} +2 -2
  111. package/dist/{chunk-BFPF5N3H.js → chunk-HXFIILGF.js} +4 -4
  112. package/dist/{chunk-YBKQAVPZ.js → chunk-IMKOJVD4.js} +101 -81
  113. package/dist/chunk-IMKOJVD4.js.map +1 -0
  114. package/dist/{chunk-CMNL3QSL.js → chunk-IN6U2BS5.js} +2 -2
  115. package/dist/{chunk-BWRX322G.js → chunk-IUYXCTA7.js} +2 -2
  116. package/dist/{chunk-LU57EEJW.js → chunk-JBWD3J5U.js} +3 -3
  117. package/dist/chunk-JNA7LFMQ.js +41 -0
  118. package/dist/chunk-JNA7LFMQ.js.map +1 -0
  119. package/dist/{chunk-2V664H6O.js → chunk-JS44PBCM.js} +5 -5
  120. package/dist/{chunk-XKTHJHSQ.js → chunk-JZCBMKR5.js} +3 -3
  121. package/dist/{chunk-MXPVNDGO.js → chunk-K5QQXVG6.js} +12 -12
  122. package/dist/{chunk-BUKK5SWA.js → chunk-KQAFEZQX.js} +2 -2
  123. package/dist/{chunk-WUBGMV3G.js → chunk-KRAMCBIW.js} +9 -9
  124. package/dist/{chunk-NDMPRT3Z.js → chunk-KW5YZELC.js} +2 -2
  125. package/dist/{chunk-2RQR4TXV.js → chunk-LIBVANOF.js} +2 -2
  126. package/dist/{chunk-M7L5A2AT.js → chunk-LMMBV7QC.js} +4 -4
  127. package/dist/{chunk-PJBYVJKO.js → chunk-LXXIV7Y2.js} +2 -2
  128. package/dist/{chunk-MZCTS3ZG.js → chunk-M2DOJBH7.js} +2 -2
  129. package/dist/{chunk-WOPZ2P5Z.js → chunk-M4HRYLOA.js} +2 -2
  130. package/dist/{chunk-JCYEDMOY.js → chunk-M6TP6UYD.js} +3 -3
  131. package/dist/{chunk-KAUCVTAW.js → chunk-MLNXATFG.js} +2 -2
  132. package/dist/{chunk-2NX7JTST.js → chunk-MPWOAIIH.js} +2 -2
  133. package/dist/{chunk-RQLTCHUL.js → chunk-MWURUSRE.js} +13 -13
  134. package/dist/{chunk-3EGL7XZO.js → chunk-N74WJ7B2.js} +2 -2
  135. package/dist/{chunk-DRXTMDAG.js → chunk-N7FNCE7F.js} +2 -2
  136. package/dist/{chunk-6CWAY4R7.js → chunk-NPLM7AFU.js} +2 -2
  137. package/dist/{chunk-VECRSYQG.js → chunk-NRGQ5HJO.js} +1 -1
  138. package/dist/{chunk-PPDNG7WC.js → chunk-NSP5PIQH.js} +2 -2
  139. package/dist/{chunk-6HVLHIAB.js → chunk-P7OVFGMU.js} +2 -2
  140. package/dist/{chunk-EX27VKU6.js → chunk-PDTNQLPC.js} +2 -2
  141. package/dist/{chunk-ME5LVISI.js → chunk-PJCVI44H.js} +2 -2
  142. package/dist/{chunk-LJCHDFLP.js → chunk-Q3PNQYAC.js} +2 -2
  143. package/dist/{chunk-YFJHFEET.js → chunk-QS765SBL.js} +2 -2
  144. package/dist/{chunk-FJ264QBP.js → chunk-QTDZ2NYC.js} +11 -11
  145. package/dist/{chunk-Q6UZREJK.js → chunk-RKRX4C4H.js} +2 -2
  146. package/dist/{chunk-YFXRLRKP.js → chunk-S4OZKPBS.js} +7 -11
  147. package/dist/{chunk-YFXRLRKP.js.map → chunk-S4OZKPBS.js.map} +1 -1
  148. package/dist/{chunk-GLZWOIU3.js → chunk-SJTSWG2Z.js} +3 -3
  149. package/dist/{chunk-Y57GJDG5.js → chunk-SWRRZNHM.js} +14 -17
  150. package/dist/chunk-SWRRZNHM.js.map +1 -0
  151. package/dist/{chunk-CCTLRRV6.js → chunk-TEH5MDX5.js} +55 -34
  152. package/dist/chunk-TEH5MDX5.js.map +1 -0
  153. package/dist/{chunk-XM747YJF.js → chunk-TFWJXWL6.js} +2 -2
  154. package/dist/{chunk-QFQ4UG3I.js → chunk-TXKQBQYW.js} +4 -4
  155. package/dist/{chunk-BMOJFGVX.js → chunk-UALXWWGN.js} +2 -2
  156. package/dist/{chunk-B44NFHWI.js → chunk-UNOAVHV4.js} +2 -2
  157. package/dist/{chunk-TF5NZGPS.js → chunk-UQBOPCDK.js} +2 -2
  158. package/dist/{chunk-SUKPM6DV.js → chunk-UWMZT2TY.js} +47 -4
  159. package/dist/chunk-UWMZT2TY.js.map +1 -0
  160. package/dist/{chunk-5R2IF2KI.js → chunk-UXGXJUZ5.js} +7 -7
  161. package/dist/{chunk-FWV2SXSL.js → chunk-UZVQFGAY.js} +101 -98
  162. package/dist/chunk-UZVQFGAY.js.map +1 -0
  163. package/dist/{chunk-J6B6FK2X.js → chunk-VCOMACR6.js} +53 -53
  164. package/dist/{chunk-WCGJK4NQ.js → chunk-VKQI47DJ.js} +7 -7
  165. package/dist/{chunk-MPJDQC4G.js → chunk-WJCXUE2J.js} +2 -2
  166. package/dist/{chunk-BCDECPVO.js → chunk-WV4UYDP6.js} +2 -2
  167. package/dist/{chunk-VWJ56T63.js → chunk-WXJC2L66.js} +2 -2
  168. package/dist/chunk-X2KO4FK5.js +40 -0
  169. package/dist/chunk-X2KO4FK5.js.map +1 -0
  170. package/dist/{chunk-UVYI6VIX.js → chunk-X7Y7WX73.js} +1 -1
  171. package/dist/{chunk-FPANDKMQ.js → chunk-XC3H7KON.js} +2 -2
  172. package/dist/{chunk-OOOX5C4X.js → chunk-XL2EE5W3.js} +4 -4
  173. package/dist/{chunk-3DKKZVXS.js → chunk-XTXWR6KQ.js} +26 -11
  174. package/dist/chunk-XTXWR6KQ.js.map +1 -0
  175. package/dist/{chunk-3SL52JKD.js → chunk-XVZA6VXC.js} +4 -4
  176. package/dist/{chunk-CZVNOTLR.js → chunk-XXR4A33N.js} +2 -2
  177. package/dist/{chunk-2AV2U2SZ.js → chunk-YDIOMDBW.js} +2 -2
  178. package/dist/{chunk-XTVH2NAW.js → chunk-YECEEJUU.js} +3 -3
  179. package/dist/{chunk-QR6W3FSW.js → chunk-Z5277JWF.js} +3 -3
  180. package/dist/{chunk-CTHYR4QK.js → chunk-ZFNXJEUG.js} +2 -2
  181. package/dist/{chunk-6Q56WDHG.js → chunk-ZLCBLDJO.js} +2 -2
  182. package/dist/{chunk-VY6BD5KB.js → chunk-ZMO2S5RT.js} +12 -10
  183. package/dist/chunk-ZMO2S5RT.js.map +1 -0
  184. package/dist/{chunk-KRACAE73.js → chunk-ZNRTHYYW.js} +6 -6
  185. package/dist/{chunk-7AIPWNM7.js → chunk-ZSMA67FV.js} +2 -2
  186. package/dist/cli.js +123 -120
  187. package/dist/coding/export-okf-codegraph.js +19 -18
  188. package/dist/coding/export-okf-codegraph.js.map +1 -1
  189. package/dist/coding/pre-action-gate.js +2 -2
  190. package/dist/compounding/engine.js +21 -20
  191. package/dist/config.js +32 -30
  192. package/dist/connectors/codex-materialize-runner.js +20 -19
  193. package/dist/connectors/index.js +20 -19
  194. package/dist/consolidation-provenance-check.js +4 -4
  195. package/dist/consolidation-undo.js +2 -2
  196. package/dist/convergence-refresh.js +22 -21
  197. package/dist/corpus-watermark.js +22 -21
  198. package/dist/correction/passive-correction-notifications.d.ts +1 -1
  199. package/dist/correction/passive-correction-notifications.d.ts.map +1 -1
  200. package/dist/dashboard-runtime.js +2 -2
  201. package/dist/deep-recall-http-glue.js +12 -11
  202. package/dist/deep-recall.js +2 -2
  203. package/dist/embedding-fallback.js +3 -2
  204. package/dist/embedding-generation-marker.d.ts +16 -0
  205. package/dist/embedding-generation-marker.d.ts.map +1 -0
  206. package/dist/embedding-generation-marker.js +19 -0
  207. package/dist/embedding-generation-marker.js.map +1 -0
  208. package/dist/embedding-index-storage.d.ts +2 -4
  209. package/dist/embedding-index-storage.d.ts.map +1 -1
  210. package/dist/embedding-index-storage.js +2 -1
  211. package/dist/{entity-canonical-id-migration-2YOLMOFC.js → entity-canonical-id-migration-ZBNRGUNV.js} +3 -3
  212. package/dist/entity-canonical-id-migration-ZBNRGUNV.js.map +1 -0
  213. package/dist/entity-retrieval.js +22 -21
  214. package/dist/event-order-recall.js +2 -2
  215. package/dist/explicit-capture.js +3 -3
  216. package/dist/external-wiki-access.js +74 -71
  217. package/dist/extraction-span-materialize.js +2 -2
  218. package/dist/extraction.js +30 -29
  219. package/dist/{graph-edge-decay-3HVLMT6U.js → graph-edge-decay-IU75NV42.js} +6 -6
  220. package/dist/graph-edge-decay-IU75NV42.js.map +1 -0
  221. package/dist/graph-jsonl.js +4 -4
  222. package/dist/graph-snapshot.js +4 -4
  223. package/dist/graph.js +5 -5
  224. package/dist/harmonic-retrieval.js +20 -19
  225. package/dist/index.d.ts +2 -1
  226. package/dist/index.d.ts.map +1 -1
  227. package/dist/index.js +230 -218
  228. package/dist/lcm/engine.js +3 -3
  229. package/dist/lcm/index.js +5 -5
  230. package/dist/local-llm.js +2 -2
  231. package/dist/location/index.js +25 -24
  232. package/dist/location/tagging.d.ts +7 -5
  233. package/dist/location/tagging.d.ts.map +1 -1
  234. package/dist/maintenance/memory-governance.js +20 -19
  235. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +20 -19
  236. package/dist/maintenance/rebuild-memory-projection-runner.js +22 -21
  237. package/dist/maintenance/rebuild-memory-projection.js +21 -20
  238. package/dist/memory-browse-operations.js +74 -71
  239. package/dist/memory-browse.js +5 -5
  240. package/dist/namespaces/migrate.js +21 -20
  241. package/dist/namespaces/search.js +6 -6
  242. package/dist/namespaces/storage.js +20 -19
  243. package/dist/offline-sync-changeset-generations.js +5 -4
  244. package/dist/offline-sync-embedding-generation.d.ts +12 -2
  245. package/dist/offline-sync-embedding-generation.d.ts.map +1 -1
  246. package/dist/offline-sync-embedding-generation.js +6 -3
  247. package/dist/offline-sync-file-io.d.ts +15 -0
  248. package/dist/offline-sync-file-io.d.ts.map +1 -1
  249. package/dist/offline-sync-file-io.js +9 -1
  250. package/dist/offline-sync-snapshot-stream.js +9 -8
  251. package/dist/offline-sync-upload-staging.js +2 -2
  252. package/dist/offline-sync.d.ts.map +1 -1
  253. package/dist/offline-sync.js +10 -9
  254. package/dist/operator-doctor-corpus.js +23 -22
  255. package/dist/operator-toolkit.js +45 -43
  256. package/dist/orchestration/maintenance.js +33 -32
  257. package/dist/orchestrator.d.ts +1 -0
  258. package/dist/orchestrator.d.ts.map +1 -1
  259. package/dist/orchestrator.js +170 -167
  260. package/dist/page-versioning.js +2 -2
  261. package/dist/qmd.js +2 -2
  262. package/dist/recall-navigation-operations.js +74 -71
  263. package/dist/recall-navigation.js +73 -70
  264. package/dist/recall-state-view-admission.js +5 -5
  265. package/dist/recall-why-service.js +5 -5
  266. package/dist/resume-bundles.js +33 -31
  267. package/dist/search/factory.js +5 -5
  268. package/dist/search/index.js +9 -9
  269. package/dist/semantic-consolidation.js +25 -24
  270. package/dist/semantic-rule-promotion.js +20 -19
  271. package/dist/semantic-rule-verifier.js +20 -19
  272. package/dist/source-agent-qualifier.js +24 -23
  273. package/dist/standup/build.js +20 -19
  274. package/dist/storage/entity-canonical-id-migration-adapter.d.ts +1 -0
  275. package/dist/storage/entity-canonical-id-migration-adapter.d.ts.map +1 -1
  276. package/dist/storage/entity-canonical-id-migration.d.ts +7 -0
  277. package/dist/storage/entity-canonical-id-migration.d.ts.map +1 -1
  278. package/dist/storage/entity-canonical-id-references.d.ts +2 -0
  279. package/dist/storage/entity-canonical-id-references.d.ts.map +1 -1
  280. package/dist/storage/entity-migration-completion-cache.d.ts +46 -0
  281. package/dist/storage/entity-migration-completion-cache.d.ts.map +1 -0
  282. package/dist/storage/entity-store.d.ts +1 -8
  283. package/dist/storage/entity-store.d.ts.map +1 -1
  284. package/dist/storage/memory-read-store.d.ts +3 -2
  285. package/dist/storage/memory-read-store.d.ts.map +1 -1
  286. package/dist/storage/windowed-memory-read.d.ts +6 -0
  287. package/dist/storage/windowed-memory-read.d.ts.map +1 -1
  288. package/dist/storage.d.ts +2 -0
  289. package/dist/storage.d.ts.map +1 -1
  290. package/dist/storage.js +19 -18
  291. package/dist/summarizer.js +3 -3
  292. package/dist/support-passport/index.js +76 -73
  293. package/dist/transfer/backup.js +2 -2
  294. package/dist/transfer/capsule-export.js +2 -2
  295. package/dist/transfer/capsule-import.js +4 -4
  296. package/dist/transfer/export-okf.js +20 -19
  297. package/dist/verified-recall.js +20 -19
  298. package/dist/who-knows-cli.js +21 -20
  299. package/dist/who-knows.js +20 -19
  300. package/package.json +2 -2
  301. package/src/access-http-briefing.ts +4 -0
  302. package/src/access-http.test.ts +40 -11
  303. package/src/access-http.ts +6 -0
  304. package/src/access-observe-write-category.ts +1 -1
  305. package/src/access-observe-write-surface.ts +24 -19
  306. package/src/access-operations-batch.ts +18 -2
  307. package/src/access-service-peer-capture-backoff.test.ts +236 -0
  308. package/src/access-service.ts +11 -2
  309. package/src/briefing-abort.test.ts +173 -0
  310. package/src/briefing-deadline.test.ts +84 -0
  311. package/src/briefing-deadline.ts +33 -0
  312. package/src/briefing-window-abort.test.ts +47 -0
  313. package/src/briefing-window.ts +33 -4
  314. package/src/briefing.ts +23 -16
  315. package/src/capture-write-lock-scope.test.ts +32 -0
  316. package/src/capture-write-lock-scope.ts +30 -0
  317. package/src/correction/passive-correction-notifications-abort.test.ts +42 -0
  318. package/src/correction/passive-correction-notifications.ts +4 -0
  319. package/src/embedding-generation-marker.ts +51 -0
  320. package/src/embedding-index-storage.ts +13 -29
  321. package/src/entity-lazy-compatibility.test.ts +229 -0
  322. package/src/entity-migration-completion-notification.test.ts +148 -0
  323. package/src/index.ts +2 -2
  324. package/src/location/tagging.ts +14 -12
  325. package/src/offline-sync-embedding-delete-digest.test.ts +58 -0
  326. package/src/offline-sync-embedding-generation-marker.test.ts +245 -0
  327. package/src/offline-sync-embedding-generation.ts +47 -2
  328. package/src/offline-sync-embedding-lock-loss.test.ts +42 -0
  329. package/src/offline-sync-file-io.ts +61 -2
  330. package/src/offline-sync.test.ts +313 -20
  331. package/src/offline-sync.ts +47 -22
  332. package/src/orchestrator-init.test.ts +49 -1
  333. package/src/orchestrator.ts +9 -8
  334. package/src/storage/entity-canonical-id-migration-adapter.ts +16 -1
  335. package/src/storage/entity-canonical-id-migration.ts +17 -8
  336. package/src/storage/entity-canonical-id-references.ts +24 -0
  337. package/src/storage/entity-migration-completion-cache.nofollow.test.ts +223 -0
  338. package/src/storage/entity-migration-completion-cache.ts +153 -0
  339. package/src/storage/entity-store.ts +58 -86
  340. package/src/storage/memory-read-store.ts +9 -3
  341. package/src/storage/windowed-memory-read.ts +8 -0
  342. package/src/storage-startup-nonblocking-migration.test.ts +183 -0
  343. package/src/storage.ts +9 -3
  344. package/src/testing/subjects/entity-migration-completion-cache.test.ts +90 -0
  345. package/dist/chunk-3DKKZVXS.js.map +0 -1
  346. package/dist/chunk-CCTLRRV6.js.map +0 -1
  347. package/dist/chunk-CHSRA76M.js.map +0 -1
  348. package/dist/chunk-FRXS6SC7.js.map +0 -1
  349. package/dist/chunk-FWV2SXSL.js.map +0 -1
  350. package/dist/chunk-L46MG2GE.js.map +0 -1
  351. package/dist/chunk-LWRIGKFM.js.map +0 -1
  352. package/dist/chunk-OXSTKCYK.js.map +0 -1
  353. package/dist/chunk-PKPOO6X5.js.map +0 -1
  354. package/dist/chunk-SUKPM6DV.js.map +0 -1
  355. package/dist/chunk-UXAF4RQM.js.map +0 -1
  356. package/dist/chunk-VY6BD5KB.js.map +0 -1
  357. package/dist/chunk-Y57GJDG5.js.map +0 -1
  358. package/dist/chunk-YBKQAVPZ.js.map +0 -1
  359. package/dist/chunk-YUE7J2HZ.js.map +0 -1
  360. /package/dist/{auto-sync-BNCBMU6H.js.map → auto-sync-UQQ6YJSB.js.map} +0 -0
  361. /package/dist/{capsule-crypto-GWVG7LGC.js.map → briefing-deadline.js.map} +0 -0
  362. /package/dist/{entity-canonical-id-migration-2YOLMOFC.js.map → capsule-crypto-7FJQINUR.js.map} +0 -0
  363. /package/dist/{capsule-merge-RNQUP2VL.js.map → capsule-merge-D4BTIJGK.js.map} +0 -0
  364. /package/dist/{graph-edge-decay-3HVLMT6U.js.map → capture-write-lock-scope.js.map} +0 -0
  365. /package/dist/{chunk-FAKRTS3Y.js.map → chunk-2AKIVSHP.js.map} +0 -0
  366. /package/dist/{chunk-EIYJK7OD.js.map → chunk-2M36PR7R.js.map} +0 -0
  367. /package/dist/{chunk-OWHERGF2.js.map → chunk-2NLLXCJG.js.map} +0 -0
  368. /package/dist/{chunk-6P6MZDBL.js.map → chunk-3NL4ALUP.js.map} +0 -0
  369. /package/dist/{chunk-6CC2YCKG.js.map → chunk-43TUWQNR.js.map} +0 -0
  370. /package/dist/{chunk-H7BZBSP6.js.map → chunk-52II7PB6.js.map} +0 -0
  371. /package/dist/{chunk-2NLCDNGA.js.map → chunk-564YP3DW.js.map} +0 -0
  372. /package/dist/{chunk-5KGYR5VN.js.map → chunk-5DVOBBTV.js.map} +0 -0
  373. /package/dist/{chunk-RT2GN6KQ.js.map → chunk-5FUDSK6Z.js.map} +0 -0
  374. /package/dist/{chunk-B4XSXFKI.js.map → chunk-5G53JSRO.js.map} +0 -0
  375. /package/dist/{chunk-BGK3SFCC.js.map → chunk-5X2H3X4J.js.map} +0 -0
  376. /package/dist/{chunk-4J7BNL7B.js.map → chunk-6ZQX3R66.js.map} +0 -0
  377. /package/dist/{chunk-JNLYHJG7.js.map → chunk-74AY3S7U.js.map} +0 -0
  378. /package/dist/{chunk-RUAIO5UA.js.map → chunk-7J6A5MV2.js.map} +0 -0
  379. /package/dist/{chunk-A45QXZMU.js.map → chunk-7SAE6T36.js.map} +0 -0
  380. /package/dist/{chunk-QE2XQM3S.js.map → chunk-7YVER7ZO.js.map} +0 -0
  381. /package/dist/{chunk-IJH6METJ.js.map → chunk-A3UFV4K3.js.map} +0 -0
  382. /package/dist/{chunk-LXN737FX.js.map → chunk-AAANTXCQ.js.map} +0 -0
  383. /package/dist/{chunk-I3IPXGFQ.js.map → chunk-AJJPXUE6.js.map} +0 -0
  384. /package/dist/{chunk-2DPJFJFH.js.map → chunk-BNJAVRNJ.js.map} +0 -0
  385. /package/dist/{chunk-YL564QS2.js.map → chunk-CCL2OXFO.js.map} +0 -0
  386. /package/dist/{chunk-ZX5N5D6M.js.map → chunk-DAOMWGO2.js.map} +0 -0
  387. /package/dist/{chunk-23ZMQONL.js.map → chunk-ERMT5WQM.js.map} +0 -0
  388. /package/dist/{chunk-VTCNVGM6.js.map → chunk-EWBXMGXR.js.map} +0 -0
  389. /package/dist/{chunk-YNDLCWXS.js.map → chunk-EZ25VE3G.js.map} +0 -0
  390. /package/dist/{chunk-XTN54NML.js.map → chunk-G3T6YMBO.js.map} +0 -0
  391. /package/dist/{chunk-2JNBPOZ2.js.map → chunk-GDYLZ6FR.js.map} +0 -0
  392. /package/dist/{chunk-DZPTR2XQ.js.map → chunk-GFAEKSFR.js.map} +0 -0
  393. /package/dist/{chunk-M7LA4QBY.js.map → chunk-GXVXBYRA.js.map} +0 -0
  394. /package/dist/{chunk-JGMJZBFV.js.map → chunk-HPNXS6W6.js.map} +0 -0
  395. /package/dist/{chunk-BFPF5N3H.js.map → chunk-HXFIILGF.js.map} +0 -0
  396. /package/dist/{chunk-CMNL3QSL.js.map → chunk-IN6U2BS5.js.map} +0 -0
  397. /package/dist/{chunk-BWRX322G.js.map → chunk-IUYXCTA7.js.map} +0 -0
  398. /package/dist/{chunk-LU57EEJW.js.map → chunk-JBWD3J5U.js.map} +0 -0
  399. /package/dist/{chunk-2V664H6O.js.map → chunk-JS44PBCM.js.map} +0 -0
  400. /package/dist/{chunk-XKTHJHSQ.js.map → chunk-JZCBMKR5.js.map} +0 -0
  401. /package/dist/{chunk-MXPVNDGO.js.map → chunk-K5QQXVG6.js.map} +0 -0
  402. /package/dist/{chunk-BUKK5SWA.js.map → chunk-KQAFEZQX.js.map} +0 -0
  403. /package/dist/{chunk-WUBGMV3G.js.map → chunk-KRAMCBIW.js.map} +0 -0
  404. /package/dist/{chunk-NDMPRT3Z.js.map → chunk-KW5YZELC.js.map} +0 -0
  405. /package/dist/{chunk-2RQR4TXV.js.map → chunk-LIBVANOF.js.map} +0 -0
  406. /package/dist/{chunk-M7L5A2AT.js.map → chunk-LMMBV7QC.js.map} +0 -0
  407. /package/dist/{chunk-PJBYVJKO.js.map → chunk-LXXIV7Y2.js.map} +0 -0
  408. /package/dist/{chunk-MZCTS3ZG.js.map → chunk-M2DOJBH7.js.map} +0 -0
  409. /package/dist/{chunk-WOPZ2P5Z.js.map → chunk-M4HRYLOA.js.map} +0 -0
  410. /package/dist/{chunk-JCYEDMOY.js.map → chunk-M6TP6UYD.js.map} +0 -0
  411. /package/dist/{chunk-KAUCVTAW.js.map → chunk-MLNXATFG.js.map} +0 -0
  412. /package/dist/{chunk-2NX7JTST.js.map → chunk-MPWOAIIH.js.map} +0 -0
  413. /package/dist/{chunk-RQLTCHUL.js.map → chunk-MWURUSRE.js.map} +0 -0
  414. /package/dist/{chunk-3EGL7XZO.js.map → chunk-N74WJ7B2.js.map} +0 -0
  415. /package/dist/{chunk-DRXTMDAG.js.map → chunk-N7FNCE7F.js.map} +0 -0
  416. /package/dist/{chunk-6CWAY4R7.js.map → chunk-NPLM7AFU.js.map} +0 -0
  417. /package/dist/{chunk-VECRSYQG.js.map → chunk-NRGQ5HJO.js.map} +0 -0
  418. /package/dist/{chunk-PPDNG7WC.js.map → chunk-NSP5PIQH.js.map} +0 -0
  419. /package/dist/{chunk-6HVLHIAB.js.map → chunk-P7OVFGMU.js.map} +0 -0
  420. /package/dist/{chunk-EX27VKU6.js.map → chunk-PDTNQLPC.js.map} +0 -0
  421. /package/dist/{chunk-ME5LVISI.js.map → chunk-PJCVI44H.js.map} +0 -0
  422. /package/dist/{chunk-LJCHDFLP.js.map → chunk-Q3PNQYAC.js.map} +0 -0
  423. /package/dist/{chunk-YFJHFEET.js.map → chunk-QS765SBL.js.map} +0 -0
  424. /package/dist/{chunk-FJ264QBP.js.map → chunk-QTDZ2NYC.js.map} +0 -0
  425. /package/dist/{chunk-Q6UZREJK.js.map → chunk-RKRX4C4H.js.map} +0 -0
  426. /package/dist/{chunk-GLZWOIU3.js.map → chunk-SJTSWG2Z.js.map} +0 -0
  427. /package/dist/{chunk-XM747YJF.js.map → chunk-TFWJXWL6.js.map} +0 -0
  428. /package/dist/{chunk-QFQ4UG3I.js.map → chunk-TXKQBQYW.js.map} +0 -0
  429. /package/dist/{chunk-BMOJFGVX.js.map → chunk-UALXWWGN.js.map} +0 -0
  430. /package/dist/{chunk-B44NFHWI.js.map → chunk-UNOAVHV4.js.map} +0 -0
  431. /package/dist/{chunk-TF5NZGPS.js.map → chunk-UQBOPCDK.js.map} +0 -0
  432. /package/dist/{chunk-5R2IF2KI.js.map → chunk-UXGXJUZ5.js.map} +0 -0
  433. /package/dist/{chunk-J6B6FK2X.js.map → chunk-VCOMACR6.js.map} +0 -0
  434. /package/dist/{chunk-WCGJK4NQ.js.map → chunk-VKQI47DJ.js.map} +0 -0
  435. /package/dist/{chunk-MPJDQC4G.js.map → chunk-WJCXUE2J.js.map} +0 -0
  436. /package/dist/{chunk-BCDECPVO.js.map → chunk-WV4UYDP6.js.map} +0 -0
  437. /package/dist/{chunk-VWJ56T63.js.map → chunk-WXJC2L66.js.map} +0 -0
  438. /package/dist/{chunk-UVYI6VIX.js.map → chunk-X7Y7WX73.js.map} +0 -0
  439. /package/dist/{chunk-FPANDKMQ.js.map → chunk-XC3H7KON.js.map} +0 -0
  440. /package/dist/{chunk-OOOX5C4X.js.map → chunk-XL2EE5W3.js.map} +0 -0
  441. /package/dist/{chunk-3SL52JKD.js.map → chunk-XVZA6VXC.js.map} +0 -0
  442. /package/dist/{chunk-CZVNOTLR.js.map → chunk-XXR4A33N.js.map} +0 -0
  443. /package/dist/{chunk-2AV2U2SZ.js.map → chunk-YDIOMDBW.js.map} +0 -0
  444. /package/dist/{chunk-XTVH2NAW.js.map → chunk-YECEEJUU.js.map} +0 -0
  445. /package/dist/{chunk-QR6W3FSW.js.map → chunk-Z5277JWF.js.map} +0 -0
  446. /package/dist/{chunk-CTHYR4QK.js.map → chunk-ZFNXJEUG.js.map} +0 -0
  447. /package/dist/{chunk-6Q56WDHG.js.map → chunk-ZLCBLDJO.js.map} +0 -0
  448. /package/dist/{chunk-KRACAE73.js.map → chunk-ZNRTHYYW.js.map} +0 -0
  449. /package/dist/{chunk-7AIPWNM7.js.map → chunk-ZSMA67FV.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/offline-sync-file-io.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { createReadStream } from \"node:fs\";\nimport { open } from \"node:fs/promises\";\nimport { throwIfAborted } from \"./abort-error.js\";\nimport { MAGIC_HEADER_SIZE, isEncryptedFile } from \"./secure-store/secure-fs.js\";\n\nexport interface OfflineSyncFileTarget {\n root: string;\n path: string;\n filePath: string;\n}\n\nexport type OfflineSyncExcludeFile = (target: OfflineSyncFileTarget) => boolean | Promise<boolean>;\nexport async function shouldExcludeOfflineSyncFile(\n excludeFile: OfflineSyncExcludeFile | undefined,\n target: OfflineSyncFileTarget\n): Promise<boolean> {\n return excludeFile ? await excludeFile(target) : false;\n}\n\nexport async function sha256OfflineSyncFile(\n filePath: string,\n signal?: AbortSignal\n): Promise<{ sha256: string; bytes: number }> {\n const hash = createHash(\"sha256\");\n let bytes = 0;\n for await (const chunk of createReadStream(filePath)) {\n throwIfAborted(signal, \"offline sync request aborted\");\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n hash.update(buffer);\n bytes += buffer.length;\n }\n throwIfAborted(signal, \"offline sync request aborted\");\n return { sha256: hash.digest(\"hex\"), bytes };\n}\n\nexport async function isEncryptedOfflineSyncFile(filePath: string): Promise<boolean> {\n const handle = await open(filePath, \"r\");\n try {\n const header = Buffer.alloc(MAGIC_HEADER_SIZE);\n const { bytesRead } = await handle.read(header, 0, header.length, 0);\n return bytesRead >= MAGIC_HEADER_SIZE && isEncryptedFile(header);\n } finally {\n await handle.close();\n }\n}\n\nexport async function readPlainOfflineSyncFileChunk(options: {\n filePath: string;\n offset: number;\n length: number;\n bytes: number;\n}): Promise<Buffer> {\n const chunkBytes = Math.min(options.length, options.bytes - options.offset);\n const chunk = Buffer.alloc(chunkBytes);\n if (chunkBytes === 0) return chunk;\n const handle = await open(options.filePath, \"r\");\n try {\n const { bytesRead } = await handle.read(chunk, 0, chunk.length, options.offset);\n return bytesRead === chunk.length ? chunk : chunk.subarray(0, bytesRead);\n } finally {\n await handle.close();\n }\n}\n\n/** Streamed whole-file sha256 for plain-file chunk reads — the value the\n * file-content response contract exposes as x-remnic-file-sha256. Deliberately\n * UNCACHED: identity keys like (path, size, mtimeMs) are spoofable (a rewrite\n * that preserves size and mtime would serve a stale digest, defeating the\n * uploader's verify-before-idempotent-skip check). One bounded-memory hash\n * pass per chunk request; content reads stay windowed. */\nexport async function plainFileDigest(filePath: string): Promise<string> {\n const hash = createHash(\"sha256\");\n for await (const piece of createReadStream(filePath)) {\n hash.update(piece as Buffer);\n }\n return hash.digest(\"hex\");\n}\n\nimport { lstat, utimes } from \"node:fs/promises\";\nimport { isCensusMtimeMs } from \"./census-validation.js\";\nimport { resolveSafeArchiveTarget, type SafeArchiveRoot } from \"./transfer/fs-utils.js\";\n\n/** Sync-internal directory spooling partial uploads and coordination state. */\nexport const SYNC_INTERNAL_DIR = \".offline-sync\";\n\nexport const OFFLINE_SYNC_FAST_BASE_MTIME_TOLERANCE_MS = 1_000;\n\nexport function assertNonNegativeFinite(value: unknown, field: string): number {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value < 0) {\n throw new Error(`${field} must be a non-negative finite number`);\n }\n return value;\n}\n\nexport function assertOfflineSyncMtimeMs(value: unknown, field: string): number {\n const mtimeMs = assertNonNegativeFinite(value, field);\n if (!isCensusMtimeMs(mtimeMs)) {\n throw new Error(`${field} must be within JavaScript Date range`);\n }\n return mtimeMs;\n}\n\nexport async function setSafeFileMtime(\n root: SafeArchiveRoot,\n relPath: string,\n mtimeMs: number | undefined,\n): Promise<boolean> {\n if (mtimeMs === undefined) return true;\n const target = await resolveSafeArchiveTarget(root, relPath);\n const targetStat = await lstat(target).catch((error: unknown) => {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw error;\n });\n if (!targetStat) return false;\n if (targetStat.isSymbolicLink()) {\n throw new Error(`offline sync target is a symlink: ${relPath}`);\n }\n const targetMtimeMs = assertOfflineSyncMtimeMs(mtimeMs, \"mtimeMs\");\n if (Math.abs(targetStat.mtimeMs - targetMtimeMs) <= OFFLINE_SYNC_FAST_BASE_MTIME_TOLERANCE_MS) {\n return true;\n }\n const mtime = new Date(targetMtimeMs);\n await utimes(target, mtime, mtime);\n return true;\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/briefing.ts","../src/correction/passive-correction-notifications.ts"],"sourcesContent":["/**\n * Daily Context Briefing (Issue #370)\n *\n * Produces a focused \"here is what matters right now\" briefing by\n * cross-referencing active entities, recent facts, open commitments,\n * LLM-generated follow-ups, and an optional calendar source.\n *\n * The module exposes:\n * - `parseBriefingWindow(token)` — CLI-friendly window parser.\n * - `buildBriefing(options)` — core builder that returns markdown + JSON.\n * - `FileCalendarSource` — stub CalendarSource implementation that reads\n * a local ICS or JSON file.\n *\n * ALL OpenAI usage in this module goes through the Responses API. Chat\n * Completions is never used.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { log } from \"./logger.js\";\nimport { withBriefingFollowupTimeout } from \"./briefing-followup-timeout.js\";\nimport { extractJsonCandidates } from \"./json-extract.js\";\nimport { drainPassiveCorrectionNotifications } from \"./correction/passive-correction-notifications.js\";\nimport { excludeSupportPassportPrivateMemories } from \"./support-passport/card-projection.js\";\nimport { normalizeEntityName, StorageManager } from \"./storage.js\";\nimport { safeReadMemories } from \"./briefing-window.js\";\nimport { readEnvVar, resolveHomeDir } from \"./runtime/env.js\";\nimport type {\n BriefingActiveThread,\n BriefingCalendarSourceError,\n BriefingFocus,\n BriefingFollowup,\n BriefingOpenCommitment,\n BriefingRecentEntity,\n BriefingResult,\n BriefingSections,\n CalendarEvent,\n CalendarSource,\n EntityFile,\n MemoryFile,\n} from \"./types.js\";\n\n// ──────────────────────────────────────────────────────────────────────────\n// Window parsing\n// ──────────────────────────────────────────────────────────────────────────\n\n/** Allowed values for the briefing format flag/field. */\nexport const BRIEFING_FORMAT_ALLOWED = [\"markdown\", \"json\"] as const;\n\n/**\n * Default model used for the Responses API follow-up generation call.\n * Mirrors the extraction engine default in config.ts — keep in sync.\n */\nexport const BRIEFING_FOLLOWUP_DEFAULT_MODEL = \"gpt-5.5\";\nexport type BriefingFormatValue = typeof BRIEFING_FORMAT_ALLOWED[number];\n\n/**\n * Validate a user-supplied `--format` flag value.\n * Returns `null` when the value is valid (or `undefined`, meaning the flag\n * was not supplied and the caller should fall back to the configured default).\n * Returns an error message string when the value is explicitly invalid.\n */\nexport function validateBriefingFormat(value: string | undefined): string | null {\n if (value === undefined) return null;\n if ((BRIEFING_FORMAT_ALLOWED as readonly string[]).includes(value)) return null;\n return `Invalid --format value: \"${value}\". Accepted: ${BRIEFING_FORMAT_ALLOWED.join(\", \")}.`;\n}\n\n/** Parsed briefing lookback window. */\nexport interface ParsedBriefingWindow {\n /** Start of the window (inclusive). */\n from: Date;\n /** End of the window (exclusive). */\n to: Date;\n /** Human-readable label. */\n label: string;\n}\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\n\n/**\n * Maximum allowed lookback offset in milliseconds (100 years).\n * Anything beyond this is almost certainly a typo or abuse — and would\n * overflow to `Invalid Date` for sufficiently large values anyway.\n */\nconst MAX_WINDOW_MS = 100 * 365 * DAY_MS;\n\n/**\n * Parse a CLI-friendly window token into a concrete date range.\n *\n * Accepted forms (case-insensitive):\n * - `yesterday` — the previous UTC calendar day.\n * - `today` — the current UTC calendar day so far.\n * - `NNh` — last N hours (e.g. `24h`, `48h`).\n * - `NNd` — last N calendar days (e.g. `3d`, `7d`).\n * - `NNw` — last N weeks (e.g. `1w`, `2w`).\n *\n * Returns `null` for invalid tokens so callers can surface a clean error.\n */\nexport function parseBriefingWindow(\n token: string,\n now: Date = new Date(),\n): ParsedBriefingWindow | null {\n const raw = typeof token === \"string\" ? token.trim().toLowerCase() : \"\";\n if (raw.length === 0) return null;\n\n if (raw === \"yesterday\") {\n const startOfToday = startOfUtcDay(now);\n const from = new Date(startOfToday.getTime() - DAY_MS);\n return { from, to: startOfToday, label: \"yesterday\" };\n }\n\n if (raw === \"today\") {\n return { from: startOfUtcDay(now), to: now, label: \"today\" };\n }\n\n const match = raw.match(/^(\\d+)\\s*(h|d|w)$/);\n if (!match) return null;\n const value = parseInt(match[1], 10);\n if (!Number.isFinite(value) || value <= 0) return null;\n const unit = match[2];\n let ms = 0;\n if (unit === \"h\") ms = value * 60 * 60 * 1000;\n else if (unit === \"d\") ms = value * DAY_MS;\n else if (unit === \"w\") ms = value * 7 * DAY_MS;\n if (ms === 0) return null;\n // Reject values that exceed the 100-year cap or would overflow to Invalid Date.\n if (ms > MAX_WINDOW_MS || !Number.isFinite(ms)) return null;\n const from = new Date(now.getTime() - ms);\n if (!Number.isFinite(from.getTime())) return null;\n return {\n from,\n to: now,\n label: `last ${value}${unit}`,\n };\n}\n\nfunction startOfUtcDay(date: Date): Date {\n const d = new Date(date.getTime());\n d.setUTCHours(0, 0, 0, 0);\n return d;\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Focus filter\n// ──────────────────────────────────────────────────────────────────────────\n\n/**\n * Parse a CLI `--focus` string into a structured focus filter.\n *\n * Accepted forms:\n * - `person:Jane Doe`\n * - `project:remnic-core`\n * - `topic:retrieval`\n *\n * If no prefix is supplied, falls back to `topic:<value>`.\n */\nexport function parseBriefingFocus(token: string | undefined): BriefingFocus | null {\n if (typeof token !== \"string\") return null;\n const trimmed = token.trim();\n if (trimmed.length === 0) return null;\n const [maybeType, ...rest] = trimmed.split(\":\");\n if (rest.length === 0) {\n return { type: \"topic\", value: maybeType };\n }\n const rawType = maybeType.toLowerCase();\n if (rawType === \"person\" || rawType === \"project\" || rawType === \"topic\") {\n const value = rest.join(\":\").trim();\n if (value.length === 0) return null;\n return { type: rawType, value };\n }\n return { type: \"topic\", value: trimmed };\n}\n\n/**\n * Decide whether a memory/entity matches the given focus filter.\n * Purely deterministic — no LLM, case-insensitive substring match across\n * the most useful surfaces.\n *\n * For typed focus (`person:`, `project:`, `entity:`) we attribute via the\n * canonical-id path below to avoid the substring leak fixed in #682 PR 2/3\n * R-10 (`Alice-Test` substring-hitting `person-alice-test-a1`).\n *\n * For untyped (`topic`) focus there is no type to canonicalize against, so\n * we keep `entityRef` in the raw haystack — otherwise memories that link to\n * an entity only via `frontmatter.entityRef` (no body / tag mention) would\n * silently drop out of an untyped focus filter (codex P2 review on #695).\n */\nexport function focusMatchesMemory(\n memory: MemoryFile,\n focus: BriefingFocus,\n entityAliases?: Readonly<Record<string, string>>,\n): boolean {\n const needle = focus.value.toLowerCase();\n const entityRef = (memory.frontmatter.entityRef ?? \"\").toLowerCase();\n\n // Raw substring match across content and tags. For untyped (`topic`)\n // focus, also include the entityRef so memories linked only via the\n // frontmatter ref still match. For typed focus we deliberately\n // exclude entityRef here and route through the canonical-id path\n // below (see comment above).\n const rawHaystackParts = [\n memory.content,\n ...(memory.frontmatter.tags ?? []),\n ];\n if (focus.type === \"topic\" && entityRef) {\n rawHaystackParts.push(entityRef);\n }\n const rawHaystack = rawHaystackParts.join(\" \").toLowerCase();\n if (rawHaystack.includes(needle)) return true;\n\n // Canonical-id match (#682 PR 2/3 R-10). Normalize BOTH sides through\n // `normalizeEntityName` so:\n // - `entityRef === slug` exact comparison no longer fails when the\n // stored ref is in a non-canonical form (e.g. `person:Alice-Test`\n // vs. `person-alice-test`) — both normalize to the same canonical id.\n // - `entityRef.includes(slug)` substring matching is replaced by\n // equality, so similarly-prefixed entities like\n // `person-alice-test-a1` no longer match a focus on\n // `person:Alice-Test`.\n // This also handles the type-prefix-in-name case the cursor reviewer\n // flagged: `normalizeEntityName` strips a duplicate type prefix so\n // `Project-Alpha` of type `project` canonicalizes to `project-alpha`,\n // and a focus on `project:Project-Alpha` does the same.\n if (!entityRef) return false;\n const focusCanonical = normalizeEntityName(focus.value, focus.type, entityAliases);\n // Strip a leading `<type><delimiter>` prefix from a non-canonical\n // entityRef before normalizing — `normalizeEntityName` only strips\n // `<type>-` so other valid verbatim formats (`person:Alice-Test`,\n // `person/alice-test`, `person_alice_test`, `person Alice Test`)\n // would otherwise double up the type prefix and miss the canonical\n // comparison. Codex P2 review on #695: memory writes persist\n // entityRef strings as provided, so any non-alphanumeric delimiter\n // after the type token must be tolerated here.\n let refForNormalize = entityRef;\n const typeDelimMatch = refForNormalize.match(\n new RegExp(`^${focus.type}[^a-z0-9]+`, \"i\"),\n );\n if (typeDelimMatch) {\n refForNormalize = refForNormalize.slice(typeDelimMatch[0].length);\n }\n const refCanonical = normalizeEntityName(refForNormalize, focus.type, entityAliases);\n return refCanonical === focusCanonical;\n}\n\nexport function focusMatchesEntity(entity: EntityFile, focus: BriefingFocus): boolean {\n const needle = focus.value.toLowerCase();\n if (focus.type === \"person\" && entity.type.toLowerCase() !== \"person\") return false;\n if (focus.type === \"project\" && entity.type.toLowerCase() !== \"project\") return false;\n const haystack = [\n entity.name,\n entity.synthesis || entity.summary || \"\",\n ...entity.facts,\n ...(entity.aliases ?? []),\n ...(entity.structuredSections ?? []).flatMap((section) => [section.title, ...section.facts]),\n ]\n .join(\" \")\n .toLowerCase();\n return haystack.includes(needle);\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Calendar source\n// ──────────────────────────────────────────────────────────────────────────\n\n/**\n * Stub `CalendarSource` backed by a single local file. Supports:\n * - JSON files containing an array of `CalendarEvent` records, OR a wrapper\n * `{ events: CalendarEvent[] }` object.\n * - Minimal ICS (`.ics`) files — extracts `VEVENT` blocks with `SUMMARY`,\n * `DTSTART`, `DTEND`, `LOCATION`, `DESCRIPTION`, `UID`.\n *\n * Real calendar integrations (Google, iCloud, Microsoft) can plug into the\n * same `CalendarSource` interface later.\n */\nexport class FileCalendarSource implements CalendarSource {\n constructor(private readonly filePath: string) {}\n\n async eventsForDate(dateIso: string): Promise<CalendarEvent[]> {\n let raw: string;\n try {\n raw = await readFile(this.filePath, \"utf-8\");\n } catch (err) {\n log.warn(`briefing: calendar source unreadable at ${this.filePath}: ${err}`);\n return [];\n }\n\n const events = this.filePath.toLowerCase().endsWith(\".ics\")\n ? parseIcsEvents(raw)\n : parseJsonEvents(raw);\n\n return events.filter((event) => eventFallsOnDate(event, dateIso));\n }\n}\n\nfunction parseJsonEvents(raw: string): CalendarEvent[] {\n try {\n const parsed = JSON.parse(raw) as unknown;\n const arr = Array.isArray(parsed)\n ? parsed\n : parsed && typeof parsed === \"object\" && Array.isArray((parsed as { events?: unknown }).events)\n ? ((parsed as { events: unknown[] }).events)\n : [];\n const events: CalendarEvent[] = [];\n for (const entry of arr) {\n if (!entry || typeof entry !== \"object\") continue;\n const e = entry as Record<string, unknown>;\n const id = typeof e.id === \"string\" ? e.id : typeof e.uid === \"string\" ? e.uid : cryptoRandomId();\n const title = typeof e.title === \"string\" ? e.title : typeof e.summary === \"string\" ? e.summary : \"\";\n const start = typeof e.start === \"string\" ? e.start : typeof e.dtstart === \"string\" ? e.dtstart : \"\";\n if (!title || !start) continue;\n events.push({\n id,\n title,\n start,\n end: typeof e.end === \"string\" ? e.end : typeof e.dtend === \"string\" ? e.dtend : undefined,\n location: typeof e.location === \"string\" ? e.location : undefined,\n notes: typeof e.notes === \"string\" ? e.notes : typeof e.description === \"string\" ? e.description : undefined,\n });\n }\n return events;\n } catch (err) {\n log.warn(`briefing: calendar JSON parse failed: ${err}`);\n return [];\n }\n}\n\ninterface IcsParsedLine {\n property: string;\n params: Record<string, string>;\n value: string;\n}\n\n/**\n * Parse a single ICS content line into its property, parameters, and value.\n * Returns null if the line is not a well-formed property line.\n *\n * Example:\n * `DTSTART;TZID=America/New_York:20260411T233000`\n * → { property: \"DTSTART\", params: { TZID: \"America/New_York\" }, value: \"20260411T233000\" }\n */\nfunction parseIcsLine(line: string): IcsParsedLine | null {\n // Find the first `:` outside of any parameter value. Per RFC 5545 parameter\n // values may be quoted, but the minimal-parser use case here only needs to\n // handle unquoted TZID values.\n const colonIdx = line.indexOf(\":\");\n if (colonIdx <= 0) return null;\n const head = line.slice(0, colonIdx);\n const value = line.slice(colonIdx + 1).trim();\n // Head is of the form `PROPERTY[;PARAM=val[;PARAM=val]]`.\n const headParts = head.split(\";\");\n const property = headParts[0]!.toUpperCase();\n if (!/^[A-Z0-9-]+$/.test(property)) return null;\n const params: Record<string, string> = {};\n for (let i = 1; i < headParts.length; i++) {\n const segment = headParts[i]!;\n const eqIdx = segment.indexOf(\"=\");\n if (eqIdx <= 0) continue;\n const name = segment.slice(0, eqIdx).toUpperCase();\n let val = segment.slice(eqIdx + 1);\n // Strip surrounding quotes if present (RFC 5545 §3.1).\n if (val.startsWith('\"') && val.endsWith('\"') && val.length >= 2) {\n val = val.slice(1, -1);\n }\n params[name] = val;\n }\n return { property, params, value };\n}\n\ninterface IcsDateField {\n raw: string;\n params: Record<string, string>;\n}\n\n/** @internal — exported for testing only. */\nexport function parseIcsEvents(raw: string): CalendarEvent[] {\n const events: CalendarEvent[] = [];\n // RFC 5545 §3.1 line unfolding: a CRLF (now \\n) followed by a single\n // whitespace character (space or tab) is a fold — remove both characters\n // to join the continuation onto the preceding logical line. This MUST\n // happen after normalising CRLF → \\n and BEFORE splitting on \\n.\n const normalized = raw.replace(/\\r\\n/g, \"\\n\").replace(/\\n[ \\t]/g, \"\");\n const blocks = normalized.split(/BEGIN:VEVENT/i).slice(1);\n for (const block of blocks) {\n const endIdx = block.search(/END:VEVENT/i);\n const body = endIdx === -1 ? block : block.slice(0, endIdx);\n const simpleFields: Record<string, string> = {};\n const dateFields: Record<string, IcsDateField> = {};\n for (const line of body.split(\"\\n\")) {\n const parsed = parseIcsLine(line);\n if (!parsed) continue;\n const { property, params, value } = parsed;\n if (property === \"DTSTART\" || property === \"DTEND\") {\n if (dateFields[property] === undefined) {\n dateFields[property] = { raw: value, params };\n }\n } else if (simpleFields[property] === undefined) {\n simpleFields[property] = value;\n }\n }\n const title = simpleFields.SUMMARY;\n const dtstart = dateFields.DTSTART;\n if (!title || !dtstart) continue;\n const dtend = dateFields.DTEND;\n events.push({\n id: simpleFields.UID ?? cryptoRandomId(),\n title,\n start: normalizeIcsDate(dtstart.raw, dtstart.params),\n end: dtend ? normalizeIcsDate(dtend.raw, dtend.params) : undefined,\n location: simpleFields.LOCATION,\n notes: simpleFields.DESCRIPTION,\n });\n }\n return events;\n}\n\n/**\n * Normalise an ICS date/datetime value (optionally with a `TZID` parameter)\n * into an ISO 8601 string that downstream code can compare unambiguously.\n *\n * Behaviour:\n * - `20260411T150000Z` → `2026-04-11T15:00:00Z`\n * - `20260411` → `2026-04-11T00:00:00Z` (date-only events are day-boundaries)\n * - `20260411T150000` (floating, no Z, no TZID) → `2026-04-11T15:00:00` (floating)\n * - `20260411T233000` with `TZID=America/New_York` → `2026-04-12T03:30:00Z`\n * (applies the zone offset at that wallclock time; DST-aware via Intl)\n * - Unknown TZID falls back to UTC with a logged warning (conservative:\n * the event still appears, but on the UTC date).\n */\nfunction normalizeIcsDate(value: string, params: Record<string, string> = {}): string {\n // Date-time with explicit Z suffix (UTC).\n if (/^\\d{8}T\\d{6}Z$/.test(value)) {\n const y = value.slice(0, 4);\n const m = value.slice(4, 6);\n const d = value.slice(6, 8);\n const hh = value.slice(9, 11);\n const mm = value.slice(11, 13);\n const ss = value.slice(13, 15);\n return `${y}-${m}-${d}T${hh}:${mm}:${ss}Z`;\n }\n // Date-time without Z: may be floating or zoned via TZID.\n if (/^\\d{8}T\\d{6}$/.test(value)) {\n const y = value.slice(0, 4);\n const m = value.slice(4, 6);\n const d = value.slice(6, 8);\n const hh = value.slice(9, 11);\n const mm = value.slice(11, 13);\n const ss = value.slice(13, 15);\n const local = `${y}-${m}-${d}T${hh}:${mm}:${ss}`;\n const tzid = params.TZID;\n if (tzid) {\n const utcIso = icsWallclockToUtc(local, tzid);\n if (utcIso) return utcIso;\n log.warn(\n `briefing: unsupported TZID \"${tzid}\" — treating as UTC for ${local}`,\n );\n return `${local}Z`;\n }\n // No TZID → floating. Downstream compares the date slice directly.\n return local;\n }\n // Date-only (all-day event). Date-only values carry no TZID per RFC 5545.\n if (/^\\d{8}$/.test(value)) {\n return `${value.slice(0, 4)}-${value.slice(4, 6)}-${value.slice(6, 8)}T00:00:00Z`;\n }\n return value;\n}\n\n/**\n * Convert a wallclock local datetime in a named IANA timezone to a UTC ISO\n * string. Returns null if the timezone is unsupported by the runtime.\n *\n * Implementation note: this is the standard \"invert the formatter\" technique.\n * We treat the local wallclock as though it were UTC, ask the runtime what\n * time that instant shows in the target zone, and the delta is the zone's\n * offset at that wallclock moment (DST-aware).\n */\nfunction icsWallclockToUtc(local: string, tzid: string): string | null {\n const match = local.match(/^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})$/);\n if (!match) return null;\n const [, y, mo, d, hh, mm, ss] = match;\n // Treat the wallclock as UTC for the first probe.\n const naiveUtcMs = Date.UTC(\n Number(y),\n Number(mo) - 1,\n Number(d),\n Number(hh),\n Number(mm),\n Number(ss),\n );\n if (!Number.isFinite(naiveUtcMs)) return null;\n\n let formatter: Intl.DateTimeFormat;\n try {\n formatter = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: tzid,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hour12: false,\n });\n } catch {\n return null;\n }\n\n const zonedMs = zonedFormatToMs(formatter, new Date(naiveUtcMs));\n if (zonedMs === null) return null;\n // Offset = naiveUtc − zonedAtNaiveUtc (positive for zones east of UTC).\n const offsetMs = naiveUtcMs - zonedMs;\n // Apply the offset once to land on the real UTC instant.\n const realUtcMs = naiveUtcMs + offsetMs;\n // Second pass: offsets can differ when the wallclock crosses a DST boundary.\n const zonedMs2 = zonedFormatToMs(formatter, new Date(realUtcMs));\n if (zonedMs2 !== null) {\n const offsetMs2 = realUtcMs - zonedMs2;\n if (offsetMs2 !== offsetMs) {\n return new Date(naiveUtcMs + offsetMs2).toISOString();\n }\n }\n return new Date(realUtcMs).toISOString();\n}\n\n/**\n * Format a Date in the given timezone and return the absolute ms timestamp\n * of that wallclock time interpreted as if it were UTC. Used only by\n * `icsWallclockToUtc` to compute zone offsets.\n */\nfunction zonedFormatToMs(formatter: Intl.DateTimeFormat, date: Date): number | null {\n const parts = formatter.formatToParts(date);\n const get = (type: string): string | undefined =>\n parts.find((p) => p.type === type)?.value;\n const y = get(\"year\");\n const mo = get(\"month\");\n const d = get(\"day\");\n const hh = get(\"hour\");\n const mm = get(\"minute\");\n const ss = get(\"second\");\n if (!y || !mo || !d || !hh || !mm || !ss) return null;\n // RFC 5545 / Intl edge-case: some runtimes return `hour: \"24\"` for midnight\n // while keeping the same calendar day (instead of returning hour=0 with the\n // next day). Passing 24 straight to Date.UTC would roll the date forward by\n // one day, producing a 24-hour-skewed offset in icsWallclockToUtc and\n // shifting TZID midnight events to the wrong briefing day. Normalise to 0\n // and leave the date component unchanged.\n const normalizedHour = Number(hh) === 24 ? 0 : Number(hh);\n const ms = Date.UTC(Number(y), Number(mo) - 1, Number(d), normalizedHour, Number(mm), Number(ss));\n return Number.isFinite(ms) ? ms : null;\n}\n\n/**\n * Returns true when `isoStr` has an explicit UTC or numeric offset suffix\n * (Z, ±HH:MM, or ±HHMM). Floating datetimes produced by `normalizeIcsDate`\n * have no such suffix.\n */\nfunction isoHasTimezone(isoStr: string): boolean {\n return /(Z|[+-]\\d{2}:?\\d{2})$/.test(isoStr);\n}\n\n/**\n * Parse an ISO datetime string (UTC-aware or floating) to milliseconds since\n * epoch, or `null` if the string is not a valid datetime.\n *\n * - UTC / offset-aware strings are passed directly to `new Date()`.\n * - Floating strings (no timezone suffix) are interpreted as UTC so that\n * interval arithmetic uses the same epoch base as UTC-aware strings.\n * The caller is responsible for using the correct date-boundary constants.\n */\nfunction isoToMs(isoStr: string): number | null {\n if (!isoStr) return null;\n let src = isoStr;\n if (!isoHasTimezone(src)) {\n // Treat floating time as UTC for interval math (append Z).\n src = src + \"Z\";\n }\n const ms = new Date(src).getTime();\n return Number.isFinite(ms) ? ms : null;\n}\n\n/** @internal — exported for testing only. */\nexport function eventFallsOnDate(event: CalendarEvent, dateIso: string): boolean {\n const target = dateIso.slice(0, 10);\n const start = event.start;\n\n // Floating ICS datetime (no Z, no offset): `normalizeIcsDate` produces\n // \"YYYY-MM-DDTHH:MM:SS\" with no timezone. Passing this to `new Date()`\n // causes ECMAScript to parse it as local time, which then round-trips\n // through UTC via `toISOString()` and can shift the calendar date.\n // For floating times we compare date portions directly (no epoch arithmetic).\n const startIsFloating = !isoHasTimezone(start);\n\n if (startIsFloating) {\n // Validate the start timestamp with the same rigour applied to end:\n // 1. Shape check — must match the ISO-8601 date or datetime pattern.\n // 2. Real-date check — round-trip through UTC to reject impossible dates\n // like \"2026-02-30\" that JavaScript silently auto-corrects.\n // 3. Time-component check — reject out-of-range hours/minutes/seconds\n // (e.g. \"2026-04-11T25:99:00\") that JavaScript rolls over to a\n // different day, which would cause the event to be matched against\n // unrelated calendar dates.\n // If start fails any check we skip the event entirely — there is no usable\n // start date to fall back on.\n const startShapeOk =\n typeof start === \"string\" &&\n /^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?)?$/.test(start);\n if (!startShapeOk) {\n log.debug(`briefing: skipping calendar event with invalid start value: ${JSON.stringify(start)}`);\n return false;\n }\n const startDateStr = start.slice(0, 10);\n const startDateProbe = new Date(startDateStr + \"T00:00:00Z\");\n if (\n Number.isNaN(startDateProbe.getTime()) ||\n startDateProbe.toISOString().slice(0, 10) !== startDateStr\n ) {\n log.warn(\n `briefing: skipping calendar event \"${event.title}\" with impossible start date ${JSON.stringify(startDateStr)}`,\n );\n return false;\n }\n const startRawTime = start.indexOf(\"T\") !== -1 ? start.slice(start.indexOf(\"T\") + 1) : \"\";\n if (startRawTime !== \"\") {\n const startTimeParts = startRawTime.split(\":\").map(Number);\n const shh = startTimeParts[0] ?? 0;\n const smm = startTimeParts[1] ?? 0;\n const sss = Math.floor(startTimeParts[2] ?? 0);\n if (shh > 23 || smm > 59 || sss > 59) {\n log.warn(\n `briefing: skipping calendar event \"${event.title}\" with out-of-range start time ${JSON.stringify(startRawTime)}`,\n );\n return false;\n }\n }\n\n const startDate = startDateStr;\n const end = event.end;\n\n // Point event (no end) — simple date prefix comparison.\n if (!end) return startDate === target;\n\n // Validate that end is a recognisable ISO-8601 date/datetime string before\n // slicing it for lexicographic comparison. A malformed end (e.g. a JSON\n // feed emitting \"end\": \"invalid\") would otherwise produce a non-date prefix\n // from `end.slice(0, 10)` and cause the event to appear on unrelated days.\n // Fallback: treat the event as a single-day event starting on startDate.\n const endIsValid =\n typeof end === \"string\" &&\n /^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?)?$/.test(end);\n if (!endIsValid) {\n log.warn(\n `briefing: event \"${event.title}\" has malformed end timestamp ${JSON.stringify(end)}; treating as single-day event at ${startDate}`,\n );\n // Render the event but only on its start date.\n return startDate === target;\n }\n\n // UhLh: Reject impossible calendar dates that pass the regex (e.g. \"2026-99-99\").\n // The regex only validates the *shape* of the string — it accepts month 99 and\n // day 99. We must additionally verify the date is real by constructing a UTC\n // Date and checking that the ISO round-trip matches the input prefix. If the\n // date is impossible, Date will auto-correct it (e.g. \"2026-01-99\" becomes some\n // later date), so the round-trip will differ. Fall back to single-day semantics.\n const endDate = end.slice(0, 10);\n const endDateProbe = new Date(endDate + \"T00:00:00Z\");\n if (\n Number.isNaN(endDateProbe.getTime()) ||\n endDateProbe.toISOString().slice(0, 10) !== endDate\n ) {\n log.warn(\n `briefing: event \"${event.title}\" has impossible end date ${JSON.stringify(endDate)}; treating as single-day event at ${startDate}`,\n );\n return startDate === target;\n }\n\n // Validate the time components of the end timestamp when present.\n // The regex above only checks the shape (e.g. two digits for hour) but does\n // not enforce numeric ranges. A value like \"2026-04-11T25:99:99\" passes the\n // regex and the date-round-trip check above yet carries an impossible time.\n // JavaScript's Date constructor silently rolls such values over to a later\n // date, which would make the event bleed into unrelated days. Extract the\n // individual time fields and reject anything outside the valid range.\n const rawTime = end.indexOf(\"T\") !== -1 ? end.slice(end.indexOf(\"T\") + 1) : \"\";\n if (rawTime !== \"\") {\n const timeParts = rawTime.split(\":\").map(Number);\n const hh = timeParts[0] ?? 0;\n const mm = timeParts[1] ?? 0;\n // Seconds may carry a fractional component; floor to get the integer part.\n const ss = Math.floor(timeParts[2] ?? 0);\n const timeIsValid = hh <= 23 && mm <= 59 && ss <= 59;\n if (!timeIsValid) {\n log.warn(\n `briefing: event \"${event.title}\" has out-of-range end time ${JSON.stringify(rawTime)}; treating as single-day event at ${startDate}`,\n );\n return startDate === target;\n }\n }\n\n // Span event: include if [start, end) overlaps the target calendar day.\n //\n // We can't use pure YYYY-MM-DD lexicographic comparison because a\n // same-day event (`start=2026-04-11T14:30`, `end=2026-04-11T15:00`)\n // has `startDate === endDate === \"2026-04-11\"`, and a `target < endDate`\n // check would wrongly exclude it. A cross-day event ending at\n // `2026-04-12T00:00:00` (exact midnight) also needs the end day to be\n // treated as exclusive per half-open `[start, end)` semantics.\n //\n // Decide whether the end day is still active on the end date by looking\n // at the time portion: if the end time is strictly after midnight, the\n // event is still running at the start of the end day and should include\n // it; if the end time is exactly midnight, the event ends precisely at\n // the boundary and the end day is excluded. Within-day spans always\n // have a non-zero end time and so correctly include their own date.\n //\n // UhLg: A date-only end value (no \"T\" separator) produces an empty\n // endTime string. The regex above does not match empty string, so\n // endAtExactMidnight would be false and the event would incorrectly\n // appear on the end date. Date-only end values carry [start, end)\n // semantics (the end date is exclusive), so we treat them as midnight.\n const endTime = end.slice(11); // \"HH:MM\", \"HH:MM:SS\", \"HH:MM:SS.mmm\", or \"\" (date-only)\n // Treat any end time that is exactly midnight — or absent (date-only) — as\n // day-exclusive per [start, end) semantics.\n // Cases covered:\n // \"\" — date-only end (UhLg fix: exclusive like midnight)\n // \"00:00\" — HH:MM form (valid floating-time ISO value, no seconds)\n // \"00:00:00\" — HH:MM:SS form\n // \"00:00:00.000...\" — with fractional seconds (any number of trailing zeros)\n // A bare `>` string comparison incorrectly treats \"00:00:00.000\" as > \"00:00:00\"\n // because the fractional suffix makes the string lexicographically longer.\n const endIsDateOnly = endTime === \"\";\n const endAtExactMidnight = endIsDateOnly || /^00(:00){1,2}(\\.0+)?$/.test(endTime);\n const endActiveOnEndDay = !endAtExactMidnight;\n if (endActiveOnEndDay) {\n return startDate <= target && target <= endDate;\n }\n return startDate <= target && target < endDate;\n }\n\n // UTC or offset-aware ISO string: parse and normalise to UTC milliseconds,\n // then check whether the event's [start, end) interval overlaps the target\n // UTC day [dayStart, dayEnd).\n const startMs = isoToMs(start);\n if (startMs === null) {\n log.debug(`briefing: skipping calendar event with invalid start value: ${JSON.stringify(start)}`);\n return false;\n }\n\n // Boundaries of the target UTC day (half-open: [dayStart, dayEnd)).\n const dayStart = Date.UTC(\n Number(target.slice(0, 4)),\n Number(target.slice(5, 7)) - 1,\n Number(target.slice(8, 10)),\n );\n const dayEnd = dayStart + 86_400_000; // +24 h\n\n const end = event.end;\n if (!end) {\n // Point event: included iff start falls within [dayStart, dayEnd).\n return startMs >= dayStart && startMs < dayEnd;\n }\n\n const endMs = isoToMs(end);\n if (endMs === null) {\n // Unparseable end — fall back to point-event semantics.\n return startMs >= dayStart && startMs < dayEnd;\n }\n\n // Interval event: overlaps day iff start < dayEnd AND end > dayStart.\n // Using strict > for end so that an event ending exactly at midnight\n // (dayEnd of previous day) is NOT counted on the next day.\n return startMs < dayEnd && endMs > dayStart;\n}\n\nfunction cryptoRandomId(): string {\n // Keep dependency-free: Math.random is fine for synthetic fixture IDs.\n return `evt-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// buildBriefing\n// ──────────────────────────────────────────────────────────────────────────\n\n/** Dependency-injection hook for LLM follow-up generation (used in tests). */\nexport type BriefingFollowupGenerator = (\n prompt: {\n sections: BriefingSections;\n windowLabel: string;\n maxFollowups: number;\n },\n) => Promise<BriefingFollowup[]>;\n\n/** Options accepted by `buildBriefing`. */\nexport interface BuildBriefingOptions {\n /** Workspace-scoped storage. Tests pass a temp dir. */\n storage: StorageManager;\n /** Parsed window. If omitted, a default 1-day window is used. */\n window?: ParsedBriefingWindow;\n /** Optional focus filter. */\n focus?: BriefingFocus | null;\n /** Optional namespace hint for logging. */\n namespace?: string;\n /** Calendar source. Section omitted entirely when undefined. */\n calendarSource?: CalendarSource;\n /** Maximum LLM follow-ups (0 to disable the section). */\n maxFollowups?: number;\n /** Whether the module is allowed to invoke the Responses API. */\n allowLlm?: boolean;\n /** OpenAI API key. If absent the follow-up section is gracefully omitted. */\n openaiApiKey?: string;\n /** OpenAI-compatible base URL (for Azure or proxied endpoints). */\n openaiBaseUrl?: string;\n /** Model id for the Responses call. */\n model?: string;\n /** Injected follow-up generator. Overrides real LLM call (tests). */\n followupGenerator?: BriefingFollowupGenerator;\n /** Injected \"now\" — makes tests deterministic. */\n now?: Date;\n}\n\nconst MAX_ACTIVE_THREADS = 8;\nconst MAX_RECENT_ENTITIES = 8;\nconst MAX_OPEN_COMMITMENTS = 8;\n\n/**\n * Build the daily context briefing.\n *\n * Never throws on LLM failures — the suggested follow-ups section is simply\n * omitted and `followupsUnavailableReason` is set.\n */\nexport async function buildBriefing(options: BuildBriefingOptions): Promise<BriefingResult> {\n const now = options.now ?? new Date();\n const window = options.window ?? defaultWindow(now);\n const maxFollowups = clampFollowups(options.maxFollowups);\n const focus = options.focus ?? null;\n\n const [allMemories, allEntities] = await Promise.all([\n safeReadMemories(options.storage, window).then(excludeSupportPassportPrivateMemories),\n safeReadEntities(options.storage),\n ]);\n\n const memoriesInWindow = filterMemoriesByWindow(allMemories, window);\n const focusedMemories = focus\n ? memoriesInWindow.filter((m) => focusMatchesMemory(m, focus, options.storage.entityAliases))\n : memoriesInWindow;\n\n const activeThreads = buildActiveThreads(focusedMemories);\n const recentEntities = buildRecentEntities(allEntities, window, focus);\n const openCommitments = buildOpenCommitments(focusedMemories);\n\n const calendarLoadResult = options.calendarSource\n ? await loadTodayCalendar(options.calendarSource, now)\n : undefined;\n\n const calendarSourceErrors: BriefingCalendarSourceError[] =\n calendarLoadResult?.error ? [calendarLoadResult.error] : [];\n\n const sectionsBase: BriefingSections = {\n activeThreads,\n recentEntities,\n openCommitments,\n suggestedFollowups: [],\n todayCalendar: calendarLoadResult?.events,\n };\n\n let followups: BriefingFollowup[] = [];\n let followupsUnavailableReason: string | undefined;\n\n if (maxFollowups === 0 || options.allowLlm === false) {\n followupsUnavailableReason = \"LLM follow-ups disabled by configuration\";\n } else if (!options.openaiApiKey && !options.followupGenerator) {\n followupsUnavailableReason =\n 'no LLM configured for follow-ups (set OPENAI_API_KEY, enable a local LLM, or use modelSource \"gateway\")';\n } else {\n try {\n const generator = options.followupGenerator ?? buildOpenAiFollowupGenerator({\n apiKey: options.openaiApiKey!,\n model: options.model ?? BRIEFING_FOLLOWUP_DEFAULT_MODEL,\n baseURL: options.openaiBaseUrl,\n });\n const generated = await withBriefingFollowupTimeout(\n generator({\n sections: sectionsBase,\n windowLabel: window.label,\n maxFollowups,\n }),\n );\n followups = generated.slice(0, maxFollowups);\n } catch (err) {\n const errMsg = stringifyError(err);\n const modelName = options.model ?? BRIEFING_FOLLOWUP_DEFAULT_MODEL;\n // Detect \"model not found / invalid\" errors from the Responses API and\n // produce a user-friendly message that surfaces the problematic identifier.\n if (\n /model/i.test(errMsg) &&\n (/not found/i.test(errMsg) || /does not exist/i.test(errMsg) || /invalid/i.test(errMsg))\n ) {\n followupsUnavailableReason =\n `configured follow-up model '${modelName}' is not available in the Responses API`;\n } else {\n followupsUnavailableReason = `LLM follow-ups failed: ${errMsg}`;\n }\n log.warn(`briefing: ${followupsUnavailableReason}`);\n }\n }\n\n const sections: BriefingSections = {\n ...sectionsBase,\n suggestedFollowups: followups,\n };\n\n // Drain auto-applied passive-correction notifications (issue #1581). The\n // queue is cleared on read, so each notification surfaces in exactly one\n // briefing. Fail-open: a drain error never blocks the briefing.\n let correctionNotifications: BriefingResult[\"correctionNotifications\"];\n try {\n const drained = await drainPassiveCorrectionNotifications(options.storage.dir);\n if (drained.length > 0) {\n correctionNotifications = drained.map((n) => ({\n planId: n.planId,\n summary: n.summary,\n undoCommand: n.undoCommand,\n createdAt: n.createdAt,\n }));\n }\n } catch (err) {\n log.debug(`briefing: correction notification drain failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);\n }\n\n const windowIso = { from: window.from.toISOString(), to: window.to.toISOString() };\n const markdown = renderBriefingMarkdown({\n sections,\n windowLabel: window.label,\n focus,\n followupsUnavailableReason,\n generatedAt: now,\n namespace: options.namespace,\n correctionNotifications,\n });\n\n const json: Record<string, unknown> = {\n generatedAt: now.toISOString(),\n window: windowIso,\n focus,\n namespace: options.namespace ?? null,\n correctionNotifications: correctionNotifications ?? null,\n sections,\n followupsUnavailableReason: followupsUnavailableReason ?? null,\n calendarSourceErrors: calendarSourceErrors.length > 0 ? calendarSourceErrors : null,\n };\n\n const result: BriefingResult = {\n markdown,\n json,\n sections,\n followupsUnavailableReason,\n window: windowIso,\n ...(correctionNotifications ? { correctionNotifications } : {}),\n };\n\n if (calendarSourceErrors.length > 0) {\n result.calendarSourceErrors = calendarSourceErrors;\n }\n\n return result;\n}\n\nfunction clampFollowups(value: number | undefined): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return 5;\n return Math.max(0, Math.min(10, Math.floor(value)));\n}\n\nfunction defaultWindow(now: Date): ParsedBriefingWindow {\n const parsed = parseBriefingWindow(\"yesterday\", now);\n if (parsed) return parsed;\n return { from: new Date(now.getTime() - DAY_MS), to: now, label: \"yesterday\" };\n}\n\nasync function safeReadEntities(storage: StorageManager): Promise<EntityFile[]> {\n try {\n return await storage.readAllEntityFiles();\n } catch (err) {\n log.warn(`briefing: readAllEntityFiles failed: ${err}`);\n return [];\n }\n}\n\nfunction memoryTimestamp(memory: MemoryFile): number {\n const raw = memory.frontmatter.updated || memory.frontmatter.created;\n if (!raw) return 0;\n const t = Date.parse(raw);\n return Number.isFinite(t) ? t : 0;\n}\n\n/** @internal — exported for testing only. */\nexport function filterMemoriesByWindow(memories: MemoryFile[], window: ParsedBriefingWindow): MemoryFile[] {\n const fromMs = window.from.getTime();\n const toMs = window.to.getTime();\n return memories.filter((m) => {\n // Exclude explicitly retired statuses so commitments overridden within the\n // window don't appear as open. In addition to `superseded` / `archived`\n // (temporal retirement), also exclude `rejected` and `quarantined`, which\n // come from governance/disposition workflows: those memories have been\n // explicitly marked unsafe or invalid and must NOT flow into active\n // threads, open commitments, or follow-up generation, even if they\n // fall within the briefing window. Surfacing them would reintroduce\n // quarantined content into downstream automation as actionable context.\n //\n // `pending_review` memories are awaiting human review — not invalidated —\n // and must be included so reviewers see them in the briefing.\n const status = m.frontmatter.status;\n if (\n status === \"superseded\" ||\n status === \"archived\" ||\n status === \"rejected\" ||\n status === \"quarantined\" ||\n status === \"forgotten\"\n ) {\n return false;\n }\n const ts = memoryTimestamp(m);\n return ts >= fromMs && ts < toMs;\n });\n}\n\n/** @internal — exported for testing only. */\nexport function buildActiveThreads(memories: MemoryFile[]): BriefingActiveThread[] {\n const buckets = new Map<string, BriefingActiveThread>();\n for (const memory of memories) {\n const threadKey = extractThreadKey(memory);\n const updatedAt = memory.frontmatter.updated || memory.frontmatter.created || \"\";\n const existing = buckets.get(threadKey);\n if (!existing || updatedAt > existing.updatedAt) {\n buckets.set(threadKey, {\n id: threadKey,\n title: summarizeContentTitle(memory),\n updatedAt,\n // Always derive reason from the newest memory so the description\n // reflects the most-recent activity type, not the first memory seen.\n reason: describeReason(memory),\n });\n }\n }\n return Array.from(buckets.values())\n .sort((a, b) => {\n if (a.updatedAt > b.updatedAt) return -1;\n if (a.updatedAt < b.updatedAt) return 1;\n // Tiebreaker: lexicographic order by id ensures a deterministic, stable\n // result when multiple threads share the same updatedAt timestamp (e.g.\n // after a batch extraction run).\n return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;\n })\n .slice(0, MAX_ACTIVE_THREADS);\n}\n\nfunction extractThreadKey(memory: MemoryFile): string {\n const entityRef = memory.frontmatter.entityRef?.trim();\n if (entityRef) return `entity:${entityRef}`;\n const tags = memory.frontmatter.tags ?? [];\n const topicTag = tags.find((t) => t.startsWith(\"topic:\"));\n if (topicTag) return topicTag;\n if (tags.length > 0) return `tag:${tags[0]}`;\n return `memory:${memory.frontmatter.id}`;\n}\n\nfunction summarizeContentTitle(memory: MemoryFile): string {\n const firstLine = (memory.content || \"\").split(\"\\n\").find((line) => line.trim().length > 0) ?? \"\";\n const trimmed = firstLine.trim();\n if (trimmed.length === 0) return memory.frontmatter.id;\n return trimmed.length > 120 ? `${trimmed.slice(0, 117)}...` : trimmed;\n}\n\nfunction describeReason(memory: MemoryFile): string {\n const cat = memory.frontmatter.category;\n if (cat === \"commitment\") return \"open commitment\";\n if (cat === \"decision\") return \"recent decision\";\n if (cat === \"correction\") return \"recent correction\";\n return \"recent activity\";\n}\n\n/** @internal — exported for testing only. */\nexport function buildRecentEntities(\n entities: EntityFile[],\n window: ParsedBriefingWindow,\n focus: BriefingFocus | null,\n): BriefingRecentEntity[] {\n const fromMs = window.from.getTime();\n const scored: BriefingRecentEntity[] = [];\n const now = window.to;\n for (const entity of entities) {\n if (focus && !focusMatchesEntity(entity, focus)) continue;\n const toMs = window.to.getTime();\n const updatedMs = entity.updated ? Date.parse(entity.updated) : 0;\n if (!Number.isFinite(updatedMs) || updatedMs < fromMs || updatedMs >= toMs) continue;\n const score = StorageManager.scoreEntity(entity, now);\n scored.push({\n name: entity.name,\n type: entity.type,\n updatedAt: entity.updated,\n score: Number(score.toFixed(4)),\n summary: entity.synthesis || entity.summary,\n });\n }\n return scored\n .sort((a, b) => {\n const scoreDiff = b.score - a.score;\n if (scoreDiff !== 0) return scoreDiff;\n if (a.updatedAt > b.updatedAt) return -1;\n if (a.updatedAt < b.updatedAt) return 1;\n return 0;\n })\n .slice(0, MAX_RECENT_ENTITIES);\n}\n\n/** @internal — exported for testing only. */\nexport function buildOpenCommitments(memories: MemoryFile[]): BriefingOpenCommitment[] {\n const commitments: BriefingOpenCommitment[] = [];\n\n for (const memory of memories) {\n const tags = memory.frontmatter.tags ?? [];\n const isPending = tags.some((t) => t.toLowerCase() === \"pending\");\n const isCommitment = memory.frontmatter.category === \"commitment\";\n const isUnresolvedQuestion = /(?:[??؟]$|\\bfollow[- ]up\\b|\\btodo\\b)/i.test(memory.content);\n\n if (isPending || isCommitment || isUnresolvedQuestion) {\n const kind: BriefingOpenCommitment[\"kind\"] = isCommitment\n ? \"commitment\"\n : isUnresolvedQuestion\n ? \"question\"\n : \"pending_memory\";\n commitments.push({\n id: memory.frontmatter.id,\n kind,\n text: summarizeContentTitle(memory),\n source: memory.frontmatter.source,\n createdAt: memory.frontmatter.created,\n });\n }\n }\n\n return commitments\n .sort((a, b) => {\n // Missing timestamps sort last (highest comparator value).\n if (!a.createdAt && !b.createdAt) return 0;\n if (!a.createdAt) return 1;\n if (!b.createdAt) return -1;\n if (a.createdAt > b.createdAt) return -1;\n if (a.createdAt < b.createdAt) return 1;\n return 0;\n })\n .slice(0, MAX_OPEN_COMMITMENTS);\n}\n\ninterface CalendarLoadResult {\n events: CalendarEvent[] | undefined;\n error: BriefingCalendarSourceError | undefined;\n}\n\nasync function loadTodayCalendar(\n source: CalendarSource,\n now: Date,\n): Promise<CalendarLoadResult> {\n const sourceLabel = (source as { filePath?: string }).filePath ?? \"calendar\";\n try {\n const dateIso = now.toISOString().slice(0, 10);\n const events = await source.eventsForDate(dateIso);\n // Return the events array (possibly empty for a legitimately empty calendar).\n // An empty array is distinct from `undefined`: empty means \"source responded\n // with no events today\"; undefined means \"source failed\".\n return { events, error: undefined };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n log.warn(`briefing: calendar source error (${sourceLabel}): ${message}`);\n // Return undefined events (not []) to signal an error so callers can\n // distinguish \"no events today\" from \"the calendar source threw\".\n return {\n events: undefined,\n error: { source: sourceLabel, error: message },\n };\n }\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Follow-ups (Responses API)\n// ──────────────────────────────────────────────────────────────────────────\n\nfunction buildOpenAiFollowupGenerator(cfg: {\n apiKey: string;\n model: string;\n baseURL?: string;\n}): BriefingFollowupGenerator {\n return async ({ sections, windowLabel, maxFollowups }) => {\n // Lazy import keeps the module dependency-free when LLM path is unused.\n const { OpenAI } = (await import(\"openai\")) as {\n OpenAI: new (opts: { apiKey: string; baseURL?: string }) => unknown;\n };\n const clientOpts: { apiKey: string; baseURL?: string } = { apiKey: cfg.apiKey };\n if (cfg.baseURL) clientOpts.baseURL = cfg.baseURL;\n const client = new OpenAI(clientOpts) as {\n responses: {\n create: (args: {\n model: string;\n instructions: string;\n input: string;\n max_output_tokens?: number;\n }) => Promise<{ output_text?: string }>;\n };\n };\n\n const prompt = buildFollowupPrompt(sections, windowLabel, maxFollowups);\n const response = await client.responses.create({\n model: cfg.model,\n instructions: FOLLOWUP_INSTRUCTIONS,\n input: prompt,\n max_output_tokens: 512,\n });\n\n const text = typeof response.output_text === \"string\" ? response.output_text : \"\";\n return parseFollowupResponse(text, maxFollowups);\n };\n}\n\n/**\n * Minimal chat-completion surface shared by `FallbackLlmClient` (gateway\n * model chain) and `LocalLlmClient` (Ollama / OpenAI-compatible local\n * endpoints). Matches `Orchestrator.fastLlmForRerank` so briefing\n * follow-ups can ride the same routing as other fast-tier operations.\n */\nexport interface BriefingChainLlmClient {\n chatCompletion(\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n options?: {\n temperature?: number;\n maxTokens?: number;\n timeoutMs?: number;\n operation?: string;\n priority?: \"background\" | \"recall-critical\";\n },\n ): Promise<{ content: string } | null>;\n}\n\n/**\n * Build a follow-up generator backed by the configured LLM chain\n * (gateway model source or local LLM) instead of a direct OpenAI key.\n *\n * Local models frequently wrap JSON in code fences or prose, so the\n * response is run through `extractJsonCandidates` and the first candidate\n * that parses into a valid `{ followups: [...] }` shape wins. Throws when\n * the chain returns nothing or no candidate parses — `buildBriefing`\n * catches and surfaces the message via `followupsUnavailableReason`.\n */\nexport function buildChainFollowupGenerator(\n client: BriefingChainLlmClient,\n): BriefingFollowupGenerator {\n return async ({ sections, windowLabel, maxFollowups }) => {\n const prompt = buildFollowupPrompt(sections, windowLabel, maxFollowups);\n const response = await client.chatCompletion(\n [\n { role: \"system\", content: FOLLOWUP_INSTRUCTIONS },\n { role: \"user\", content: prompt },\n ],\n {\n temperature: 0.2,\n maxTokens: 512,\n operation: \"briefing-followups\",\n priority: \"background\",\n },\n );\n if (!response?.content) {\n throw new Error(\"LLM chain returned no response\");\n }\n const candidates = extractJsonCandidates(response.content);\n let lastError: unknown;\n for (const candidate of candidates) {\n try {\n return parseFollowupResponse(candidate, maxFollowups);\n } catch (err) {\n lastError = err;\n }\n }\n throw new Error(\n `LLM chain response contained no valid followups JSON: ${stringifyError(lastError ?? \"no JSON candidates found\")}`,\n );\n };\n}\n\nconst FOLLOWUP_INSTRUCTIONS = `You suggest short follow-up prompts for a daily context briefing.\nReturn strict JSON of the form { \"followups\": [{ \"text\": \"...\", \"rationale\": \"...\" }] }.\nRules:\n- Never invent facts absent from the input.\n- Keep each \"text\" under 140 characters.\n- Prefer concrete, action-oriented phrasing.\n- Omit duplicates. Avoid filler.`;\n\nfunction buildFollowupPrompt(\n sections: BriefingSections,\n windowLabel: string,\n maxFollowups: number,\n): string {\n const lines: string[] = [];\n lines.push(`Window: ${windowLabel}`);\n lines.push(`Desired follow-ups: ${maxFollowups}`);\n lines.push(\"\");\n lines.push(\"Active threads:\");\n for (const t of sections.activeThreads) lines.push(`- ${t.title} (${t.reason})`);\n lines.push(\"\");\n lines.push(\"Recent entities:\");\n for (const e of sections.recentEntities) lines.push(`- ${e.name} [${e.type}]`);\n lines.push(\"\");\n lines.push(\"Open commitments:\");\n for (const c of sections.openCommitments) lines.push(`- [${c.kind}] ${c.text}`);\n return lines.join(\"\\n\");\n}\n\nfunction parseFollowupResponse(raw: string, max: number): BriefingFollowup[] {\n // JSON.parse throws on invalid JSON — let the caller catch it so the outer\n // try/catch in buildBriefing can set followupsUnavailableReason rather than\n // silently returning an empty array that masks the parse failure.\n const candidates = extractJsonCandidates(raw);\n let lastError: unknown;\n for (const candidate of candidates) {\n try {\n const parsed = JSON.parse(candidate) as unknown;\n if (!parsed || typeof parsed !== \"object\") {\n throw new Error(`LLM returned non-object JSON: ${typeof parsed}`);\n }\n const arr = (parsed as { followups?: unknown }).followups;\n if (!Array.isArray(arr)) {\n throw new Error(`LLM response missing \"followups\" array`);\n }\n const out: BriefingFollowup[] = [];\n for (const entry of arr) {\n if (!entry || typeof entry !== \"object\") continue;\n const text = (entry as Record<string, unknown>).text;\n if (typeof text !== \"string\" || text.trim().length === 0) continue;\n const rationale = (entry as Record<string, unknown>).rationale;\n out.push({\n text: text.trim(),\n rationale: typeof rationale === \"string\" ? rationale.trim() : undefined,\n });\n if (out.length >= max) break;\n }\n return out;\n } catch (err) {\n lastError = err;\n }\n }\n if (lastError) throw lastError;\n throw new Error(\"LLM response contained no JSON candidates\");\n}\n\nfunction stringifyError(err: unknown): string {\n if (err instanceof Error) return err.message;\n return String(err);\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Markdown rendering\n// ──────────────────────────────────────────────────────────────────────────\n\ninterface RenderContext {\n sections: BriefingSections;\n windowLabel: string;\n focus: BriefingFocus | null;\n followupsUnavailableReason?: string;\n generatedAt: Date;\n namespace?: string;\n /** Auto-applied passive corrections drained for this briefing (#1581). */\n correctionNotifications?: BriefingResult[\"correctionNotifications\"];\n}\n\nexport function renderBriefingMarkdown(ctx: RenderContext): string {\n const lines: string[] = [];\n lines.push(`# Daily Context Briefing`);\n lines.push(\"\");\n lines.push(`_Generated ${ctx.generatedAt.toISOString()} (window: ${ctx.windowLabel})_`);\n if (ctx.focus) {\n lines.push(`_Focus: ${ctx.focus.type}:${ctx.focus.value}_`);\n }\n if (ctx.namespace) {\n lines.push(`_Namespace: ${ctx.namespace}_`);\n }\n lines.push(\"\");\n\n if (ctx.correctionNotifications && ctx.correctionNotifications.length > 0) {\n lines.push(`## Memory updates`);\n for (const n of ctx.correctionNotifications) {\n lines.push(`- ✎ ${n.summary} (undo: ${n.undoCommand})`);\n }\n lines.push(\"\");\n }\n\n lines.push(`## Active threads`);\n if (ctx.sections.activeThreads.length === 0) {\n lines.push(`_No active threads in window._`);\n } else {\n for (const t of ctx.sections.activeThreads) {\n lines.push(`- **${t.title}** — ${t.reason} (updated ${t.updatedAt})`);\n }\n }\n lines.push(\"\");\n\n lines.push(`## Recent entities`);\n if (ctx.sections.recentEntities.length === 0) {\n lines.push(`_No entities updated in window._`);\n } else {\n for (const e of ctx.sections.recentEntities) {\n const summary = e.summary ? ` — ${e.summary}` : \"\";\n lines.push(`- **${e.name}** (${e.type}, score ${e.score})${summary}`);\n }\n }\n lines.push(\"\");\n\n lines.push(`## Open commitments`);\n if (ctx.sections.openCommitments.length === 0) {\n lines.push(`_No open commitments detected._`);\n } else {\n for (const c of ctx.sections.openCommitments) {\n lines.push(`- [${c.kind}] ${c.text}`);\n }\n }\n lines.push(\"\");\n\n lines.push(`## Suggested follow-ups`);\n if (ctx.followupsUnavailableReason) {\n lines.push(`_Unavailable: ${ctx.followupsUnavailableReason}_`);\n } else if (ctx.sections.suggestedFollowups.length === 0) {\n lines.push(`_No follow-ups suggested._`);\n } else {\n for (const f of ctx.sections.suggestedFollowups) {\n const rationale = f.rationale ? ` _(${f.rationale})_` : \"\";\n lines.push(`- ${f.text}${rationale}`);\n }\n }\n lines.push(\"\");\n\n if (ctx.sections.todayCalendar !== undefined) {\n lines.push(`## Today's calendar`);\n if (ctx.sections.todayCalendar.length === 0) {\n lines.push(`_No events on the calendar today._`);\n } else {\n for (const ev of ctx.sections.todayCalendar) {\n const end = ev.end ? ` → ${ev.end}` : \"\";\n const loc = ev.location ? ` @ ${ev.location}` : \"\";\n lines.push(`- **${ev.title}** (${ev.start}${end})${loc}`);\n }\n }\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\").trimEnd() + \"\\n\";\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Save helpers\n// ──────────────────────────────────────────────────────────────────────────\n\n/**\n * Resolve the directory where `--save` writes dated briefings.\n * Respects the following precedence:\n * 1. explicit `configOverride` argument\n * 2. `$REMNIC_HOME/briefings/`\n * 3. `$HOME/.remnic/briefings/`\n */\nexport function resolveBriefingSaveDir(\n configOverride: string | null | undefined,\n env?: NodeJS.ProcessEnv,\n): string {\n if (typeof configOverride === \"string\" && configOverride.trim().length > 0) {\n return path.resolve(configOverride.trim());\n }\n const remnicHome = (env === undefined ? readEnvVar(\"REMNIC_HOME\") : env.REMNIC_HOME)?.trim();\n if (remnicHome && remnicHome.length > 0) {\n return path.join(remnicHome, \"briefings\");\n }\n const home = env === undefined\n ? resolveHomeDir()\n : env.HOME ?? env.USERPROFILE ?? os.homedir();\n return path.join(home, \".remnic\", \"briefings\");\n}\n\n/** Format the dated filename for a given briefing. */\nexport function briefingFilename(date: Date, format: \"markdown\" | \"json\" = \"markdown\"): string {\n const day = date.toISOString().slice(0, 10);\n return format === \"json\" ? `${day}.json` : `${day}.md`;\n}\n","/**\n * correction/passive-correction-notifications.ts — JSONL notification queue\n * for auto-applied passive corrections (issue #1581).\n *\n * When passive capture auto-applies a correction, it enqueues a one-line\n * notification here: `✎ Memory updated: <summary> (auto-applied (plan <planId>))`.\n * The plan id is referenced (not a revert command) because `remnic correct`\n * has no `--revert` flag — corrections are page-versioned for manual revert.\n * The next session-start briefing drains the queue ONCE (temp-file-then-rename,\n * rule 54) and clears it, so a notification is shown exactly once.\n *\n * File location: `<storageDir>/state/corrections/notifications.jsonl`\n */\n\nimport { mkdir, readFile, rename, writeFile, unlink } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { serializeMutations } from \"../utils/serialize-mutations.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface PassiveCorrectionNotification {\n planId: string;\n /** One-line human-readable summary of what changed. */\n summary: string;\n /** Undo command the user can run. */\n undoCommand: string;\n createdAt: string;\n}\n\n// ---------------------------------------------------------------------------\n// Path\n// ---------------------------------------------------------------------------\n\nfunction notificationsPath(storageDir: string): string {\n return path.join(storageDir, \"state\", \"corrections\", \"notifications.jsonl\");\n}\n\n// ---------------------------------------------------------------------------\n// Enqueue\n// ---------------------------------------------------------------------------\n\n/**\n * Append a notification line to the JSONL queue. Atomic (rule 54): the line\n * is written to a temp file then renamed over the real file. Multiple\n * notifications accumulate across calls — the queue is drained, not replaced.\n */\nexport async function enqueuePassiveCorrectionNotification(\n storageDir: string,\n notification: PassiveCorrectionNotification,\n): Promise<void> {\n const filePath = notificationsPath(storageDir);\n await serializeMutations(`passive-notification:${filePath}`, async () => {\n // Read existing content (if any) so we accumulate rather than overwrite.\n let existing = \"\";\n try {\n existing = await readFile(filePath, \"utf-8\");\n } catch (err) {\n const code = (err as NodeJS.ErrnoException)?.code;\n if (code !== \"ENOENT\") throw err;\n }\n\n await mkdir(path.dirname(filePath), { recursive: true });\n const line = `${JSON.stringify(notification)}\\n`;\n const content = existing + line;\n\n const tmp = `${filePath}.${process.pid}.${Date.now().toString(36)}.tmp`;\n await writeFile(tmp, content, \"utf-8\");\n await rename(tmp, filePath);\n });\n}\n\n// ---------------------------------------------------------------------------\n// Drain (read + clear — exactly once)\n// ---------------------------------------------------------------------------\n\n/**\n * Read all pending notifications and atomically clear the file. Returns the\n * notifications in insertion order. If the file does not exist, returns [].\n *\n * Drain-once semantics: the file is deleted after reading, so calling this\n * twice in a row returns notifications only the first time.\n */\nexport async function drainPassiveCorrectionNotifications(\n storageDir: string,\n): Promise<PassiveCorrectionNotification[]> {\n const filePath = notificationsPath(storageDir);\n return serializeMutations(`passive-notification:${filePath}`, async () => {\n let raw: string;\n try {\n raw = await readFile(filePath, \"utf-8\");\n } catch (err) {\n const code = (err as NodeJS.ErrnoException)?.code;\n if (code === \"ENOENT\") return [];\n throw err;\n }\n\n const notifications: PassiveCorrectionNotification[] = [];\n for (const line of raw.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n notifications.push(JSON.parse(trimmed) as PassiveCorrectionNotification);\n } catch {\n // Skip malformed lines (rule 34 — best-effort, never crash a briefing).\n continue;\n }\n }\n\n // Clear the file atomically (delete is atomic enough for this use case —\n // the worst case is a duplicate notification, never a lost one, because\n // the read already captured the content).\n try {\n await unlink(filePath);\n } catch (err) {\n const code = (err as NodeJS.ErrnoException)?.code;\n if (code !== \"ENOENT\") throw err;\n }\n\n return notifications;\n });\n}\n"],"mappings":"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","names":["readFile","path","from","readFile","end","path"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/storage/entity-canonical-id-migration.ts"],"sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport {\n access,\n lstat,\n readFile,\n readdir,\n realpath,\n rename,\n stat as statFile,\n unlink,\n writeFile,\n} from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { lstatSync, readFileSync } from \"node:fs\";\nimport { z } from \"zod\";\nimport { computeSupersessionKey, normalizeSupersessionKey } from \"../temporal-supersession.js\";\nimport type { EntityFile, MemoryFile } from \"../types.js\";\nimport { log } from \"../logger.js\";\nimport { isErrnoCode } from \"../utils/errno.js\";\nimport { withHeldFileLock } from \"../utils/serialize-mutations.js\";\nimport { RECALL_FALLBACK_DIRS } from \"../utils/category-dir.js\";\nimport { assertPathInsideRoot } from \"../utils/path-containment.js\";\nimport { withEntityCanonicalMutationLock } from \"./entity-canonical-id-lock.js\";\nimport { assertSafeEntityId } from \"./entity-id-safety.js\";\nimport { isMalformedEntityRecord } from \"../utils/malformed-entity-record.js\";\nimport {\n isMacOsReplicationJunkBasename,\n warnIgnoredMacOsReplicationJunk,\n} from \"../utils/macos-replication-junk.js\";\n\nimport {\n ENTITY_CANONICAL_ID_MIGRATION_FILE,\n ENTITY_CANONICAL_ID_RECONCILE_CONSUMING_MARKER,\n ENTITY_CANONICAL_ID_RECONCILE_MARKER,\n resolveHistoricalEntityCanonicalId,\n} from \"./entity-canonical-id-references.js\";\n\nconst ENTITY_CANONICAL_ID_MIGRATION_VERSION = 1;\nconst ENTITY_CANONICAL_ID_MIGRATION_LOCK_STALE_MS = 60_000;\nconst ENTITY_CANONICAL_ID_MIGRATION_LOCK_MAX_WAIT_MS = 300_000;\nconst TOMBSTONE_LOCK_STALE_MS = 30_000;\nconst TOMBSTONE_LOCK_MAX_WAIT_MS = 5_000;\nconst MEMORY_REWRITE_MAX_PASSES = 3;\nconst ENTITY_MAPPING_RESCAN_MAX_PASSES = 3;\n\nconst EntityCanonicalIdMigrationStateSchema = z.object({\n version: z.literal(ENTITY_CANONICAL_ID_MIGRATION_VERSION),\n complete: z.boolean(),\n mappings: z.record(z.string().min(1)),\n /**\n * Pairs the migration refused to resolve, PARKED rather than dropped.\n * Deleting the record would strand the collision: once an operator removes\n * the legacy file, no later scan can rediscover the mapping, so references\n * still naming the legacy id would never be rewritten. Optional so a journal\n * written by an older build still parses.\n */\n blocked: z.record(z.string().min(1)).optional(),\n});\n\ntype EntityCanonicalIdMigrationState = z.infer<typeof EntityCanonicalIdMigrationStateSchema>;\n\nexport interface EntityCanonicalIdMigrationDependencies {\n stateDir: string;\n entitiesDir: string;\n normalizeEntityName(raw: string, type: string): string;\n resolveEntityFilePath(id: string): string | null;\n readStorageSecureFile(filePath: string): Promise<string>;\n writeStorageSecureFile(filePath: string, content: string, forceEncrypt?: boolean): Promise<void>;\n isEncryptedStorageFile(filePath: string): Promise<boolean>;\n snapshotBeforeWrite(filePath: string, trigger: \"write\"): Promise<void>;\n parseEntityFile(content: string): EntityFile;\n serializeEntityFile(entity: EntityFile): string;\n serializeMemoryWithEntityRef(memory: MemoryFile, entityRef: string): string;\n readMemoryByPath(filePath: string): Promise<MemoryFile | null>;\n readAllMemories(): Promise<MemoryFile[]>;\n readAllColdMemories(): Promise<MemoryFile[]>;\n readArchivedMemories(): Promise<MemoryFile[]>;\n validateMemoryScanRoots?(): Promise<void>;\n rewriteProjectedEntityReferences(mappings: Readonly<Record<string, string>>): Promise<void>;\n rewriteProjectedMemoryEntityReference(\n memoryId: string,\n previousEntityRef: string,\n nextEntityRef: string,\n ): Promise<void>;\n invalidateKnowledgeIndexCache(): void;\n invalidateAllMemoriesCache(): void;\n invalidateColdMemoriesCache(): void;\n bumpMemoryStatusVersion(): void;\n readMigrationFingerprint?(): Promise<string>;\n}\n\nasync function fingerprintPath(filePath: string): Promise<string> {\n let fileStat;\n try {\n fileStat = await lstat(filePath);\n } catch (error) {\n if (!isErrnoCode(error, \"ENOENT\")) throw error;\n }\n return fileStat\n ? `${fileStat.dev}:${fileStat.ino}:${fileStat.mtimeMs}:${fileStat.ctimeMs}:${fileStat.size}`\n : \"missing\";\n}\n\nasync function fingerprintEntityEntries(entitiesDir: string): Promise<string> {\n let names: string[];\n try {\n names = (await readdir(entitiesDir))\n .filter((name) => path.extname(name) === \".md\" && !isMacOsReplicationJunkBasename(name))\n .sort();\n } catch (error) {\n if (isErrnoCode(error, \"ENOENT\")) return \"missing\";\n throw error;\n }\n const hash = createHash(\"sha256\");\n for (const name of names) {\n hash.update(`${name.length}:${name}:`);\n try {\n const entry = await lstat(path.join(entitiesDir, name), { bigint: true });\n hash.update(`${entry.dev}:${entry.ino}:${entry.size}:${entry.mtimeNs}:${entry.ctimeNs}`);\n } catch (error) {\n if (!isErrnoCode(error, \"ENOENT\")) throw error;\n hash.update(\"missing\");\n }\n hash.update(\"\\n\");\n }\n return hash.digest(\"hex\");\n}\n\n/**\n * Completion fingerprint for migration inputs only. Entity entry identities\n * catch same-path edits that do not change the directory inode; the cooperative\n * sentinel covers managed writes. Memory-corpus writes stay excluded so plain\n * fact writes do not reopen migration.\n */\nexport async function getFingerprint(\n baseDir: string,\n entitiesDir: string,\n getEntityMutationVersion: () => string,\n): Promise<string> {\n const [\n entityDirectoryFingerprint,\n entityEntriesFingerprint,\n aliasFingerprint,\n reconcileFingerprint,\n consumingFingerprint,\n ] = await Promise.all([\n fingerprintPath(entitiesDir),\n fingerprintEntityEntries(entitiesDir),\n fingerprintPath(path.join(baseDir, \"config\", \"aliases.json\")),\n fingerprintPath(path.join(baseDir, \"state\", ENTITY_CANONICAL_ID_RECONCILE_MARKER)),\n fingerprintPath(path.join(baseDir, \"state\", ENTITY_CANONICAL_ID_RECONCILE_CONSUMING_MARKER)),\n ]);\n return [\n entityDirectoryFingerprint,\n entityEntriesFingerprint,\n aliasFingerprint,\n reconcileFingerprint,\n consumingFingerprint,\n getEntityMutationVersion(),\n ].join(\":\");\n}\n\nexport async function validateRoots(\n baseDir: string,\n entitiesDir: string,\n stateDir: string,\n archiveDir: string,\n): Promise<void> {\n let baseStat;\n try {\n baseStat = await lstat(baseDir);\n } catch (error) {\n if (isErrnoCode(error, \"ENOENT\")) return;\n throw error;\n }\n if (baseStat.isSymbolicLink() || !baseStat.isDirectory()) {\n throw new Error(`Refusing entity canonical-id migration through unsafe memory root: ${baseDir}.`);\n }\n const memoryRoot = await realpath(baseDir);\n const roots = [\n entitiesDir,\n stateDir,\n ...RECALL_FALLBACK_DIRS.map((directory) => path.join(baseDir, directory)),\n path.join(baseDir, \"cold\"),\n archiveDir,\n ];\n for (const root of roots) {\n let rootStat;\n try {\n rootStat = await lstat(root);\n } catch (error) {\n if (isErrnoCode(error, \"ENOENT\")) continue;\n throw error;\n }\n if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {\n throw new Error(`Refusing entity canonical-id migration through unsafe memory root: ${root}.`);\n }\n assertPathInsideRoot(memoryRoot, await realpath(root), root);\n }\n}\n\nfunction statePath(deps: EntityCanonicalIdMigrationDependencies): string {\n return path.join(deps.stateDir, ENTITY_CANONICAL_ID_MIGRATION_FILE);\n}\n\nasync function assertSafeMigrationStateFile(\n deps: EntityCanonicalIdMigrationDependencies,\n): Promise<void> {\n const migrationStatePath = statePath(deps);\n let fileStat;\n try {\n fileStat = await lstat(migrationStatePath);\n } catch (error) {\n if (isErrnoCode(error, \"ENOENT\")) return;\n throw error;\n }\n if (fileStat.isSymbolicLink() || !fileStat.isFile()) {\n throw new Error(\n `Refusing entity canonical-id migration through unsafe migration state file: ${migrationStatePath}.`,\n );\n }\n const [stateRoot, stateFile] = await Promise.all([\n realpath(deps.stateDir),\n realpath(migrationStatePath),\n ]);\n assertPathInsideRoot(stateRoot, stateFile, migrationStatePath);\n}\nasync function assertSafeTombstoneFile(\n deps: EntityCanonicalIdMigrationDependencies,\n tombstonePath: string,\n): Promise<boolean> {\n let fileStat;\n try {\n fileStat = await lstat(tombstonePath);\n } catch (error) {\n if (isErrnoCode(error, \"ENOENT\")) return false;\n throw error;\n }\n if (fileStat.isSymbolicLink() || !fileStat.isFile()) {\n throw new Error(\n `Refusing entity canonical-id migration through unsafe tombstone ledger: ${tombstonePath}.`,\n );\n }\n const [stateRoot, tombstoneFile] = await Promise.all([\n realpath(deps.stateDir),\n realpath(tombstonePath),\n ]);\n assertPathInsideRoot(stateRoot, tombstoneFile, tombstonePath);\n return true;\n}\n\nfunction lockPath(deps: EntityCanonicalIdMigrationDependencies): string {\n return path.join(deps.stateDir, \"entity-canonical-id-migration.lock\");\n}\n\n\nasync function readState(\n deps: EntityCanonicalIdMigrationDependencies\n): Promise<EntityCanonicalIdMigrationState | null> {\n await assertSafeMigrationStateFile(deps);\n let raw: string;\n try {\n raw = await readFile(statePath(deps), \"utf-8\");\n } catch (error) {\n if (isErrnoCode(error, \"ENOENT\")) return null;\n throw error;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new Error(\"Invalid entity canonical-id migration state.\");\n }\n const result = EntityCanonicalIdMigrationStateSchema.safeParse(parsed);\n if (!result.success) throw new Error(\"Invalid entity canonical-id migration state.\");\n for (const mappings of [result.data.mappings, result.data.blocked ?? {}]) {\n for (const [legacyId, canonicalId] of Object.entries(mappings)) {\n assertSafeEntityId(legacyId);\n assertSafeEntityId(canonicalId);\n }\n }\n return result.data;\n}\n\nasync function writeState(\n deps: EntityCanonicalIdMigrationDependencies,\n state: EntityCanonicalIdMigrationState\n): Promise<void> {\n const destination = statePath(deps);\n const temporary = `${destination}.${process.pid}.${randomUUID()}.tmp`;\n try {\n await writeFile(temporary, `${JSON.stringify(state, null, 2)}\\n`, \"utf-8\");\n await rename(temporary, destination);\n } finally {\n await unlink(temporary).catch(() => undefined);\n }\n}\n\n/**\n * Every journal publish — additive merges, completion markers, prunes —\n * serializes under the entity MUTATION lock (issue #2213 review): entity\n * writers, post-persist repair settles, and tombstone appends revalidate\n * the journal identity under that lock, so an unlocked publish could land\n * inside their resolve→write window and strand the write on a superseded\n * id space.\n */\nasync function publishState(\n deps: EntityCanonicalIdMigrationDependencies,\n state: EntityCanonicalIdMigrationState\n): Promise<void> {\n await withEntityCanonicalMutationLock(deps.stateDir, async () => {\n await writeState(deps, state);\n });\n}\n\nasync function fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n return true;\n } catch (error) {\n if (isErrnoCode(error, \"ENOENT\")) return false;\n throw error;\n }\n}\n\nasync function sameFileIdentity(leftPath: string, rightPath: string): Promise<boolean> {\n try {\n const [left, right] = await Promise.all([statFile(leftPath), statFile(rightPath)]);\n return left.dev === right.dev && left.ino === right.ino;\n } catch (error) {\n if (isErrnoCode(error, \"ENOENT\")) return false;\n throw error;\n }\n}\n\nasync function assertNotSymlink(filePath: string, label: string): Promise<void> {\n let stat;\n try {\n stat = await lstat(filePath);\n } catch (error) {\n if (isErrnoCode(error, \"ENOENT\")) return;\n throw error;\n }\n if (stat.isSymbolicLink()) {\n throw new Error(`Refusing entity canonical-id migration through symlink: ${label}.`);\n }\n}\nasync function assertNotMemorySymlink(filePath: string): Promise<void> {\n let stat;\n try {\n stat = await lstat(filePath);\n } catch (error) {\n if (isErrnoCode(error, \"ENOENT\")) return;\n throw error;\n }\n if (stat.isSymbolicLink()) {\n throw new Error(`Refusing entity canonical-id migration through symlinked memory: ${filePath}.`);\n }\n}\nasync function assertEntityContentUnchanged(\n deps: EntityCanonicalIdMigrationDependencies,\n filePath: string,\n expectedContent: string,\n legacyId: string,\n): Promise<void> {\n const latestContent = await deps.readStorageSecureFile(filePath);\n if (latestContent !== expectedContent) {\n throw new Error(`Legacy entity id ${legacyId} changed while migration was running; retry migration.`);\n }\n}\n\nexport async function readSafeEntityEntries(entitiesDir: string): Promise<string[]> {\n const rootStat = await lstat(entitiesDir);\n if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {\n throw new Error(\"Refusing entity canonical-id migration through a symlinked or non-directory entities root.\");\n }\n const entries = await readdir(entitiesDir);\n for (const entry of entries) {\n await assertNotSymlink(path.join(entitiesDir, entry), entry);\n }\n return entries;\n}\n\n/**\n * Legacy ids the migration cannot resolve on its own, mapped to the canonical\n * id they collide with. Two entity files describing one entity is an ORDINARY\n * operator state, and picking a winner would silently drop whichever side lost.\n * So the pair is skipped and reported: reads of the legacy id keep working\n * exactly as they did before the migration ran.\n */\nexport type BlockedEntityPairs = Map<string, string>;\n\ninterface EntityScanNotes {\n junk: Set<string>;\n malformed: Set<string>;\n}\n\nasync function discoverMappings(\n deps: EntityCanonicalIdMigrationDependencies,\n knownMappings: Readonly<Record<string, string>> = {},\n blocked: BlockedEntityPairs = new Map(),\n scanNotes?: EntityScanNotes,\n): Promise<Record<string, string>> {\n const mappings: Record<string, string> = {};\n const canonicalOwners = new Map<string, string[]>();\n // Blocks describe THIS scan of the directory, so they are rebuilt per pass.\n // Accumulating them across a run would let a collision another writer has\n // since resolved keep pruning its now-valid mapping: the entity file gets\n // renamed while the pruned mapping skips the reference rewrite, stranding\n // memories on a filename that no longer exists.\n blocked.clear();\n const entityEntries = await readSafeEntityEntries(deps.entitiesDir);\n for (const entry of entityEntries) {\n // AppleDouble (`._*`) and `.DS_Store` are Finder metadata, not entity\n // files. A real file that cannot be parsed is left in place: this scan\n // runs during directory init, and aborting it took the daemon down.\n // Skipping is the same outcome as the old throw (the file is not\n // rewritten) without refusing to boot. Symlink checks above still run.\n if (isMacOsReplicationJunkBasename(entry)) {\n scanNotes?.junk.add(entry);\n continue;\n }\n if (!entry.endsWith(\".md\")) continue;\n const legacyId = entry.slice(0, -\".md\".length);\n assertSafeEntityId(legacyId);\n const legacyPath = deps.resolveEntityFilePath(legacyId);\n if (legacyPath === null) continue;\n const entityContent = await deps.readStorageSecureFile(legacyPath);\n if (isMalformedEntityRecord(entityContent)) {\n scanNotes?.malformed.add(entry);\n continue;\n }\n const entity = deps.parseEntityFile(entityContent);\n if (!entity.name.trim() || !entity.type.trim()) {\n scanNotes?.malformed.add(entry);\n continue;\n }\n const canonicalId = deps.normalizeEntityName(entity.name, entity.type);\n assertSafeEntityId(canonicalId);\n if (legacyId === canonicalId) continue;\n const canonicalPath = deps.resolveEntityFilePath(canonicalId);\n if (canonicalPath === null) continue;\n const canonicalExists = await fileExists(canonicalPath);\n if (\n canonicalExists\n && await sameFileIdentity(legacyPath, canonicalPath)\n && knownMappings[legacyId] === canonicalId\n ) continue;\n if (canonicalExists) {\n const canonicalContent = await deps.readStorageSecureFile(canonicalPath);\n if (entityContent !== canonicalContent) {\n blocked.set(legacyId, canonicalId);\n continue;\n }\n }\n canonicalOwners.set(canonicalId, [...(canonicalOwners.get(canonicalId) ?? []), legacyId]);\n mappings[legacyId] = canonicalId;\n }\n // A canonical id claimed by more than one legacy file has no non-arbitrary\n // winner, and readdir order is not stable (§6) - so the \"winner\" would differ\n // between hosts reading identical data. Block every claimant instead.\n for (const [canonicalId, claimants] of canonicalOwners) {\n if (claimants.length < 2) continue;\n for (const legacyId of claimants) {\n blocked.set(legacyId, canonicalId);\n delete mappings[legacyId];\n }\n }\n return mappings;\n}\n\nasync function rewriteReferences(\n deps: EntityCanonicalIdMigrationDependencies,\n mappings: Readonly<Record<string, string>>,\n refreshLock: () => Promise<boolean>\n): Promise<{ rewroteColdMemory: boolean }> {\n let rewroteColdMemory = false;\n for (let pass = 0; pass < MEMORY_REWRITE_MAX_PASSES; pass += 1) {\n deps.invalidateAllMemoriesCache();\n deps.invalidateColdMemoriesCache();\n const [hotMemories, coldMemories, archivedMemories] = await Promise.all([\n deps.readAllMemories(),\n deps.readAllColdMemories(),\n deps.readArchivedMemories(),\n ]);\n const seenPaths = new Set<string>();\n let rewroteAny = false;\n for (const memory of [...hotMemories, ...coldMemories, ...archivedMemories]) {\n const memoryPath = path.resolve(memory.path);\n if (seenPaths.has(memoryPath)) continue;\n seenPaths.add(memoryPath);\n const previousEntityRef = memory.frontmatter.entityRef;\n const canonicalId = previousEntityRef ? mappings[previousEntityRef] : undefined;\n if (!previousEntityRef || !canonicalId) continue;\n await assertNotMemorySymlink(memory.path);\n const observedMemoryFingerprint = await fingerprintPath(memory.path);\n if (observedMemoryFingerprint === \"missing\") continue;\n if (!(await refreshLock())) throw new Error(\"Lost entity canonical-id migration lock.\");\n const latestMemory = await deps.readMemoryByPath(memory.path);\n if (!latestMemory) continue;\n const latestMemoryFingerprint = await fingerprintPath(latestMemory.path);\n if (latestMemoryFingerprint !== observedMemoryFingerprint) continue;\n const latestPreviousEntityRef = latestMemory.frontmatter.entityRef;\n const latestCanonicalId = latestPreviousEntityRef ? mappings[latestPreviousEntityRef] : undefined;\n if (!latestPreviousEntityRef || !latestCanonicalId) continue;\n const memoryEncrypted = await deps.isEncryptedStorageFile(latestMemory.path);\n if ((await fingerprintPath(latestMemory.path)) !== latestMemoryFingerprint) continue;\n await deps.snapshotBeforeWrite(latestMemory.path, \"write\");\n if ((await fingerprintPath(latestMemory.path)) !== latestMemoryFingerprint) continue;\n await deps.writeStorageSecureFile(\n latestMemory.path,\n deps.serializeMemoryWithEntityRef(latestMemory, latestCanonicalId),\n memoryEncrypted,\n );\n await deps.rewriteProjectedMemoryEntityReference(\n latestMemory.frontmatter.id,\n latestPreviousEntityRef,\n latestCanonicalId,\n );\n rewroteAny = true;\n rewroteColdMemory ||= latestMemory.path.includes(`${path.sep}cold${path.sep}`);\n }\n if (!rewroteAny) return { rewroteColdMemory };\n }\n throw new Error(\"Memory references changed while entity canonical-id migration was running; retry migration.\");\n}\n\nfunction rewriteTombstoneLine(\n line: string,\n mappings: Readonly<Record<string, string>>,\n normalizedMappings: ReadonlyMap<string, string>,\n): string {\n let parsed: unknown;\n try {\n parsed = JSON.parse(line);\n } catch {\n return line;\n }\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) return line;\n\n const tombstone = parsed as Record<string, unknown>;\n let changed = false;\n if (typeof tombstone.entityRef === \"string\") {\n const canonicalId = mappings[tombstone.entityRef];\n if (canonicalId) {\n tombstone.entityRef = canonicalId;\n changed = true;\n }\n }\n if (typeof tombstone.supersessionKey === \"string\") {\n const separator = tombstone.supersessionKey.indexOf(\"::\");\n if (separator < 0) return changed ? JSON.stringify(tombstone) : line;\n const normalizedLegacyId = normalizeSupersessionKey(tombstone.supersessionKey.slice(0, separator));\n const canonicalId = normalizedMappings.get(normalizedLegacyId);\n if (canonicalId) {\n const canonicalKey = computeSupersessionKey(canonicalId, tombstone.supersessionKey.slice(separator + 2));\n if (canonicalKey) {\n tombstone.supersessionKey = canonicalKey;\n changed = true;\n }\n }\n }\n return changed ? JSON.stringify(tombstone) : line;\n}\n\nasync function rewriteTombstoneReferences(\n deps: EntityCanonicalIdMigrationDependencies,\n mappings: Readonly<Record<string, string>>,\n refreshLock: () => Promise<boolean>\n): Promise<void> {\n const tombstonePath = path.join(deps.stateDir, \"tombstones.jsonl\");\n const normalizedMappings = new Map<string, string>();\n for (const [legacyId, canonicalId] of Object.entries(mappings)) {\n const normalizedLegacyId = normalizeSupersessionKey(legacyId);\n if (normalizedLegacyId && !normalizedMappings.has(normalizedLegacyId)) {\n normalizedMappings.set(normalizedLegacyId, canonicalId);\n }\n }\n\n await withHeldFileLock(\n path.join(deps.stateDir, \"tombstones.lock\"),\n {\n staleMs: TOMBSTONE_LOCK_STALE_MS,\n maxWaitMs: TOMBSTONE_LOCK_MAX_WAIT_MS,\n },\n async (acquired, tombstoneLock) => {\n if (!acquired) throw new Error(\"Timed out waiting for active tombstone migration.\");\n if (!(await assertSafeTombstoneFile(deps, tombstonePath))) return;\n const tombstoneEncrypted = await deps.isEncryptedStorageFile(tombstonePath);\n const original = await deps.readStorageSecureFile(tombstonePath);\n let changed = false;\n const rewritten = original\n .split(\"\\n\")\n .map((line) => {\n const next = rewriteTombstoneLine(line, mappings, normalizedMappings);\n changed ||= next !== line;\n return next;\n })\n .join(\"\\n\");\n if (!changed) return;\n if (!(await refreshLock())) throw new Error(\"Lost entity canonical-id migration lock.\");\n if (!(await tombstoneLock.refresh())) throw new Error(\"Lost tombstone migration lock.\");\n await deps.snapshotBeforeWrite(tombstonePath, \"write\");\n await deps.writeStorageSecureFile(tombstonePath, rewritten, tombstoneEncrypted);\n },\n );\n}\n\nasync function rewriteRelationshipTargets(\n deps: EntityCanonicalIdMigrationDependencies,\n mappings: Readonly<Record<string, string>>,\n refreshLock: () => Promise<boolean>\n): Promise<boolean> {\n let rewritten = false;\n const entries = await readSafeEntityEntries(deps.entitiesDir);\n for (const entry of entries) {\n if (!entry.endsWith(\".md\") || isMacOsReplicationJunkBasename(entry)) continue;\n const entityPath = deps.resolveEntityFilePath(entry.slice(0, -\".md\".length));\n if (entityPath === null) continue;\n const observedEntityFingerprint = await fingerprintPath(entityPath);\n if (observedEntityFingerprint === \"missing\") continue;\n const entityRaw = await deps.readStorageSecureFile(entityPath);\n if (isMalformedEntityRecord(entityRaw)) continue;\n const entity = deps.parseEntityFile(entityRaw);\n const relationships = entity.relationships.map((relationship) => {\n const target = mappings[relationship.target];\n return target ? { ...relationship, target } : relationship;\n });\n if (relationships.every((relationship, index) => relationship === entity.relationships[index])) continue;\n const rewroteEntity = await withEntityCanonicalMutationLock(\n deps.stateDir,\n async (refreshEntityLock) => {\n if (!(await refreshLock())) throw new Error(\"Lost entity canonical-id migration lock.\");\n const latestRaw = await deps.readStorageSecureFile(entityPath);\n if (isMalformedEntityRecord(latestRaw)) return false;\n const latestEntity = deps.parseEntityFile(latestRaw);\n if ((await fingerprintPath(entityPath)) !== observedEntityFingerprint) return false;\n const latestRelationships = latestEntity.relationships.map((relationship) => {\n const target = mappings[relationship.target];\n return target ? { ...relationship, target } : relationship;\n });\n if (latestRelationships.every((relationship, index) => relationship === latestEntity.relationships[index])) {\n return false;\n }\n const entityEncrypted = await deps.isEncryptedStorageFile(entityPath);\n if (!(await refreshEntityLock())) throw new Error(\"Lost entity mutation lock.\");\n if ((await fingerprintPath(entityPath)) !== observedEntityFingerprint) return false;\n await deps.snapshotBeforeWrite(entityPath, \"write\");\n if ((await fingerprintPath(entityPath)) !== observedEntityFingerprint) return false;\n await deps.writeStorageSecureFile(\n entityPath,\n deps.serializeEntityFile({ ...latestEntity, relationships: latestRelationships }),\n entityEncrypted,\n );\n return true;\n },\n );\n rewritten ||= rewroteEntity;\n }\n return rewritten;\n}\n\nasync function rewriteKnownReferences(\n deps: EntityCanonicalIdMigrationDependencies,\n mappings: Readonly<Record<string, string>>,\n refreshLock: () => Promise<boolean>,\n): Promise<boolean> {\n await rewriteRelationshipTargets(deps, mappings, refreshLock);\n let rewroteColdMemory = (await rewriteReferences(deps, mappings, refreshLock)).rewroteColdMemory;\n await rewriteTombstoneReferences(deps, mappings, refreshLock);\n await deps.rewriteProjectedEntityReferences(mappings);\n const finalMemoryPass = await rewriteReferences(deps, mappings, refreshLock);\n rewroteColdMemory ||= finalMemoryPass.rewroteColdMemory;\n return rewroteColdMemory;\n}\n\nfunction wouldCreateMappingCycle(\n mappings: Readonly<Record<string, string>>,\n legacyId: string,\n canonicalId: string,\n): boolean {\n let current = canonicalId;\n const seen = new Set<string>();\n while (true) {\n if (current === legacyId) return true;\n if (seen.has(current)) return false;\n seen.add(current);\n const next = mappings[current];\n if (!next || next === current) return false;\n current = next;\n }\n}\n\nfunction rejectMappingCycles(\n existing: Readonly<Record<string, string>>,\n discovered: Readonly<Record<string, string>>,\n): Record<string, string> {\n const accepted: Record<string, string> = {};\n const merged = { ...existing };\n for (const [legacyId, canonicalId] of Object.entries(discovered)) {\n if (\n merged[legacyId] === undefined &&\n wouldCreateMappingCycle(merged, legacyId, canonicalId)\n ) continue;\n accepted[legacyId] = canonicalId;\n merged[legacyId] = canonicalId;\n }\n return accepted;\n}\n\nfunction mergeDiscoveredMappings(\n existing: Readonly<Record<string, string>>,\n discovered: Readonly<Record<string, string>>,\n): Record<string, string> {\n const merged = { ...existing };\n const canonicalOwners = new Map<string, string>();\n for (const [legacyId, canonicalId] of Object.entries(discovered)) {\n const previousMapping = merged[legacyId];\n if (previousMapping !== undefined && previousMapping !== canonicalId) {\n continue;\n }\n if (previousMapping === undefined && wouldCreateMappingCycle(merged, legacyId, canonicalId)) continue;\n const previousOwner = canonicalOwners.get(canonicalId);\n if (previousOwner !== undefined && previousOwner !== legacyId) {\n throw new Error(\n `Cannot migrate legacy entity ids ${previousOwner} and ${legacyId}: both normalize to ${canonicalId}.`,\n );\n }\n merged[legacyId] = canonicalId;\n canonicalOwners.set(canonicalId, legacyId);\n }\n return merged;\n}\nfunction collapseMappings(mappings: Readonly<Record<string, string>>): Record<string, string> {\n const collapsed: Record<string, string> = {};\n for (const legacyId of Object.keys(mappings)) {\n let current = legacyId;\n const seen = new Set<string>();\n while (true) {\n const next = mappings[current];\n if (!next || next === current) break;\n if (seen.has(current)) {\n throw new Error(`Entity canonical-id migration mapping cycle includes ${legacyId}.`);\n }\n seen.add(current);\n current = next;\n }\n if (current !== legacyId) collapsed[legacyId] = current;\n }\n return collapsed;\n}\n\ntype EntityFileMigrationResult = {\n deferred: boolean;\n progressed: boolean;\n /** The pair collided on content; skipped and reported, never migrated. */\n blocked?: boolean;\n};\n\nasync function migrateEntityFilePair(\n deps: EntityCanonicalIdMigrationDependencies,\n legacyId: string,\n canonicalId: string,\n refreshMigrationLock: () => Promise<boolean>,\n): Promise<EntityFileMigrationResult> {\n return withEntityCanonicalMutationLock(deps.stateDir, async (refreshEntityLock) => {\n assertSafeEntityId(legacyId);\n assertSafeEntityId(canonicalId);\n const legacyPath = deps.resolveEntityFilePath(legacyId);\n const canonicalPath = deps.resolveEntityFilePath(canonicalId);\n if (legacyPath === null || canonicalPath === null) {\n throw new Error(\"Invalid entity canonical-id migration path.\");\n }\n await assertNotSymlink(legacyPath, legacyId);\n await assertNotSymlink(canonicalPath, canonicalId);\n const legacyExists = await fileExists(legacyPath);\n const canonicalExists = await fileExists(canonicalPath);\n if (!legacyExists && !canonicalExists) return { deferred: true, progressed: false };\n if (legacyExists && canonicalExists) {\n if (await sameFileIdentity(legacyPath, canonicalPath)) return { deferred: false, progressed: false };\n const [legacyContent, canonicalContent, legacyEncrypted, canonicalEncrypted] = await Promise.all([\n deps.readStorageSecureFile(legacyPath),\n deps.readStorageSecureFile(canonicalPath),\n deps.isEncryptedStorageFile(legacyPath),\n deps.isEncryptedStorageFile(canonicalPath),\n ]);\n if (legacyContent !== canonicalContent) {\n // A mapping persisted by an older build, or a file changed since\n // discovery. Same verdict as discovery: skip the pair, keep both files.\n return { deferred: false, progressed: false, blocked: true };\n }\n if (legacyEncrypted && !canonicalEncrypted) {\n if (!(await refreshMigrationLock()) || !(await refreshEntityLock())) {\n throw new Error(\"Lost entity canonical-id migration lock.\");\n }\n await deps.snapshotBeforeWrite(canonicalPath, \"write\");\n await deps.writeStorageSecureFile(canonicalPath, legacyContent, true);\n if ((await deps.readStorageSecureFile(canonicalPath)) !== legacyContent) {\n throw new Error(`Cannot verify migrated entity id ${canonicalId}.`);\n }\n }\n if (!(await refreshMigrationLock()) || !(await refreshEntityLock())) {\n throw new Error(\"Lost entity canonical-id migration lock.\");\n }\n await deps.snapshotBeforeWrite(legacyPath, \"write\");\n await assertNotSymlink(legacyPath, legacyId);\n await assertEntityContentUnchanged(deps, legacyPath, legacyContent, legacyId);\n await unlink(legacyPath);\n return { deferred: false, progressed: true };\n }\n if (!legacyExists) return { deferred: false, progressed: false };\n if (!(await refreshMigrationLock()) || !(await refreshEntityLock())) {\n throw new Error(\"Lost entity canonical-id migration lock.\");\n }\n const entityEncrypted = await deps.isEncryptedStorageFile(legacyPath);\n const entityContent = await deps.readStorageSecureFile(legacyPath);\n await assertNotSymlink(legacyPath, legacyId);\n await assertEntityContentUnchanged(deps, legacyPath, entityContent, legacyId);\n // A journal entry must not relocate Finder metadata or a record this scan\n // would leave in place. readStorageSecureFile has already decrypted, so\n // the check is on that plaintext. The mapping stays for a later repair.\n if (\n isMacOsReplicationJunkBasename(path.basename(legacyPath))\n || isMalformedEntityRecord(entityContent)\n ) {\n return { deferred: false, progressed: false };\n }\n let canonicalFingerprintAfterWrite: string | null = null;\n try {\n await deps.writeStorageSecureFile(canonicalPath, entityContent, entityEncrypted);\n canonicalFingerprintAfterWrite = await fingerprintPath(canonicalPath);\n if ((await deps.readStorageSecureFile(canonicalPath)) !== entityContent) {\n throw new Error(`Cannot verify migrated entity id ${canonicalId}.`);\n }\n if (!(await refreshMigrationLock()) || !(await refreshEntityLock())) {\n throw new Error(\"Lost entity canonical-id migration lock.\");\n }\n await deps.snapshotBeforeWrite(legacyPath, \"write\");\n await assertNotSymlink(legacyPath, legacyId);\n await assertEntityContentUnchanged(deps, legacyPath, entityContent, legacyId);\n await unlink(legacyPath);\n } catch (error) {\n if (!canonicalExists && canonicalFingerprintAfterWrite !== null) {\n try {\n if ((await fingerprintPath(canonicalPath)) === canonicalFingerprintAfterWrite) {\n await assertNotSymlink(canonicalPath, canonicalId);\n await unlink(canonicalPath);\n }\n } catch {\n // Preserve the migration error when cleanup races another writer.\n }\n }\n throw error;\n }\n return { deferred: false, progressed: true };\n });\n}\n\nexport async function migrateLegacyEntityCanonicalIds(\n deps: EntityCanonicalIdMigrationDependencies,\n): Promise<string | undefined> {\n return withHeldFileLock(\n lockPath(deps),\n {\n staleMs: ENTITY_CANONICAL_ID_MIGRATION_LOCK_STALE_MS,\n maxWaitMs: ENTITY_CANONICAL_ID_MIGRATION_LOCK_MAX_WAIT_MS,\n },\n async (acquired, lock) => {\n if (!acquired) throw new Error(\"Timed out waiting for active entity canonical-id migration.\");\n await deps.validateMemoryScanRoots?.();\n await readSafeEntityEntries(deps.entitiesDir);\n // A raw-byte writer (offline sync, consolidation-undo, governance\n // restore) may have landed memory bytes this process never vetted; the\n // marker requests ONE reference-reconciliation pass (issue #2213). The\n // observed generation is RENAMED aside before the pass so a request made\n // WHILE this run scans creates a fresh marker that survives finish() —\n // and a `.consuming` file left by a crashed run still owes its pass.\n const reconcileMarkerPath = path.join(deps.stateDir, ENTITY_CANONICAL_ID_RECONCILE_MARKER);\n const consumingMarkerPath = path.join(deps.stateDir, ENTITY_CANONICAL_ID_RECONCILE_CONSUMING_MARKER);\n let reconcileRequested = await fileExists(consumingMarkerPath);\n if (await fileExists(reconcileMarkerPath)) {\n try {\n await rename(reconcileMarkerPath, consumingMarkerPath);\n reconcileRequested = true;\n } catch (error) {\n if (!isErrnoCode(error, \"ENOENT\")) throw error;\n }\n }\n let state = await readState(deps);\n if (!state) {\n state = {\n version: ENTITY_CANONICAL_ID_MIGRATION_VERSION,\n complete: false,\n mappings: {},\n };\n }\n const blocked: BlockedEntityPairs = new Map();\n const scanNotes: EntityScanNotes = { junk: new Set(), malformed: new Set() };\n // Parks for pairs whose files are GONE, tracked separately from\n // collision parks: pruneBlocked drops these (the canonical file is gone\n // too), and the rescan's blocked.clear() would otherwise silence them\n // before finish() reports anything.\n const staleParked: BlockedEntityPairs = new Map();\n // ONE aggregated line per run, not one per pair per restart: this used to\n // abort the daemon, so the operator needs the whole list and the remedy.\n const finish = async (): Promise<string | undefined> => {\n if (blocked.size > 0) {\n const pairs = [...blocked].map(([legacyId, canonicalId]) => `${legacyId} -> ${canonicalId}`).join(\", \");\n log.warn(\n `entity canonical-id migration skipped ${blocked.size} unresolvable pair(s): ${pairs}. `\n + \"Both files were kept and the legacy id still resolves; merge or delete one side to migrate it.\",\n );\n }\n if (staleParked.size > 0) {\n const pairs = [...staleParked].map(([legacyId, canonicalId]) => `${legacyId} -> ${canonicalId}`).join(\", \");\n log.warn(\n `entity canonical-id migration dropped ${staleParked.size} mapping(s) whose entity files are gone: ${pairs}. `\n + \"The stale journal entries were removed; references to those legacy ids do not resolve either way.\",\n );\n }\n warnIgnoredMacOsReplicationJunk(deps.entitiesDir, scanNotes.junk.size, (message) => {\n log.warn(`entity canonical-id migration ${message}`);\n });\n if (scanNotes.malformed.size > 0) {\n const names = [...scanNotes.malformed].sort().join(\", \");\n log.warn(\n `entity canonical-id migration skipped ${scanNotes.malformed.size} malformed entity file(s): ${names}. `\n + \"Each file was left in place; startup continues. Fix or remove the file to migrate it.\",\n );\n }\n // Every finish() path either just rewrote references (main/reconcile\n // paths), has none to rewrite (empty mappings), or observed no marker\n // (stable no-op) — so the CONSUMED generation is removed here. A fresh\n // marker written mid-run is untouched, and a crash before this point\n // leaves `.consuming` set for the next run (the rewrite is idempotent).\n if (reconcileRequested) {\n await unlink(consumingMarkerPath).catch((error: unknown) => {\n if (!isErrnoCode(error, \"ENOENT\")) throw error;\n });\n }\n // A FRESH marker written while this run scanned means bytes landed the\n // pass may not have covered. Withholding the completion fingerprint\n // makes the runner's stability check fail, so it immediately retries —\n // and the retry consumes the fresh generation. ORDER MATTERS: read the\n // fingerprint BEFORE the marker check. A marker landing after the\n // fingerprint read but before the check is caught by the check; one\n // landing after the check is absent from the returned fingerprint, so\n // the live marker mismatches the runner's cached completion and the\n // next ensure() re-invokes. Checking first left a window where the\n // fingerprint could SEAL a marker the check never saw.\n const completionFingerprint = await deps.readMigrationFingerprint?.();\n if (await fileExists(reconcileMarkerPath)) return undefined;\n return completionFingerprint;\n };\n /**\n * Drop blocked pairs from the working set. Discovery only refuses to\n * ADD them; a mapping persisted by an earlier run would otherwise still\n * be applied - migrating one claimant and rewriting every reference onto\n * the canonical file, i.e. picking the winner this fix exists to avoid,\n * off stale state rather than what is on disk.\n */\n /** @returns true when a park was promoted back into the mapping set. */\n const pruneBlocked = async (): Promise<boolean> => {\n const current = state;\n if (!current) return false;\n // THIS scan's verdict wins over the journal: normalization can change\n // between runs, so a stale parked target would otherwise be promoted\n // later and rewrite references onto an id that does not exist.\n let revived = false;\n const parked: Record<string, string> = { ...(current.blocked ?? {}) };\n const active: Record<string, string> = {};\n for (const [legacyId, canonicalId] of Object.entries(current.mappings)) {\n if (!blocked.has(legacyId)) active[legacyId] = canonicalId;\n }\n for (const [legacyId, canonicalId] of blocked) parked[legacyId] = canonicalId;\n // A parked pair whose legacy file is gone was resolved by keeping the\n // canonical side. The rename is moot; the reference rewrite is not, and\n // this record is the only thing that can still perform it.\n // A park survives ONLY while this scan re-establishes the collision.\n // Anything else - the scan found a real migration, or found the source\n // needs none (already canonical under a newer normalization) - makes\n // the record obsolete, and keeping it lets a later deletion promote a\n // target the entity never occupied.\n for (const [legacyId, canonicalId] of Object.entries(parked)) {\n if (blocked.has(legacyId)) continue;\n delete parked[legacyId];\n if (active[legacyId] !== undefined) continue;\n const legacyPath = deps.resolveEntityFilePath(legacyId);\n if (legacyPath !== null && (await fileExists(legacyPath))) continue;\n // Resolve the parked target through ACTIVE moves before deciding\n // its file is gone: a parked A -> B beside an active B -> C must\n // promote to A -> C and rewrite, not drop because B was moved\n // out-of-band. Only a target with no surviving file may drop.\n let target = canonicalId;\n const seenTargets = new Set<string>();\n while (active[target] !== undefined && !seenTargets.has(target)) {\n seenTargets.add(target);\n target = active[target]!;\n }\n const targetPath = deps.resolveEntityFilePath(target);\n if (targetPath === null || !(await fileExists(targetPath))) {\n staleParked.set(legacyId, canonicalId);\n continue;\n }\n active[legacyId] = target;\n revived = true;\n }\n // Reviving a whole chain at once yields A -> B -> C, and each rewrite\n // surface makes a different number of passes over it - so collapse\n // transitively before anything consumes the set.\n const collapsed = collapseMappings(active);\n // Parked targets follow ACTIVE moves only: hopping over a still-blocked\n // link would jump to an id the migration has not been allowed to reach.\n for (const [legacyId, canonicalId] of Object.entries(parked)) {\n const moved = collapsed[canonicalId];\n if (moved !== undefined && moved !== legacyId) parked[legacyId] = moved;\n }\n const same = (a: Record<string, string>, b: Record<string, string>): boolean =>\n Object.keys(a).length === Object.keys(b).length\n && Object.entries(a).every(([key, value]) => b[key] === value);\n if (same(collapsed, current.mappings) && same(parked, current.blocked ?? {})) return revived;\n // Parking/removing an active mapping mid-entity-write would land an\n // alias/activity/relationship mutation on a now-unrelated canonical\n // claimant — the reconcile pass rewrites references, it cannot move\n // a misplaced mutation. publishState serializes under the lock.\n state = { ...current, mappings: collapsed, blocked: parked };\n await publishState(deps, state);\n return revived;\n };\n const previousMappings = state.mappings;\n const collapsedMappings = collapseMappings(previousMappings);\n const mappingsChanged =\n Object.keys(collapsedMappings).length !== Object.keys(previousMappings).length\n || Object.entries(collapsedMappings).some(([legacyId, canonicalId]) =>\n previousMappings[legacyId] !== canonicalId\n );\n state = { ...state, mappings: collapsedMappings };\n for (let migrationPass = 0; migrationPass < ENTITY_MAPPING_RESCAN_MAX_PASSES; migrationPass += 1) {\n if (!(await lock.refresh())) throw new Error(\"Lost entity canonical-id migration lock.\");\n const discoveredBefore = rejectMappingCycles(\n state.mappings,\n await discoverMappings(deps, state.mappings, blocked, scanNotes),\n );\n const revivedBefore = await pruneBlocked();\n const mergedBefore = collapseMappings(mergeDiscoveredMappings(state.mappings, discoveredBefore));\n // Stable completed journal → no-op (issue #2213). Retained mappings are\n // read-compat state whose references were already rewritten (to\n // convergence) before `complete: true` was persisted, so re-running\n // `rewriteKnownReferences` here re-read the ENTIRE hot+cold+archived\n // corpus — twice — and invalidated every memory cache on EVERY\n // `ensureDirectories()`. On a write-active daemon that was a\n // self-sustaining hot loop (multi-GB/s of re-reads, caches never warm,\n // recall degraded to the fallback path). A park revived by THIS run's\n // pruneBlocked still owes its reference rewrite: it falls through to\n // the main flow below, which demotes `complete` BEFORE rewriting — so\n // a crash mid-rewrite resumes, which the old in-place rewrite did not.\n if (\n state.complete\n && Object.keys(discoveredBefore).length === 0\n && !mappingsChanged\n && !revivedBefore\n ) {\n // Stable journal, but a raw-byte writer requested reconciliation:\n // run ONE bounded rewrite pass over the retained mappings, then let\n // finish() consume the marker.\n if (reconcileRequested && Object.keys(state.mappings).length > 0) {\n const rewroteColdMemory = await rewriteKnownReferences(\n deps,\n state.mappings,\n () => lock.refresh(),\n );\n deps.invalidateKnowledgeIndexCache();\n deps.invalidateAllMemoriesCache();\n if (rewroteColdMemory) deps.invalidateColdMemoriesCache();\n deps.bumpMemoryStatusVersion();\n }\n return finish();\n }\n if (\n state.complete\n || Object.keys(discoveredBefore).length > 0\n || Object.keys(mergedBefore).length !== Object.keys(state.mappings).length\n ) {\n state = { ...state, complete: false, mappings: mergedBefore };\n await publishState(deps, state);\n }\n if (Object.keys(state.mappings).length === 0) {\n if (!(await lock.refresh())) throw new Error(\"Lost entity canonical-id migration lock.\");\n const discoveredAfter = rejectMappingCycles(\n state.mappings,\n await discoverMappings(deps, state.mappings, blocked, scanNotes),\n );\n // Reconcile before declaring completion: this rescan can resolve a\n // park (another writer removed the legacy file), and completing\n // without promoting it strands its references behind a fingerprint\n // that already reflects the deletion - so no later run revisits it.\n const revivedInRescan = await pruneBlocked();\n if (Object.keys(discoveredAfter).length === 0 && !revivedInRescan) {\n await publishState(deps, { ...state, complete: true });\n return finish();\n }\n state = {\n ...state,\n complete: false,\n mappings: collapseMappings(mergeDiscoveredMappings(state.mappings, discoveredAfter)),\n };\n await publishState(deps, state);\n continue;\n }\n let pendingMappings = Object.entries(state.mappings);\n while (pendingMappings.length > 0) {\n const deferredMappings: Array<[string, string]> = [];\n let progressed = false;\n for (const [legacyId, canonicalId] of pendingMappings) {\n const result = await migrateEntityFilePair(\n deps,\n legacyId,\n canonicalId,\n () => lock.refresh(),\n );\n if (result.blocked) blocked.set(legacyId, canonicalId);\n else if (result.deferred) deferredMappings.push([legacyId, canonicalId]);\n progressed ||= result.progressed;\n }\n if (deferredMappings.length === 0) break;\n if (!progressed) {\n // Deferred means both files are missing: no later pass can help\n // these pairs, so park them all instead of Fatal-exiting.\n for (const [legacyId, canonicalId] of deferredMappings) {\n blocked.set(legacyId, canonicalId);\n staleParked.set(legacyId, canonicalId);\n }\n break;\n }\n pendingMappings = deferredMappings;\n }\n await pruneBlocked();\n\n const rewroteColdMemory = await rewriteKnownReferences(\n deps,\n state.mappings,\n () => lock.refresh(),\n );\n deps.invalidateKnowledgeIndexCache();\n deps.invalidateAllMemoriesCache();\n if (rewroteColdMemory) deps.invalidateColdMemoriesCache();\n deps.bumpMemoryStatusVersion();\n\n if (!(await lock.refresh())) throw new Error(\"Lost entity canonical-id migration lock.\");\n const discoveredAfter = rejectMappingCycles(\n state.mappings,\n await discoverMappings(deps, state.mappings, blocked, scanNotes),\n );\n // A park resolved after the rewrite pass is promoted here, and a\n // promotion still has to be APPLIED. Completing on `discoveredAfter`\n // alone fingerprints the post-deletion corpus, so the runner never\n // retries and those references stay stranded.\n const revivedAfterRewrite = await pruneBlocked();\n if (Object.keys(discoveredAfter).length === 0 && !revivedAfterRewrite) {\n await publishState(deps, { ...state, complete: true });\n return finish();\n }\n state = {\n ...state,\n complete: false,\n mappings: collapseMappings(mergeDiscoveredMappings(state.mappings, discoveredAfter)),\n };\n await publishState(deps, state);\n }\n throw new Error(\"Entity canonical-id migration mappings changed while migration was running; retry migration.\");\n }\n );\n}\n"],"mappings":"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","names":["rewroteColdMemory","discoveredAfter"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/embedding-index-storage.ts"],"sourcesContent":["/**\n * File-level storage for the embedding fallback index (issue #3146).\n *\n * Two single-generation layouts, exactly one authoritative at a time:\n * - `legacy`: the single `state/embeddings.json` file, used while the\n * whole index serializes within `resolveIndexFileCharLimit()` chars;\n * - `sharded`: the published `state/embeddings/` directory of hash-assigned\n * shard files, migrated to ONE-WAY and ATOMICALLY (stage a complete shard\n * set, publish with a single directory rename, demote the legacy file to\n * a non-authoritative recovery artifact). Generations never merge, so a\n * crash cannot resurrect deleted entries or drop published ones.\n *\n * This module owns files, budgets, and durable diagnostics only — no index\n * caching or provider logic (that lives in `embedding-fallback.ts`).\n */\nimport path from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { constants as bufferConstants } from \"node:buffer\";\nimport { lstat, mkdir, mkdtemp, realpath, readFile, readdir, rename, rm, stat, writeFile } from \"node:fs/promises\";\nimport { log } from \"./logger.js\";\nimport { readEnvVar } from \"./runtime/env.js\";\nimport type { EmbeddingGenerationFence } from \"./embedding-generation-lock.js\";\n\nexport type EmbeddingProviderType = \"openai\" | \"local\" | \"host\";\n\nexport type EmbeddingIndexEntry = {\n vector: number[];\n path: string;\n};\n\nexport type EmbeddingIndexFile = {\n version: 1;\n provider: EmbeddingProviderType;\n model: string;\n entries: Record<string, EmbeddingIndexEntry>;\n};\n\nexport type EmbeddingIndexIdentity = Pick<EmbeddingIndexFile, \"provider\" | \"model\">;\n\n/** Outcome of reading one managed index file (legacy or shard). */\nexport type ManagedIndexRead =\n | { outcome: \"ok\"; file: EmbeddingIndexFile }\n | { outcome: \"absent\" }\n | { outcome: \"foreign\" }\n | { outcome: \"unreadable\"; reason: string };\n\n/** Which generation owns the index right now. */\nexport type IndexLayout = \"empty\" | \"legacy\" | \"sharded\";\n\n/**\n * Thrown when an embedding index (or one of its shards) cannot be persisted\n * within the per-file serialization budget. Issue #3146: index files are\n * materialized as single V8 strings, so a JSON document at or past\n * `bufferConstants.MAX_STRING_LENGTH` chars makes every read and write throw\n * `RangeError: Invalid string length`. The index is sharded before that\n * ceiling; this tagged error is reserved for the residual case where even\n * one shard cannot hold the payload (e.g. a single entry with an enormous\n * vector), so callers can classify it as recoverable-with-record instead of\n * an opaque background failure.\n */\nexport class EmbeddingIndexCapacityError extends Error {\n override readonly name = \"EmbeddingIndexCapacityError\" as const;\n constructor(\n message: string,\n readonly memoryId?: string,\n ) {\n super(message);\n }\n}\n\n/**\n * Thrown when an index file that should be authoritative cannot be read\n * (I/O error, over-budget size, unparseable JSON). Mutation paths fail\n * CLOSED on this: they must not continue with a fresh empty index and\n * pretend the previous vectors were loaded — the file is left untouched in\n * place for recovery. Read-only paths (recall) fail open to empty results\n * and record the reason durably.\n */\nexport class EmbeddingIndexStorageError extends Error {\n override readonly name = \"EmbeddingIndexStorageError\" as const;\n}\n\n/** Durable diagnostic row describing the last failed embedding-index write. */\nexport interface EmbeddingIndexWriteFailure {\n ts: string;\n kind: \"capacity\" | \"io\";\n message: string;\n memoryId?: string;\n}\n\n/**\n * Durable embedding-index diagnostics (issue #3146), persisted at\n * `<memoryDir>/state/embedding-fallback-status.json` and surfaced through\n * console_state, so background ingestion failures are visible to operators\n * instead of only ever appearing as log lines.\n */\nexport interface EmbeddingIndexStatusFile {\n version: 1;\n /** Cumulative count of failed index writes since the file was created. */\n failureCount: number;\n /** Active write failure; cleared by the next successful save. */\n lastWriteFailure?: EmbeddingIndexWriteFailure;\n /** Set when an unreadable index file was detected instead of overwritten. */\n lastReadRecovery?: { ts: string; message: string };\n lastSuccessAt?: string;\n}\n\n/**\n * Per-file serialization budget (JSON-string chars) for WRITES of the legacy\n * `state/embeddings.json` file and of every shard under `state/embeddings/`.\n * The 16MiB margin keeps the write round-trip clear of the V8 single-string\n * ceiling even with UTF-8 byte/count skew and small header growth. Override\n * per environment (tests, headroom tuning) via\n * `REMNIC_EMBEDDING_INDEX_FILE_CHAR_LIMIT`.\n *\n * READS are gated by the HARD ceiling (`resolveIndexHardReadCharLimit`),\n * never by this budget: a legacy index written by an older version may\n * legitimately sit between the soft budget and the hard ceiling (the\n * reported production file does), and it must still load and migrate.\n */\nconst DEFAULT_INDEX_FILE_CHAR_LIMIT =\n bufferConstants.MAX_STRING_LENGTH - 16 * 1024 * 1024;\n\n/**\n * Hard single-string ceiling for READS: files at or above this size are not\n * attempted (a readFile would throw `RangeError: Invalid string length`);\n * their bytes are preserved in place and mutations fail closed with an\n * actionable error. Defaults to the V8 maximum; tests inject a small value\n * via `REMNIC_EMBEDDING_INDEX_HARD_READ_LIMIT_CHARS`.\n */\nfunction resolveIndexHardReadCharLimit(): number {\n const soft = resolveIndexFileCharLimit();\n const raw = readEnvVar(\"REMNIC_EMBEDDING_INDEX_HARD_READ_LIMIT_CHARS\");\n if (raw) {\n const parsed = Number(raw);\n if (Number.isInteger(parsed) && parsed >= soft) return parsed;\n }\n return bufferConstants.MAX_STRING_LENGTH;\n}\n\n/** Hash shards under `state/embeddings/` used once the budget is exceeded. */\nconst SHARD_COUNT = 64;\n\nconst SHARD_FILE_PATTERN = /^shard-\\d{4}\\.json$/;\n\nexport function resolveIndexFileCharLimit(): number {\n const raw = readEnvVar(\"REMNIC_EMBEDDING_INDEX_FILE_CHAR_LIMIT\");\n if (raw) {\n const parsed = Number(raw);\n if (Number.isInteger(parsed) && parsed >= 1024) return parsed;\n }\n return DEFAULT_INDEX_FILE_CHAR_LIMIT;\n}\n\nfunction shardFileName(shardIndex: number): string {\n return `shard-${String(shardIndex).padStart(4, \"0\")}.json`;\n}\n\n/**\n * Fixed transaction backup path for identity replacements. A fixed (not\n * per-process) name is what makes the rename gap recoverable: whatever\n * process restarts after a crash finds the demoted former generation here\n * and rolls it back into place.\n */\nfunction replacementBackupPath(shardDir: string): string {\n return path.join(path.dirname(shardDir), \"embeddings.pre-replace.tmp\");\n}\n\n/**\n * Warm-cache generation marker (`<stateDir>/embeddings.generation`).\n * Two-phase publication barrier: in-flight before a publication's first\n * destructive write, fresh unique value after its last write. A stamp is\n * therefore stable only for a completed generation — readers can never\n * cache a stamp that stays stable while generation bytes change, and an\n * in-flight stamp never repeats, so probes cached during a failed\n * completion keep revalidating (PR #3176 codex P2). Plain fs like the\n * status file.\n */\nconst GENERATION_MARKER_BASENAME = \"embeddings.generation\";\n\n/** Reserved in-flight marker value; never equals a completion marker. */\nconst GENERATION_MARKER_IN_FLIGHT = \"in-flight\";\n\n/**\n * Stable FNV-1a shard assignment: an entry always hashes to the same shard,\n * so an update or removal never needs a manifest to find its file.\n */\nexport function shardIndexOf(memoryId: string, shardCount: number): number {\n let hash = 0x811c9dc5;\n for (let i = 0; i < memoryId.length; i++) {\n hash ^= memoryId.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193) >>> 0;\n }\n return hash % shardCount;\n}\n\n/**\n * A shard file named `shard-NNNN.json` must only contain ids that hash to\n * that shard: a misplaced copy would be loaded today and silently resurrect\n * stale vectors after the computed shard is rewritten (issue #3148, round 6).\n * Throws the tagged storage error so mutations fail closed and recall fails\n * open at its boundary.\n */\nexport function validateShardMembership(shardName: string, file: EmbeddingIndexFile): void {\n const match = /^shard-(\\d{4})\\.json$/.exec(shardName);\n if (!match) return; // not a shard file (legacy documents have no constraint)\n const shardIndex = Number(match[1]);\n for (const id of Object.keys(file.entries)) {\n if (shardIndexOf(id, SHARD_COUNT) !== shardIndex) {\n throw new EmbeddingIndexStorageError(\n `refusing embedding index shard ${shardName}: id ${id} hashes to shard ${shardIndexOf(id, SHARD_COUNT)}, not ${shardIndex}; file preserved in place`,\n );\n }\n }\n}\n\nexport function isInvalidStringLengthError(err: unknown): boolean {\n return err instanceof RangeError && /invalid string length/i.test(err.message);\n}\n\n/**\n * Parse and FULLY validate one serialized index document (legacy or shard).\n * The single read boundary shared by disk reads and offline-sync pulls: a\n * document with a valid header but a malformed body (`entries` not a plain\n * record, or an entry whose `path` is not a string / whose `vector` is not a\n * finite-number array) is `unreadable`, not `ok` — recall fails open on the\n * tagged reason, mutations fail closed, and the bytes are never overwritten\n * (issue #3148 review, round 4).\n */\nexport function parseEmbeddingIndexDocument(raw: string): ManagedIndexRead {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n return {\n outcome: \"unreadable\",\n reason: `JSON parse failed: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n if (\n !parsed ||\n typeof parsed !== \"object\" ||\n Array.isArray(parsed) ||\n (parsed as Partial<EmbeddingIndexFile>).version !== 1 ||\n typeof (parsed as Partial<EmbeddingIndexFile>).provider !== \"string\" ||\n typeof (parsed as Partial<EmbeddingIndexFile>).model !== \"string\" ||\n ((parsed as Partial<EmbeddingIndexFile>).model as string).length === 0\n ) {\n return { outcome: \"foreign\" };\n }\n // An unsupported identity must fail closed like any other malformed\n // document: recall cannot serve it and mutations must not adopt it as\n // authoritative (issue #3148, round 6).\n if (\n (parsed as Partial<EmbeddingIndexFile>).provider !== \"openai\" &&\n (parsed as Partial<EmbeddingIndexFile>).provider !== \"local\" &&\n (parsed as Partial<EmbeddingIndexFile>).provider !== \"host\"\n ) {\n return { outcome: \"foreign\" };\n }\n const entries = (parsed as Partial<EmbeddingIndexFile>).entries;\n if (!entries || typeof entries !== \"object\" || Array.isArray(entries)) {\n return {\n outcome: \"unreadable\",\n reason: \"entries must be a plain record of memory id -> entry\",\n };\n }\n for (const [id, entry] of Object.entries(entries as Record<string, unknown>)) {\n if (!entry || typeof entry !== \"object\" || Array.isArray(entry)) {\n return { outcome: \"unreadable\", reason: `entry ${id} must be an object` };\n }\n const candidate = entry as Partial<EmbeddingIndexEntry>;\n if (typeof candidate.path !== \"string\") {\n return { outcome: \"unreadable\", reason: `entry ${id}.path must be a string` };\n }\n if (!Array.isArray(candidate.vector)) {\n return { outcome: \"unreadable\", reason: `entry ${id}.vector must be an array` };\n }\n for (const component of candidate.vector) {\n if (!Number.isFinite(component)) {\n return {\n outcome: \"unreadable\",\n reason: `entry ${id}.vector must contain only finite numbers`,\n };\n }\n }\n }\n return { outcome: \"ok\", file: parsed as EmbeddingIndexFile };\n}\n\n/**\n * Group a complete index into per-shard entry records using the stable FNV\n * assignment. Shared by `persist()` and the offline-sync generation\n * transaction so a converted generation shards identically everywhere.\n */\nexport function shardEntriesForIndex(\n index: EmbeddingIndexFile,\n): Map<number, Record<string, EmbeddingIndexEntry>> {\n const groups = new Map<number, Record<string, EmbeddingIndexEntry>>();\n for (const id of Object.keys(index.entries)) {\n const shardIndex = shardIndexOf(id, SHARD_COUNT);\n const group = groups.get(shardIndex);\n if (group) group[id] = index.entries[id];\n else groups.set(shardIndex, { [id]: index.entries[id] });\n }\n return groups;\n}\n\n/**\n * Serialize one shard, translating a ceiling overflow into a tagged,\n * recoverable capacity error. Exported for the offline-sync generation\n * transaction, which stages incoming generations through the same budget.\n */\nexport function serializeEmbeddingShard(\n index: EmbeddingIndexFile,\n shardIndex: number,\n entries: Record<string, EmbeddingIndexEntry>,\n limit: number = resolveIndexFileCharLimit(),\n): string {\n let body: string;\n try {\n body = JSON.stringify({\n version: 1 as const,\n provider: index.provider,\n model: index.model,\n entries,\n });\n } catch (err) {\n throw new EmbeddingIndexCapacityError(\n `embedding index shard ${shardFileName(shardIndex)} exceeded the single-string ceiling while serializing: ${err instanceof Error ? err.message : String(err)}`,\n largestEntryId(entries) || undefined,\n );\n }\n if (body.length > limit) {\n const worstId = largestEntryId(entries);\n const worstLength = worstId && entries[worstId] ? JSON.stringify(entries[worstId]).length : 0;\n throw new EmbeddingIndexCapacityError(\n `embedding index shard ${shardFileName(shardIndex)} needs ${body.length} chars, over the ${limit}-char single-file limit; largest entry ${worstId || \"(unknown)\"} serializes to ${worstLength} chars`,\n worstId || undefined,\n );\n }\n return body;\n}\n\nfunction largestEntryId(entries: Record<string, EmbeddingIndexEntry>): string {\n let worstId = \"\";\n let worstLength = 0;\n for (const [id, entry] of Object.entries(entries)) {\n let length: number;\n try {\n length = JSON.stringify(entry).length;\n } catch {\n return id;\n }\n if (length > worstLength) {\n worstLength = length;\n worstId = id;\n }\n }\n return worstId;\n}\n\n/**\n * Optional canonical secure-store IO for the daemon. When provided (both\n * halves), file content is read/written through the SAME\n * StorageManager-backed contract offline sync uses: canonical-path AAD,\n * secure-key-aware, atomic. Shard STAGING writes carry the FINAL published\n * path so the post-publish rename keeps the ciphertext decryptable; dirty\n * shard, status, and legacy writes are their own canonical final path.\n * When omitted, the store behaves exactly as before (raw plaintext fs).\n * The diagnostics status file is intentionally ALWAYS plain: it holds no\n * memory content (failure strings/counts only) and console_state reads it\n * without unlocking the store.\n */\nexport interface EmbeddingIndexStoreIo {\n readUtf8(filePath: string): Promise<string>;\n writeUtf8(filePath: string, contents: string, opts?: { finalAadFilePath?: string }): Promise<void>;\n}\n\nexport class EmbeddingIndexFileStore {\n constructor(\n private readonly indexPath: string,\n private readonly shardDir: string,\n private readonly statusPath: string,\n private readonly io?: EmbeddingIndexStoreIo,\n ) {\n if (io && (Boolean(io.readUtf8) !== Boolean(io.writeUtf8))) {\n throw new EmbeddingIndexStorageError(\n \"embedding index store io requires both readUtf8 and writeUtf8; a half-wired io would mix encrypted and plaintext files\",\n );\n }\n }\n\n /** Legacy single-file layout path (public for error messages). */\n get legacyPath(): string {\n return this.indexPath;\n }\n\n /** Record an unreadable-authoritative-file reason (both read and mutation paths). */\n async recordIndexStatusForLoad(reason: string): Promise<void> {\n await this.recordIndexStatus({\n lastReadRecovery: { ts: new Date().toISOString(), message: reason },\n });\n }\n\n /**\n * Which generation owns the index right now. The published\n * `state/embeddings/` directory IS the layout marker: until it exists the\n * legacy file is authoritative; from the atomic rename onward it never\n * merges again.\n */\n async detectLayout(): Promise<IndexLayout> {\n // An interrupted identity replacement leaves the published directory\n // absent with the fixed transaction backup still on disk (the publish\n // rename never completed). Report SHARDED for that gap: the backup holds\n // the authoritative generation, so neither the legacy file nor a fresh\n // index may win, and reads fail open to empty until the mutation queue\n // performs the rollback. The rollback itself only ever runs inside\n // persist() — a write — so read paths never race an in-flight\n // replacement with a recovery rename (issue #3148 review).\n try {\n // lstat, NOT stat: a symlinked generation directory must be rejected\n // outright — following it would read or write OUTSIDE the state\n // directory through the link (codex PRRT_kwDORJXyws6mZ72r).\n const info = await lstat(this.shardDir);\n if (info.isSymbolicLink()) {\n throw new EmbeddingIndexStorageError(\n `embedding index shard directory is a symlink; refusing to read or write through it: ${this.shardDir}`,\n );\n }\n return \"sharded\";\n } catch (err) {\n if (err instanceof EmbeddingIndexStorageError) throw err;\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n // The pointer exists but cannot be stat'ed — fail towards the\n // published generation so its readers surface the I/O error.\n log.warn(`embedding index: shard dir stat failed (${err instanceof Error ? err.message : String(err)}); assuming sharded layout`);\n return \"sharded\";\n }\n if (await this.replacementBackupExists()) return \"sharded\";\n return this.legacyOrEmptyLayout();\n }\n }\n\n /**\n * Roll the fixed transaction backup back into the published position.\n * Returns true when a rollback happened.\n */\n private async replacementBackupExists(): Promise<boolean> {\n const backupPath = replacementBackupPath(this.shardDir);\n try {\n await stat(backupPath);\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n // An unreadable backup must not be silently treated as absent —\n // that would permit the legacy fallback over a generation we cannot\n // inspect (issue #3148 review, round 1).\n throw new EmbeddingIndexStorageError(\n `cannot stat embedding index replacement backup ${backupPath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n /**\n * The fixed transaction backup is a path with well-known semantics; a\n * planted symlink must never be restored into the published position (it\n * would point the generation outside the memory dir) nor removed through\n * as if it were the former generation. Symlinks are rejected outright.\n */\n private async assertBackupNotSymlink(backupPath: string): Promise<void> {\n const info = await lstat(backupPath).catch((err) => {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw err;\n });\n if (info?.isSymbolicLink()) {\n throw new EmbeddingIndexStorageError(\n `embedding index replacement backup is a symlink; refusing to touch it: ${backupPath}`,\n );\n }\n }\n\n /**\n * Mutation-queue entry point: roll the fixed transaction backup back into\n * the published position after an interrupted replacement. Returns true\n * when a rollback happened. Only fires in the actual rename gap\n * (published directory absent); callers must invalidate any cached index\n * view when this returns true. A successful rollback finalizes the\n * generation marker strictly: a failed completion write fails the\n * recovery instead of leaving every search reloading forever.\n */\n async recoverIfInterrupted(fence?: EmbeddingGenerationFence): Promise<boolean> {\n // Only the rename gap (published directory absent) is recoverable. When\n // the directory is present - including after a crash that followed a\n // successful publish - the generation on disk is already authoritative\n // and renaming the backup onto it would fail with EEXIST (issue #3148\n // review, round 2).\n try {\n await stat(this.shardDir);\n return false;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n log.warn(`embedding index: recovery skipped because shard dir stat failed: ${err instanceof Error ? err.message : String(err)}`);\n return false;\n }\n }\n const backupPath = replacementBackupPath(this.shardDir);\n if (!(await this.replacementBackupExists())) return false;\n await this.assertBackupNotSymlink(backupPath);\n // The rollback rename is destructive: reassert lock ownership first.\n await fence?.();\n await rename(backupPath, this.shardDir);\n await this.writeGenerationMarker(randomUUID());\n log.warn(\n `embedding index: recovered interrupted replacement; former generation restored from ${backupPath}`,\n );\n await this.recordIndexStatus({\n lastReadRecovery: {\n ts: new Date().toISOString(),\n message: \"interrupted identity replacement rolled back to the former generation\",\n },\n });\n return true;\n }\n\n private async legacyOrEmptyLayout(): Promise<IndexLayout> {\n try {\n await stat(this.indexPath);\n return \"legacy\";\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return \"empty\";\n throw err;\n }\n }\n\n /** Barrier first half: strict, so bytes can never change under a stable stamp. */\n private async beginGenerationMarker(): Promise<void> {\n await this.writeGenerationMarker(GENERATION_MARKER_IN_FLIGHT);\n }\n\n /**\n * Barrier second half: written after the last content write. Best-effort:\n * a failure leaves the in-flight marker, whose stamp identityStamp()\n * never repeats, so cached probes revalidate on every search until a\n * publication completes.\n */\n private async completeGenerationMarker(): Promise<void> {\n await this.writeGenerationMarker(randomUUID()).catch((err) => {\n log.warn(`embedding index: could not write generation completion marker: ${err instanceof Error ? err.message : String(err)}`);\n });\n }\n\n private async writeGenerationMarker(value: string): Promise<void> {\n await this.writeAtomicFile(\n path.join(path.dirname(this.shardDir), GENERATION_MARKER_BASENAME),\n value,\n { bypassSecureIo: true },\n );\n }\n\n /** Marker value, or null when absent/unreadable (pre-marker stores keep the stat stamp). */\n private async readGenerationMarker(): Promise<string | null> {\n try {\n const raw = await readFile(\n path.join(path.dirname(this.shardDir), GENERATION_MARKER_BASENAME),\n \"utf-8\",\n );\n const value = raw.trim();\n return value.length > 0 ? value : null;\n } catch {\n return null;\n }\n }\n\n /**\n * Warm-cache stamp. A completed generation marker alone would freeze the\n * stamp for a pre-marker writer in a rolling deployment (it publishes\n * shard/index changes without updating the marker), so the marker is\n * combined with the watched path's stat: the marker keeps consecutive\n * publications distinct on coarse-tick filesystems, the stat keeps\n * legacy writers visible. In-flight never repeats, forcing revalidation\n * until a publication completes. Stores never published through this\n * module keep the plain stat stamp — a directory stat alone aliases\n * consecutive publications on coarse-tick filesystems and across inode\n * reuse.\n */\n async identityStamp(): Promise<string> {\n const layout = await this.detectLayout();\n if (layout === \"sharded\") {\n const info = await lstat(this.shardDir).catch((err) => {\n // SAFETY: only the errno code is inspected; any other rejection shape rethrows uninterpreted.\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw new EmbeddingIndexStorageError(\n `cannot stat embedding index shard directory ${this.shardDir}: ${err instanceof Error ? err.message : String(err)}`,\n );\n });\n if (!info) return \"rename-gap\";\n const marker = await this.readGenerationMarker();\n // An in-flight marker must never produce a repeatable stamp: a probe\n // that cached one (completion write failed) must revalidate on every\n // later probe until a publication completes.\n if (marker) {\n if (marker === GENERATION_MARKER_IN_FLIGHT) return `gen:${randomUUID()}`;\n return `gen:${marker}:${info.ino}:${info.mtimeMs}`;\n }\n return `shard:${info.ino}:${info.mtimeMs}`;\n }\n if (layout === \"legacy\") {\n const info = await stat(this.indexPath).catch((err) => {\n // SAFETY: only the errno code is inspected; any other rejection shape rethrows uninterpreted.\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw new EmbeddingIndexStorageError(\n `cannot stat embedding index file ${this.indexPath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n });\n if (!info) return \"rename-gap\";\n const marker = await this.readGenerationMarker();\n if (marker) {\n if (marker === GENERATION_MARKER_IN_FLIGHT) return `gen:${randomUUID()}`;\n return `gen:${marker}:${info.ino}:${info.mtimeMs}`;\n }\n return `legacy:${info.ino}:${info.mtimeMs}`;\n }\n return \"empty\";\n }\n\n /** Header identity of the authoritative generation, read leniently. */\n async identityFromDisk(): Promise<EmbeddingIndexIdentity | null> {\n const layout = await this.detectLayout();\n if (layout === \"sharded\") {\n // Shard files are bounded by the per-file budget, so the identity\n // probe never reads a ceiling-sized string.\n for (const name of await this.readShardNames()) {\n const read = await this.readFileAt(path.join(this.shardDir, name));\n if (read.outcome === \"ok\") {\n return { provider: read.file.provider, model: read.file.model };\n }\n }\n return null;\n }\n if (layout === \"legacy\") {\n const read = await this.readFileAt(this.indexPath);\n if (read.outcome === \"ok\") {\n return { provider: read.file.provider, model: read.file.model };\n }\n }\n return null;\n }\n\n /**\n * Load every readable shard into `merged`. Returns the generation identity.\n * Unreadable shards record durable diagnostics; `strict` additionally\n * fails the mutation closed.\n */\n async readShardGenerationInto(\n merged: Record<string, EmbeddingIndexEntry>,\n ): Promise<EmbeddingIndexIdentity | null> {\n let identity: EmbeddingIndexIdentity | null = null;\n for (const name of await this.readShardNames()) {\n const read = await this.readFileAt(path.join(this.shardDir, name));\n if (read.outcome !== \"ok\") {\n if (read.outcome === \"unreadable\") {\n await this.recordIndexStatus({\n lastReadRecovery: { ts: new Date().toISOString(), message: `${name}: ${read.reason}` },\n });\n throw new EmbeddingIndexStorageError(\n `refusing to continue from unreadable embedding index shard ${name}: ${read.reason}`,\n );\n }\n await this.recordIndexStatus({\n lastReadRecovery: { ts: new Date().toISOString(), message: `${name}: unrecognized index format` },\n });\n throw new EmbeddingIndexStorageError(\n `refusing to continue from malformed embedding index shard ${name} (unrecognized format); file preserved in place`,\n );\n }\n try {\n validateShardMembership(name, read.file);\n } catch (error) {\n await this.recordIndexStatus({\n lastReadRecovery: { ts: new Date().toISOString(), message: String(error) },\n });\n throw error;\n }\n const shardIdentity = { provider: read.file.provider, model: read.file.model };\n if (!identity) identity = shardIdentity;\n if (!sameIndexIdentity(shardIdentity, identity)) {\n // A partial rewrite from this map would silently drop the foreign\n // shard's entries, so even recall must not serve this generation.\n throw new EmbeddingIndexStorageError(\n `refusing to continue from mixed-identity embedding index generation: ${name} is ${shardIdentity.provider}/${shardIdentity.model} but the generation identity is ${identity.provider}/${identity.model}; files preserved in place`,\n );\n }\n Object.assign(merged, read.file.entries);\n }\n return identity;\n }\n\n async readLegacy(): Promise<ManagedIndexRead> {\n return this.readFileAt(this.indexPath);\n }\n\n /**\n * Read one managed index file (legacy or shard). The file is never\n * modified or moved here: an unreadable file stays in place for recovery\n * and is reported through the returned outcome (mutation paths fail\n * closed on it; read-only paths fail open and record the reason).\n */\n /**\n * Read/mutation boundary for any generation member or the legacy marker\n * (codex PRRT_kwDORJXyws6mZ72r): a symlinked member is rejected outright\n * and its real target must stay inside the canonical state directory —\n * following a planted link would read or write outside the memory root.\n * Runs BEFORE the secure IO so storage-backed reads are covered too.\n */\n private async assertGenerationFileNotSymlinked(filePath: string): Promise<void> {\n const info = await lstat(filePath).catch((err) => {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw new EmbeddingIndexStorageError(\n `cannot lstat embedding index file ${filePath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n });\n const stateDir = path.dirname(this.shardDir);\n // Containment must be checked even when the member itself is a regular\n // file: a SYMLINKED GENERATION DIRECTORY lets reads through to whatever\n // path the link resolves to — a regular file in a linked dir is still\n // outside the memory root and must be rejected.\n const real = await realpath(filePath).catch(() => null);\n if (real) {\n const scope = await realpath(stateDir).catch(() => stateDir);\n const rel = path.relative(scope, real);\n if (rel.startsWith(\"..\") || path.isAbsolute(rel)) {\n throw new EmbeddingIndexStorageError(\n `embedding index file escapes the state directory via symlink; refusing: ${filePath} -> ${real}`,\n );\n }\n }\n if (!info || !info.isSymbolicLink()) return;\n throw new EmbeddingIndexStorageError(\n `embedding index file is a symlink; refusing to read or write through it: ${filePath}`,\n );\n }\n\n async readFileAt(filePath: string): Promise<ManagedIndexRead> {\n await this.assertGenerationFileNotSymlinked(filePath);\n const hardLimit = resolveIndexHardReadCharLimit();\n let size = 0;\n try {\n size = (await stat(filePath)).size;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return { outcome: \"absent\" };\n return { outcome: \"unreadable\", reason: `stat failed: ${err instanceof Error ? err.message : String(err)}` };\n }\n // Reads are gated by the HARD single-string ceiling only — NOT by the\n // soft write budget — so a legacy index from an older version sitting\n // between the two still loads and migrates (issue #3146). Byte size is\n // only a conservative bound for UTF-8 (bytes ≥ chars): at or below it a\n // readFile is guaranteed materializable; past it the file MAY still be\n // readable as a string, but refusing is the safe side. An over-ceiling\n // file is preserved in place; the caller decides fail-open (recall) or\n // fail-closed (mutation).\n if (size > hardLimit) {\n return {\n outcome: \"unreadable\",\n reason: `file is ${size} bytes, over the ${hardLimit}-char hard single-string read ceiling (V8 RangeError territory); bytes preserved in place — move or prune the file to recover`,\n };\n }\n let raw: string;\n try {\n raw = this.io\n ? await this.io.readUtf8(filePath)\n : await readFile(filePath, \"utf-8\");\n } catch (err) {\n return { outcome: \"unreadable\", reason: `read failed: ${err instanceof Error ? err.message : String(err)}` };\n }\n return parseEmbeddingIndexDocument(raw);\n }\n\n private async readShardNames(): Promise<string[]> {\n try {\n return (await readdir(this.shardDir))\n .filter((name) => SHARD_FILE_PATTERN.test(name))\n .sort();\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n // The published generation exists but cannot be enumerated. Treating\n // it as empty would let a mutation overwrite shards it never saw, so\n // this must propagate — strict mutation fails closed, recall fails\n // open at its boundary (issue #3146 review).\n throw new EmbeddingIndexStorageError(\n `cannot enumerate embedding index shard directory ${this.shardDir}: ${err instanceof Error ? `${err.message} (${(err as NodeJS.ErrnoException).code ?? \"unknown\"})` : String(err)}`,\n );\n }\n }\n\n /**\n * Persist the index. Layout decisions, in order:\n * - published generation: rewrite only the shards this mutation touched;\n * - legacy/empty generation and the whole index fits one bounded JSON\n * string: stay the single `state/embeddings.json` file;\n * - over budget: ONE-WAY migration — stage a complete shard set, publish\n * it with a single directory rename, then rename the legacy file aside\n * as a non-authoritative recovery artifact (issue #3146).\n */\n async persist(\n index: EmbeddingIndexFile,\n opts: {\n touchedIds?: readonly string[];\n memoryId?: string;\n /** Reasserts mutation-lock ownership immediately before each destructive write (after serialization). */\n fence?: EmbeddingGenerationFence;\n } = {},\n ): Promise<void> {\n const stateDir = path.dirname(this.indexPath);\n await mkdir(stateDir, { recursive: true });\n await this.cleanForeignStagingDirs();\n // NOTE: interrupted-replacement recovery runs in the mutation wrapper\n // (EmbeddingFallback.enqueueIndexMutation) BEFORE this method, so the\n // caller's index already reflects the restored generation.\n\n const groups = shardEntriesForIndex(index);\n\n const layout = await this.detectLayout();\n\n if (layout === \"sharded\") {\n const limit = resolveIndexFileCharLimit();\n // An identity change on a published generation (a sharded host index\n // replaced by a fallback provider, or a host-model change) must\n // publish a COMPLETE replacement: the dirty-shard fast path would\n // rewrite only the touched shard under the new identity and leave the\n // former identity's shards in place — a mixed generation that every\n // later load rejects (codex P1, PR #3148).\n const diskIdentity = await this.identityFromDisk();\n if (groups.size === 0) {\n // An empty directory has no transport-visible deletion evidence.\n // Publish a valid empty shard so a peer without a shared base also\n // replaces its stale generation rather than preserving old vectors.\n groups.set(0, {});\n await this.publishReplacementGeneration(index, groups, limit, opts.fence);\n return;\n }\n const identityChanged =\n diskIdentity !== null &&\n (diskIdentity.provider !== index.provider || diskIdentity.model !== index.model);\n if (identityChanged) {\n await this.publishReplacementGeneration(index, groups, limit, opts.fence);\n return;\n }\n const dirtyShards = opts.touchedIds?.length\n ? new Set(opts.touchedIds.map((id) => shardIndexOf(id, SHARD_COUNT)))\n : null;\n // Serialize every touched shard BEFORE writing anything so a capacity\n // failure cannot leave a partial rewrite.\n const targets = dirtyShards ? [...dirtyShards] : [...groups.keys()];\n const payloads = targets\n .filter((shardIndex) => groups.has(shardIndex))\n .map((shardIndex) => ({\n shardIndex,\n body: serializeEmbeddingShard(index, shardIndex, groups.get(shardIndex)!, limit),\n }));\n // Serialization is done: reassert lock ownership immediately before\n // the destructive live-generation writes.\n await opts.fence?.();\n await this.beginGenerationMarker();\n await opts.fence?.();\n for (const payload of payloads) {\n await this.writeAtomicFile(\n path.join(this.shardDir, shardFileName(payload.shardIndex)),\n payload.body,\n );\n }\n // A dirty shard with no remaining entries (its last entry was just\n // removed) must be removed from disk, not silently skipped.\n for (const shardIndex of targets) {\n if (!groups.has(shardIndex)) {\n await rm(path.join(this.shardDir, shardFileName(shardIndex)), { force: true });\n }\n }\n await this.completeGenerationMarker();\n return;\n }\n\n let whole: string | null;\n try {\n whole = JSON.stringify(index);\n } catch (err) {\n if (!isInvalidStringLengthError(err)) throw err;\n whole = null;\n }\n const limit = resolveIndexFileCharLimit();\n if (whole !== null && whole.length <= limit) {\n // Serialized above: reassert lock ownership immediately before the\n // destructive atomic write.\n await opts.fence?.();\n await this.beginGenerationMarker();\n await opts.fence?.();\n await this.writeAtomicFile(this.indexPath, whole);\n await this.completeGenerationMarker();\n return;\n }\n\n // One-way migration. Until the publish rename, the legacy file stays\n // authoritative; afterwards the published directory is the only\n // generation and the legacy file is demoted to a recovery artifact.\n const stagingDir = path.join(\n stateDir,\n `embeddings.staging.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`,\n );\n await mkdir(stagingDir, { recursive: true });\n try {\n await this.stageShardSet(stagingDir, index, groups, limit);\n // Atomic publish: the directory appearing IS the layout marker.\n await this.publishSwappedGeneration(stagingDir, opts.fence);\n } catch (err) {\n await rm(stagingDir, { recursive: true, force: true }).catch(() => undefined);\n throw err;\n }\n await rename(\n this.indexPath,\n `${this.indexPath}.pre-migration.tmp-${Date.now()}-${Math.random().toString(16).slice(2)}`,\n ).catch(() => undefined);\n }\n\n /**\n * Replace a published generation wholesale after a provider/model identity\n * change (replacement semantics — the former entries are obsolete by\n * definition). Crash-safe by the same construction as the migration: the\n * COMPLETE new-identity set is staged and validated first (old bytes are\n * not touched until staging succeeds), then published atomically through\n * `publishSwappedGeneration`.\n */\n private async publishReplacementGeneration(\n index: EmbeddingIndexFile,\n groups: Map<number, Record<string, EmbeddingIndexEntry>>,\n limit: number,\n fence?: EmbeddingGenerationFence,\n ): Promise<void> {\n const stateDir = path.dirname(this.indexPath);\n const stagingDir = path.join(\n stateDir,\n `embeddings.staging.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`,\n );\n await mkdir(stagingDir, { recursive: true });\n try {\n await this.stageShardSet(stagingDir, index, groups, limit);\n await this.publishSwappedGeneration(stagingDir, fence);\n } catch (err) {\n await rm(stagingDir, { recursive: true, force: true }).catch(() => undefined);\n throw err;\n }\n }\n\n /**\n * Write a complete shard set into a staging directory. Every shard is\n * serialized before the first write, so a capacity failure cannot leave a\n * partial stage behind.\n */\n private async stageShardSet(\n stagingDir: string,\n index: EmbeddingIndexFile,\n groups: Map<number, Record<string, EmbeddingIndexEntry>>,\n limit: number,\n ): Promise<void> {\n const payloads = [...groups]\n .map(([shardIndex, entries]) => ({\n shardIndex,\n body: serializeEmbeddingShard(index, shardIndex, entries, limit),\n }));\n for (const payload of payloads) {\n const finalAadFilePath = path.join(this.shardDir, shardFileName(payload.shardIndex));\n await this.writeAtomicFile(\n path.join(stagingDir, shardFileName(payload.shardIndex)),\n payload.body,\n { finalAadFilePath },\n );\n }\n }\n\n /**\n * Atomically remove the published generation (offline-sync: the remote\n * deleted the whole index) by publishing an EMPTY staged directory\n * through the shared swap: the rename is atomic, so the former generation\n * moves whole into the fixed backup and back — a failure before the\n * commit rolls the former generation back intact, and a post-commit\n * backup-cleanup failure is non-fatal (the empty published directory\n * stays the authoritative one-way layout marker). The fixed backup is\n * symlink-checked first (round 5).\n */\n async removePublishedGeneration(fence?: EmbeddingGenerationFence): Promise<void> {\n const backupPath = replacementBackupPath(this.shardDir);\n await this.assertBackupNotSymlink(backupPath);\n await rm(backupPath, { recursive: true, force: true });\n // Remove by publishing an EMPTY staged directory through the SAME swap\n // state machine as every other publication: rename is atomic, so the\n // former generation moves whole into the fixed backup and back — a\n // partially-deleted generation can never be exposed. The empty\n // published directory stays the one-way layout marker, so a stray\n // legacy file can never become authoritative after removal. The\n // demoted backup's post-commit cleanup is non-fatal (existing\n // publishSwappedGeneration semantics).\n await mkdir(path.dirname(this.shardDir), { recursive: true });\n const emptyStaging = await mkdtemp(\n path.join(path.dirname(this.shardDir), \"embeddings.staging.tmp-empty-\"),\n );\n try {\n await this.publishSwappedGeneration(emptyStaging, fence);\n } catch (err) {\n await rm(emptyStaging, { recursive: true, force: true }).catch(() => undefined);\n throw err;\n }\n }\n\n /**\n * Atomically publish a fully staged generation directory as THE published\n * generation. The former published directory is demoted to the FIXED\n * transaction backup and the staging dir is renamed in with a single\n * rename; if the publish rename fails the former generation is rolled\n * back, and if the process dies in the rename gap `detectLayout()` /\n * `recoverIfInterrupted()` perform the same rollback on next use.\n * Shared by the mutation-side migration/replacement and the offline-sync\n * generation transaction (issue #3148, round 4) so there is exactly one\n * swap state machine. Staging-dir cleanup stays with the caller.\n */\n async publishSwappedGeneration(stagingDir: string, fence?: EmbeddingGenerationFence): Promise<void> {\n // The replacement is published: the backup is obsolete disk. A cleanup\n // failure is non-fatal (the next replacement removes it) but must stay\n // visible.\n const backupPath = replacementBackupPath(this.shardDir);\n await this.assertBackupNotSymlink(backupPath);\n // Cheap ownership check before the destructive backup cleanup: a peer\n // stale-break would otherwise see the backup vanish under it before we\n // even reach the live renames (Kilo PRRT_kwDORJXyws6mZ8i5).\n await fence?.();\n await rm(backupPath, { recursive: true, force: true });\n // Second ownership check immediately before the live renames: any\n // delay between the cleanup and the rename still allows a stale-break,\n // and publishing now would clobber the peer's write.\n await fence?.();\n // Publication barrier: in-flight before the renames, final at commit.\n await this.beginGenerationMarker();\n await fence?.();\n let demoted = false;\n try {\n await stat(this.shardDir);\n await rename(this.shardDir, backupPath);\n demoted = true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n try {\n await rename(stagingDir, this.shardDir);\n } catch (err) {\n if (demoted) {\n try {\n await rename(backupPath, this.shardDir);\n await this.completeGenerationMarker();\n } catch (rollbackErr) {\n log.warn(`embedding index: publication rollback did not restore the generation; marker stays in-flight: ${rollbackErr instanceof Error ? rollbackErr.message : String(rollbackErr)}`);\n }\n } else {\n await this.completeGenerationMarker();\n }\n throw err;\n }\n await this.completeGenerationMarker();\n await rm(backupPath, { recursive: true, force: true }).catch((err) => {\n log.warn(`embedding index: could not remove replacement backup ${backupPath}: ${err}`);\n });\n }\n\n /** Remove staging dirs orphaned by crashed migrations. A foreign dir younger than the grace window may belong to a live writer mid-migration, so only older dirs are removed (issue #3148 review, round 3). */\n private static readonly STAGING_CLEANUP_GRACE_MS = 10 * 60 * 1000;\n\n private async cleanForeignStagingDirs(): Promise<void> {\n const stateDir = path.dirname(this.indexPath);\n let names: string[] = [];\n try {\n names = await readdir(stateDir);\n } catch {\n return;\n }\n const ownPrefix = `embeddings.staging.tmp-${process.pid}-`;\n const cutoff = Date.now() - EmbeddingIndexFileStore.STAGING_CLEANUP_GRACE_MS;\n for (const name of names) {\n if (!name.startsWith(\"embeddings.staging.tmp-\") || name.startsWith(ownPrefix)) continue;\n try {\n const stats = await stat(path.join(stateDir, name));\n if (stats.mtimeMs > cutoff) continue;\n } catch {\n continue;\n }\n await rm(path.join(stateDir, name), { recursive: true, force: true }).catch(() => undefined);\n }\n }\n\n /**\n * Temp-file + rename write. The temp name carries `.tmp-` so offline sync\n * ignores in-flight files under `state/`. With a wired io, the write goes\n * through the canonical secure-store contract instead, optionally binding\n * the ciphertext to the FINAL published path (staged shards).\n */\n private async writeAtomicFile(\n filePath: string,\n contents: string,\n opts?: { finalAadFilePath?: string; bypassSecureIo?: boolean },\n ): Promise<void> {\n if (this.io && opts?.bypassSecureIo !== true) {\n await this.io.writeUtf8(filePath, contents, opts?.finalAadFilePath === undefined ? undefined : { finalAadFilePath: opts.finalAadFilePath });\n return;\n }\n const tempPath = `${filePath}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;\n try {\n await writeFile(tempPath, contents, \"utf-8\");\n await rename(tempPath, filePath);\n } catch (err) {\n await rm(tempPath, { force: true }).catch(() => undefined);\n throw err;\n }\n }\n\n /**\n * Record the outcome of one index mutation durably. This is diagnostics,\n * NOT recovery: write failures are recorded and then re-thrown by the\n * caller so the extraction/consolidation task fails explicitly instead of\n * declaring success over an index that could not be persisted (issue\n * #3146).\n */\n async recordIndexWriteOutcome(err: unknown, memoryId?: string): Promise<void> {\n if (!err) {\n await this.recordIndexStatus({\n lastWriteFailure: null,\n lastSuccessAt: new Date().toISOString(),\n });\n return;\n }\n const kind: EmbeddingIndexWriteFailure[\"kind\"] =\n err instanceof EmbeddingIndexCapacityError || isInvalidStringLengthError(err)\n ? \"capacity\"\n : \"io\";\n await this.recordIndexStatus({\n failureCountIncrement: 1,\n lastWriteFailure: {\n ts: new Date().toISOString(),\n kind,\n message: err instanceof Error ? err.message : String(err),\n ...(memoryId ? { memoryId } : {}),\n },\n });\n }\n\n /**\n * Merge diagnostic state into `state/embedding-fallback-status.json`.\n * Best-effort: a diagnostics failure never masks the real outcome.\n */\n private async recordIndexStatus(\n patch: {\n failureCountIncrement?: number;\n lastWriteFailure?: EmbeddingIndexWriteFailure | null;\n lastReadRecovery?: { ts: string; message: string };\n lastSuccessAt?: string;\n },\n ): Promise<void> {\n try {\n let current: EmbeddingIndexStatusFile = { version: 1, failureCount: 0 };\n try {\n const parsed = JSON.parse(await readFile(this.statusPath, \"utf-8\")) as Partial<EmbeddingIndexStatusFile>;\n if (parsed && typeof parsed === \"object\" && typeof parsed.failureCount === \"number\") {\n current = { ...parsed, version: 1, failureCount: parsed.failureCount };\n }\n } catch {\n // Absent or unreadable status starts fresh; the index files own the truth.\n }\n const next: EmbeddingIndexStatusFile = { ...current };\n if (patch.failureCountIncrement) next.failureCount += patch.failureCountIncrement;\n if (patch.lastWriteFailure !== undefined) {\n if (patch.lastWriteFailure === null) delete next.lastWriteFailure;\n else next.lastWriteFailure = patch.lastWriteFailure;\n }\n if (patch.lastReadRecovery !== undefined) next.lastReadRecovery = patch.lastReadRecovery;\n if (patch.lastSuccessAt !== undefined) next.lastSuccessAt = patch.lastSuccessAt;\n // The status file is ALWAYS plain (non-secret diagnostics: failure\n // strings and counts only). It bypasses the secure io so console_state\n // keeps reading it on locked stores and failure counts never reset.\n await mkdir(path.dirname(this.statusPath), { recursive: true });\n await this.writeAtomicFile(this.statusPath, JSON.stringify(next), { bypassSecureIo: true });\n } catch (err) {\n log.debug(`embedding fallback status write failed: ${err}`);\n }\n }\n}\n\nfunction sameIndexIdentity(\n left: EmbeddingIndexIdentity,\n right: EmbeddingIndexIdentity,\n): boolean {\n return left.provider === right.provider && left.model === right.model;\n}\n"],"mappings":"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","names":["limit"]}