@remnic/core 10.0.38 → 10.0.39

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 (416) hide show
  1. package/dist/access-admin-ops-surface.js +73 -73
  2. package/dist/access-audit.js +2 -2
  3. package/dist/access-authorization-probe.js +74 -74
  4. package/dist/access-boundary.js +73 -73
  5. package/dist/access-citation-namespace.js +2 -2
  6. package/dist/access-cli.js +161 -160
  7. package/dist/access-cli.js.map +1 -1
  8. package/dist/access-extraction-force-flush.js +73 -73
  9. package/dist/access-http-briefing.js +74 -74
  10. package/dist/access-http-query.js +75 -75
  11. package/dist/access-http.js +98 -98
  12. package/dist/access-identity-continuity-surface.js +73 -73
  13. package/dist/access-lcm-surface.js +73 -73
  14. package/dist/access-mcp.js +87 -87
  15. package/dist/access-namespace-preflight.js +73 -73
  16. package/dist/access-observe-write-category.js +15 -15
  17. package/dist/access-observe-write-surface.js +73 -73
  18. package/dist/access-offline-manifest.js +12 -12
  19. package/dist/access-operations-batch.js +77 -77
  20. package/dist/access-operations.js +84 -84
  21. package/dist/access-recall-concurrency.js +73 -73
  22. package/dist/access-recall-diagnostics.js +2 -2
  23. package/dist/access-recall-response.js +73 -73
  24. package/dist/access-recall-surface.js +73 -73
  25. package/dist/access-schema.js +10 -10
  26. package/dist/access-service.js +73 -73
  27. package/dist/access-surface-catalog.js +75 -75
  28. package/dist/active-recall.js +29 -29
  29. package/dist/adapters/index.js +4 -4
  30. package/dist/adapters/registry.js +2 -2
  31. package/dist/{auto-sync-432USQDH.js → auto-sync-BNCBMU6H.js} +7 -7
  32. package/dist/briefing.js +19 -19
  33. package/dist/buffer-types.d.ts +59 -0
  34. package/dist/buffer-types.d.ts.map +1 -0
  35. package/dist/buffer-types.js +1 -0
  36. package/dist/buffer.d.ts +14 -22
  37. package/dist/buffer.d.ts.map +1 -1
  38. package/dist/buffer.js +11 -11
  39. package/dist/calibration.js +2 -2
  40. package/dist/{capsule-crypto-7FJQINUR.js → capsule-crypto-GWVG7LGC.js} +2 -2
  41. package/dist/causal-behavior.js +2 -2
  42. package/dist/causal-chain.js +2 -2
  43. package/dist/causal-consolidation.js +22 -22
  44. package/dist/causal-retrieval.js +2 -2
  45. package/dist/causal-trajectory-graph.js +2 -2
  46. package/dist/causal-trajectory.js +1 -1
  47. package/dist/{chunk-5K23NQRI.js → chunk-22YJQTPN.js} +2 -2
  48. package/dist/{chunk-MKE5DP3S.js → chunk-23ZMQONL.js} +2 -2
  49. package/dist/{chunk-3K5IEJTK.js → chunk-2AV2U2SZ.js} +3 -3
  50. package/dist/{chunk-HJ3UTGIW.js → chunk-2JNBPOZ2.js} +3 -3
  51. package/dist/{chunk-EEOYF3IB.js → chunk-3RJDYZJE.js} +2 -2
  52. package/dist/{chunk-SZPPCEYT.js → chunk-4HJ2QK32.js} +2 -2
  53. package/dist/{chunk-OLHN4ZNT.js → chunk-4LLV2N3R.js} +3 -3
  54. package/dist/{chunk-P5CUEB2Q.js → chunk-4QLKVZH6.js} +8 -8
  55. package/dist/{chunk-4IZ6AC2U.js → chunk-4UT2NUAC.js} +2 -2
  56. package/dist/{chunk-WCBLPKG3.js → chunk-5KZNWERU.js} +155 -142
  57. package/dist/chunk-5KZNWERU.js.map +1 -0
  58. package/dist/{chunk-VC3X7XP3.js → chunk-5Q6CR65T.js} +2 -2
  59. package/dist/{chunk-XTBEXZFN.js → chunk-5SF7JOJB.js} +4 -4
  60. package/dist/{chunk-DKHX2FHI.js → chunk-64WHWIPL.js} +2 -2
  61. package/dist/{chunk-TVIGN3XZ.js → chunk-6FRFVIKC.js} +10 -10
  62. package/dist/{chunk-JIA435P3.js → chunk-6ITNEPJO.js} +2 -2
  63. package/dist/{chunk-WEK3OUGO.js → chunk-6X3J4NLZ.js} +10 -5
  64. package/dist/chunk-6X3J4NLZ.js.map +1 -0
  65. package/dist/{chunk-3AODCBKX.js → chunk-6Z2ZWBOV.js} +6 -6
  66. package/dist/{chunk-TAEJ2MRR.js → chunk-7IANFJ24.js} +56 -56
  67. package/dist/{chunk-PSQ2P2T3.js → chunk-7XZ7TOLQ.js} +3 -3
  68. package/dist/{chunk-6JEEY42N.js → chunk-AER3HD3B.js} +2 -2
  69. package/dist/{chunk-4B37KNMP.js → chunk-ANV373S5.js} +2 -2
  70. package/dist/{chunk-AYBAC2N4.js → chunk-B3I2QQU7.js} +3 -3
  71. package/dist/{chunk-TYLNWE4D.js → chunk-B7VKRCAG.js} +2 -2
  72. package/dist/{chunk-HXFIILGF.js → chunk-BFPF5N3H.js} +4 -4
  73. package/dist/{chunk-KQAFEZQX.js → chunk-BUKK5SWA.js} +2 -2
  74. package/dist/{chunk-53BLBDV6.js → chunk-BUKZ7MTN.js} +2 -2
  75. package/dist/{chunk-MCPSMWUC.js → chunk-CR72WZ3M.js} +9 -9
  76. package/dist/{chunk-YC2C67F7.js → chunk-CRXGQRNA.js} +3 -3
  77. package/dist/{chunk-WRDFYSUC.js → chunk-CZVNOTLR.js} +2 -2
  78. package/dist/{chunk-VPO53QMG.js → chunk-D6DOLQYK.js} +2 -2
  79. package/dist/{chunk-RGKXF4W3.js → chunk-E5IEI2MR.js} +2 -2
  80. package/dist/{chunk-Z2T5BPOZ.js → chunk-EIYJK7OD.js} +2 -2
  81. package/dist/{chunk-FRAL3EU4.js → chunk-EKPRLTAE.js} +2 -2
  82. package/dist/{chunk-C2KOW2UQ.js → chunk-EKR6OO4P.js} +2 -2
  83. package/dist/{chunk-TYGLV6ZG.js → chunk-EYGOM6AI.js} +2 -2
  84. package/dist/{chunk-TYGLV6ZG.js.map → chunk-EYGOM6AI.js.map} +1 -1
  85. package/dist/{chunk-SWZOAQUM.js → chunk-F2IU6A2L.js} +57 -57
  86. package/dist/{chunk-2AKIVSHP.js → chunk-FAKRTS3Y.js} +2 -2
  87. package/dist/{chunk-MZWHIAOL.js → chunk-FC3ZFEXF.js} +2 -2
  88. package/dist/{chunk-3PLAL2KP.js → chunk-FJ264QBP.js} +17 -9
  89. package/dist/chunk-FJ264QBP.js.map +1 -0
  90. package/dist/{chunk-7VFNGQ4V.js → chunk-FLDXEJFX.js} +2 -2
  91. package/dist/{chunk-JH4FCVWJ.js → chunk-G6OFEGUR.js} +2 -2
  92. package/dist/{chunk-L7PGPH4E.js → chunk-GCW7UT5I.js} +2 -2
  93. package/dist/{chunk-J7G656MY.js → chunk-GE5ZNDLE.js} +4 -4
  94. package/dist/{chunk-63YPKHAU.js → chunk-GLB6VV3J.js} +2 -2
  95. package/dist/{chunk-6F5BGIAF.js → chunk-GPVGAHXL.js} +3 -3
  96. package/dist/{chunk-XHX67LX6.js → chunk-GSBSGT43.js} +2 -2
  97. package/dist/{chunk-645WOU5I.js → chunk-H7BZBSP6.js} +4 -4
  98. package/dist/{chunk-GFYFKXWZ.js → chunk-HUDPXTXW.js} +2 -2
  99. package/dist/{chunk-L2JQSG5M.js → chunk-I7TR2OCU.js} +2 -2
  100. package/dist/{chunk-6SYS5YX3.js → chunk-IAOW2L6K.js} +2 -2
  101. package/dist/{chunk-STM4T335.js → chunk-IJH6METJ.js} +2 -2
  102. package/dist/{chunk-3XVBGGF3.js → chunk-IZRNMXZC.js} +2 -2
  103. package/dist/{chunk-XHVO3CGX.js → chunk-JGS55BAS.js} +53 -2
  104. package/dist/chunk-JGS55BAS.js.map +1 -0
  105. package/dist/{chunk-Y5ZHZKZD.js → chunk-JM6U3SAE.js} +2 -2
  106. package/dist/{chunk-74AY3S7U.js → chunk-JNLYHJG7.js} +4 -4
  107. package/dist/{chunk-QOUKZ2N3.js → chunk-JOOGPZJE.js} +2 -2
  108. package/dist/{chunk-EGNONTVT.js → chunk-JORSMFL6.js} +6 -6
  109. package/dist/{chunk-JF3MEIHO.js → chunk-JQCP5QW3.js} +4 -4
  110. package/dist/{chunk-POGPI5GR.js → chunk-JRTKYFFY.js} +2 -2
  111. package/dist/{chunk-5UPOKIFP.js → chunk-JT3IXJIL.js} +12 -12
  112. package/dist/{chunk-6DTIYAAN.js → chunk-JZFJZLJY.js} +2 -2
  113. package/dist/{chunk-6QOL4G76.js → chunk-KQXRVELF.js} +2 -2
  114. package/dist/{chunk-KOAVX2IT.js → chunk-L74NWAD4.js} +2 -2
  115. package/dist/{chunk-5DCS4C4S.js → chunk-LJXBFVI2.js} +4 -4
  116. package/dist/{chunk-OKKBOTRA.js → chunk-LPD6I2U6.js} +2 -2
  117. package/dist/{chunk-N7DVBHU3.js → chunk-LR7RW4T6.js} +3 -3
  118. package/dist/{chunk-FZVZYU5C.js → chunk-LXN737FX.js} +2 -2
  119. package/dist/{chunk-LOMSBYNS.js → chunk-MD3W2H3G.js} +298 -95
  120. package/dist/chunk-MD3W2H3G.js.map +1 -0
  121. package/dist/{chunk-7ZRRJ6RG.js → chunk-MGYZENUY.js} +2 -2
  122. package/dist/{chunk-YQ6SLFNZ.js → chunk-MPJDQC4G.js} +3 -3
  123. package/dist/{chunk-5ANUG4JZ.js → chunk-NJ2DZYEK.js} +2 -2
  124. package/dist/{chunk-OLH4XCLU.js → chunk-O2O4FDAH.js} +2 -2
  125. package/dist/{chunk-CVMAOPAH.js → chunk-ODJ2HRFW.js} +10 -10
  126. package/dist/{chunk-2NLLXCJG.js → chunk-OWHERGF2.js} +2 -2
  127. package/dist/{chunk-I73ZSORJ.js → chunk-OXSTKCYK.js} +4 -4
  128. package/dist/{chunk-RWRHGE75.js → chunk-P3YUEPSQ.js} +2 -2
  129. package/dist/{chunk-UFM3PJDT.js → chunk-PARM6OYL.js} +1 -1
  130. package/dist/chunk-PARM6OYL.js.map +1 -0
  131. package/dist/{chunk-TPKB7T6Z.js → chunk-Q6UZREJK.js} +2 -2
  132. package/dist/{chunk-UJDTXWMT.js → chunk-QIMGULGN.js} +2 -2
  133. package/dist/{chunk-E24RUQEN.js → chunk-QNIYL5SK.js} +2 -2
  134. package/dist/{chunk-KOY25HUB.js → chunk-QYAXMXOB.js} +2 -2
  135. package/dist/{chunk-X247CYL6.js → chunk-RIAKNXZB.js} +4 -4
  136. package/dist/{chunk-WCIDCJOQ.js → chunk-RO2NNWR5.js} +3 -3
  137. package/dist/{chunk-AYXRK264.js → chunk-RROFIAYI.js} +9 -9
  138. package/dist/{chunk-YYGZ4EIY.js → chunk-RUAIO5UA.js} +2 -2
  139. package/dist/{chunk-FQFMLOAC.js → chunk-RWW5KPIY.js} +18 -18
  140. package/dist/{chunk-E5PVMB3I.js → chunk-SDXBR5QO.js} +2 -2
  141. package/dist/{chunk-NH6GZEQN.js → chunk-SJHFU52W.js} +8 -8
  142. package/dist/{chunk-FGEY2AGV.js → chunk-SRPGA6WN.js} +2 -2
  143. package/dist/{chunk-CUWOSRCS.js → chunk-SUKPM6DV.js} +2 -2
  144. package/dist/{chunk-TXP7Q7T5.js → chunk-TTQYX7VU.js} +2 -2
  145. package/dist/{chunk-TDCR6PQL.js → chunk-TWRPWJJT.js} +2 -2
  146. package/dist/{chunk-I75BWXRZ.js → chunk-U66CTIF5.js} +2 -2
  147. package/dist/{chunk-DG4H56SK.js → chunk-UIK3U36O.js} +2 -2
  148. package/dist/{chunk-XLEP7SS3.js → chunk-UMSH4I3E.js} +2 -2
  149. package/dist/{chunk-X7Y7WX73.js → chunk-UVYI6VIX.js} +1 -1
  150. package/dist/{chunk-LHBOFROT.js → chunk-V2XMG7GM.js} +2 -2
  151. package/dist/chunk-VA3GNAWQ.js +58 -0
  152. package/dist/chunk-VA3GNAWQ.js.map +1 -0
  153. package/dist/{chunk-ZAP7BKTZ.js → chunk-VCOCOBUS.js} +6 -6
  154. package/dist/{chunk-4P4VLPGB.js → chunk-VECRSYQG.js} +1 -1
  155. package/dist/{chunk-GGI6FLO6.js → chunk-VHBGJAYC.js} +2 -2
  156. package/dist/{chunk-QO5KDQGG.js → chunk-VIS23B6O.js} +7 -7
  157. package/dist/{chunk-NMKETOAX.js → chunk-VSHWPOUB.js} +2 -2
  158. package/dist/{chunk-UBN5AOLT.js → chunk-W5EO4USJ.js} +2 -2
  159. package/dist/{chunk-Y7427SL7.js → chunk-WCGJK4NQ.js} +4 -4
  160. package/dist/{chunk-7HXZOWTR.js → chunk-WQYS47G4.js} +2 -2
  161. package/dist/{chunk-JH2ALMPR.js → chunk-WR5POHQ3.js} +2 -2
  162. package/dist/{chunk-VSSQIBVU.js → chunk-WT2VVYBM.js} +2 -2
  163. package/dist/{chunk-EHIKY2IA.js → chunk-WU5WTCH5.js} +2 -2
  164. package/dist/{chunk-QKXPOLQD.js → chunk-WUBGMV3G.js} +6 -6
  165. package/dist/{chunk-FKTMWDN2.js → chunk-WUL6LDJ4.js} +2 -2
  166. package/dist/{chunk-U7HJBODU.js → chunk-XKTHJHSQ.js} +5 -5
  167. package/dist/{chunk-JFI3ATK4.js → chunk-XMYWYJM2.js} +2 -2
  168. package/dist/{chunk-64S6X4IU.js → chunk-XTN54NML.js} +4 -4
  169. package/dist/{chunk-VGFNTY3Y.js → chunk-YFXRLRKP.js} +2 -2
  170. package/dist/{chunk-DAOMWGO2.js → chunk-ZX5N5D6M.js} +4 -4
  171. package/dist/{chunk-VGRM43X6.js → chunk-ZXFKG7VZ.js} +2 -2
  172. package/dist/cli.js +121 -121
  173. package/dist/coding/export-okf-codegraph.js +20 -20
  174. package/dist/coding/pre-action-gate.js +2 -2
  175. package/dist/compounding/engine.js +21 -21
  176. package/dist/config.d.ts.map +1 -1
  177. package/dist/config.js +29 -29
  178. package/dist/connectors/codex-materialize-runner.js +19 -19
  179. package/dist/connectors/index.js +19 -19
  180. package/dist/contradiction/index.js +5 -5
  181. package/dist/convergence-refresh.js +21 -21
  182. package/dist/corpus-watermark.js +21 -21
  183. package/dist/deep-recall-http-glue.js +11 -11
  184. package/dist/deep-recall.js +2 -2
  185. package/dist/entity-retrieval.js +20 -20
  186. package/dist/event-order-recall.js +2 -2
  187. package/dist/explicit-capture.js +6 -6
  188. package/dist/external-wiki-access.js +74 -74
  189. package/dist/extraction-window-drain.d.ts +48 -0
  190. package/dist/extraction-window-drain.d.ts.map +1 -0
  191. package/dist/extraction-window-drain.js +14 -0
  192. package/dist/extraction.js +27 -27
  193. package/dist/{graph-edge-decay-2BZTY2IM.js → graph-edge-decay-3HVLMT6U.js} +5 -5
  194. package/dist/graph-edge-decay-3HVLMT6U.js.map +1 -0
  195. package/dist/graph-jsonl.js +3 -3
  196. package/dist/graph-snapshot.js +3 -3
  197. package/dist/graph.js +4 -4
  198. package/dist/harmonic-retrieval.js +20 -20
  199. package/dist/harmonic-source-lookup.js +2 -2
  200. package/dist/index.js +208 -207
  201. package/dist/lcm/engine.js +2 -2
  202. package/dist/lcm/index.js +7 -7
  203. package/dist/local-llm.js +2 -2
  204. package/dist/location/index.js +22 -22
  205. package/dist/maintenance/memory-governance.js +19 -19
  206. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +19 -19
  207. package/dist/maintenance/rebuild-memory-projection-runner.js +21 -21
  208. package/dist/maintenance/rebuild-memory-projection.js +20 -20
  209. package/dist/memory-browse-operations.js +74 -74
  210. package/dist/memory-browse.js +6 -6
  211. package/dist/memory-subject.js +3 -3
  212. package/dist/namespaces/migrate.js +20 -20
  213. package/dist/namespaces/search.js +7 -7
  214. package/dist/namespaces/storage.js +19 -19
  215. package/dist/offline-sync-changeset-generations.js +3 -3
  216. package/dist/offline-sync-embedding-generation.js +2 -2
  217. package/dist/offline-sync-file-io.js +2 -2
  218. package/dist/offline-sync-impression-drain.js +3 -3
  219. package/dist/offline-sync-snapshot-stream.js +8 -8
  220. package/dist/offline-sync-upload-staging.js +3 -3
  221. package/dist/offline-sync.js +9 -9
  222. package/dist/operator-doctor-corpus.js +22 -22
  223. package/dist/operator-toolkit.js +40 -40
  224. package/dist/orchestration/extraction-drain.d.ts +90 -0
  225. package/dist/orchestration/extraction-drain.d.ts.map +1 -0
  226. package/dist/orchestration/extraction-run-helpers.d.ts +3 -0
  227. package/dist/orchestration/extraction-run-helpers.d.ts.map +1 -1
  228. package/dist/orchestration/extraction-run.d.ts +4 -1
  229. package/dist/orchestration/extraction-run.d.ts.map +1 -1
  230. package/dist/orchestration/maintenance.js +33 -33
  231. package/dist/orchestration/turn-ingestion.d.ts +3 -0
  232. package/dist/orchestration/turn-ingestion.d.ts.map +1 -1
  233. package/dist/orchestrator.d.ts +8 -0
  234. package/dist/orchestrator.d.ts.map +1 -1
  235. package/dist/orchestrator.js +161 -160
  236. package/dist/qmd.js +5 -5
  237. package/dist/recall-explain-renderer.js +3 -3
  238. package/dist/recall-navigate-access.js +2 -2
  239. package/dist/recall-navigate-disclosure.js +2 -2
  240. package/dist/recall-navigate.js +2 -2
  241. package/dist/recall-navigation-operations.js +74 -74
  242. package/dist/recall-navigation.js +73 -73
  243. package/dist/recall-state-view-admission.js +6 -6
  244. package/dist/recall-why-service.js +6 -6
  245. package/dist/recall-xray-cli.js +4 -4
  246. package/dist/recall-xray-renderer.js +3 -3
  247. package/dist/recall-xray.js +2 -2
  248. package/dist/reconcile/manifest.js +3 -3
  249. package/dist/{resolution-4ZEQPXUZ.js → resolution-NNYG37YB.js} +6 -6
  250. package/dist/resolution-NNYG37YB.js.map +1 -0
  251. package/dist/resume-bundles.js +30 -30
  252. package/dist/retrieval-agents.js +2 -2
  253. package/dist/salvage-envelope.js +5 -5
  254. package/dist/search/factory.js +6 -6
  255. package/dist/search/index.js +10 -10
  256. package/dist/secure-store/index.js +2 -2
  257. package/dist/semantic-consolidation.js +20 -20
  258. package/dist/semantic-rule-promotion.js +19 -19
  259. package/dist/semantic-rule-verifier.js +19 -19
  260. package/dist/source-agent-qualifier.js +23 -23
  261. package/dist/standup/build.js +19 -19
  262. package/dist/storage/dropped-extraction-chunk.d.ts +8 -0
  263. package/dist/storage/dropped-extraction-chunk.d.ts.map +1 -0
  264. package/dist/storage.d.ts +1 -0
  265. package/dist/storage.d.ts.map +1 -1
  266. package/dist/storage.js +18 -18
  267. package/dist/subject-config.js +4 -4
  268. package/dist/summarizer.js +3 -3
  269. package/dist/support-passport/index.js +76 -76
  270. package/dist/transfer/backup.js +2 -2
  271. package/dist/transfer/capsule-export.js +2 -2
  272. package/dist/transfer/capsule-import.js +2 -2
  273. package/dist/transfer/export-okf.js +20 -20
  274. package/dist/types.d.ts +7 -0
  275. package/dist/types.d.ts.map +1 -1
  276. package/dist/types.js +1 -1
  277. package/dist/verified-recall.js +19 -19
  278. package/dist/who-knows-cli.js +20 -20
  279. package/dist/who-knows.js +19 -19
  280. package/dist/write-envelope.js +4 -4
  281. package/package.json +2 -2
  282. package/src/buffer-drop-extraction-chunk.test.ts +196 -0
  283. package/src/buffer-types.ts +65 -0
  284. package/src/buffer.ts +60 -62
  285. package/src/config.ts +8 -0
  286. package/src/extraction-window-drain.test.ts +123 -0
  287. package/src/extraction-window-drain.ts +110 -0
  288. package/src/orchestration/extraction-drain.ts +261 -0
  289. package/src/orchestration/extraction-run-helpers.ts +8 -0
  290. package/src/orchestration/extraction-run.ts +5 -4
  291. package/src/orchestration/turn-ingestion.ts +9 -5
  292. package/src/orchestrator.ts +21 -0
  293. package/src/secure-store/secure-fs.ts +3 -1
  294. package/src/storage/dropped-extraction-chunk.ts +27 -0
  295. package/src/storage-dropped-chunk-filenames.test.ts +30 -0
  296. package/src/storage.ts +6 -7
  297. package/src/types.ts +7 -0
  298. package/dist/chunk-3PLAL2KP.js.map +0 -1
  299. package/dist/chunk-LOMSBYNS.js.map +0 -1
  300. package/dist/chunk-UFM3PJDT.js.map +0 -1
  301. package/dist/chunk-WCBLPKG3.js.map +0 -1
  302. package/dist/chunk-WEK3OUGO.js.map +0 -1
  303. package/dist/chunk-XHVO3CGX.js.map +0 -1
  304. /package/dist/{auto-sync-432USQDH.js.map → auto-sync-BNCBMU6H.js.map} +0 -0
  305. /package/dist/{capsule-crypto-7FJQINUR.js.map → buffer-types.js.map} +0 -0
  306. /package/dist/{graph-edge-decay-2BZTY2IM.js.map → capsule-crypto-GWVG7LGC.js.map} +0 -0
  307. /package/dist/{chunk-5K23NQRI.js.map → chunk-22YJQTPN.js.map} +0 -0
  308. /package/dist/{chunk-MKE5DP3S.js.map → chunk-23ZMQONL.js.map} +0 -0
  309. /package/dist/{chunk-3K5IEJTK.js.map → chunk-2AV2U2SZ.js.map} +0 -0
  310. /package/dist/{chunk-HJ3UTGIW.js.map → chunk-2JNBPOZ2.js.map} +0 -0
  311. /package/dist/{chunk-EEOYF3IB.js.map → chunk-3RJDYZJE.js.map} +0 -0
  312. /package/dist/{chunk-SZPPCEYT.js.map → chunk-4HJ2QK32.js.map} +0 -0
  313. /package/dist/{chunk-OLHN4ZNT.js.map → chunk-4LLV2N3R.js.map} +0 -0
  314. /package/dist/{chunk-P5CUEB2Q.js.map → chunk-4QLKVZH6.js.map} +0 -0
  315. /package/dist/{chunk-4IZ6AC2U.js.map → chunk-4UT2NUAC.js.map} +0 -0
  316. /package/dist/{chunk-VC3X7XP3.js.map → chunk-5Q6CR65T.js.map} +0 -0
  317. /package/dist/{chunk-XTBEXZFN.js.map → chunk-5SF7JOJB.js.map} +0 -0
  318. /package/dist/{chunk-DKHX2FHI.js.map → chunk-64WHWIPL.js.map} +0 -0
  319. /package/dist/{chunk-TVIGN3XZ.js.map → chunk-6FRFVIKC.js.map} +0 -0
  320. /package/dist/{chunk-JIA435P3.js.map → chunk-6ITNEPJO.js.map} +0 -0
  321. /package/dist/{chunk-3AODCBKX.js.map → chunk-6Z2ZWBOV.js.map} +0 -0
  322. /package/dist/{chunk-TAEJ2MRR.js.map → chunk-7IANFJ24.js.map} +0 -0
  323. /package/dist/{chunk-PSQ2P2T3.js.map → chunk-7XZ7TOLQ.js.map} +0 -0
  324. /package/dist/{chunk-6JEEY42N.js.map → chunk-AER3HD3B.js.map} +0 -0
  325. /package/dist/{chunk-4B37KNMP.js.map → chunk-ANV373S5.js.map} +0 -0
  326. /package/dist/{chunk-AYBAC2N4.js.map → chunk-B3I2QQU7.js.map} +0 -0
  327. /package/dist/{chunk-TYLNWE4D.js.map → chunk-B7VKRCAG.js.map} +0 -0
  328. /package/dist/{chunk-HXFIILGF.js.map → chunk-BFPF5N3H.js.map} +0 -0
  329. /package/dist/{chunk-KQAFEZQX.js.map → chunk-BUKK5SWA.js.map} +0 -0
  330. /package/dist/{chunk-53BLBDV6.js.map → chunk-BUKZ7MTN.js.map} +0 -0
  331. /package/dist/{chunk-MCPSMWUC.js.map → chunk-CR72WZ3M.js.map} +0 -0
  332. /package/dist/{chunk-YC2C67F7.js.map → chunk-CRXGQRNA.js.map} +0 -0
  333. /package/dist/{chunk-WRDFYSUC.js.map → chunk-CZVNOTLR.js.map} +0 -0
  334. /package/dist/{chunk-VPO53QMG.js.map → chunk-D6DOLQYK.js.map} +0 -0
  335. /package/dist/{chunk-RGKXF4W3.js.map → chunk-E5IEI2MR.js.map} +0 -0
  336. /package/dist/{chunk-Z2T5BPOZ.js.map → chunk-EIYJK7OD.js.map} +0 -0
  337. /package/dist/{chunk-FRAL3EU4.js.map → chunk-EKPRLTAE.js.map} +0 -0
  338. /package/dist/{chunk-C2KOW2UQ.js.map → chunk-EKR6OO4P.js.map} +0 -0
  339. /package/dist/{chunk-SWZOAQUM.js.map → chunk-F2IU6A2L.js.map} +0 -0
  340. /package/dist/{chunk-2AKIVSHP.js.map → chunk-FAKRTS3Y.js.map} +0 -0
  341. /package/dist/{chunk-MZWHIAOL.js.map → chunk-FC3ZFEXF.js.map} +0 -0
  342. /package/dist/{chunk-7VFNGQ4V.js.map → chunk-FLDXEJFX.js.map} +0 -0
  343. /package/dist/{chunk-JH4FCVWJ.js.map → chunk-G6OFEGUR.js.map} +0 -0
  344. /package/dist/{chunk-L7PGPH4E.js.map → chunk-GCW7UT5I.js.map} +0 -0
  345. /package/dist/{chunk-J7G656MY.js.map → chunk-GE5ZNDLE.js.map} +0 -0
  346. /package/dist/{chunk-63YPKHAU.js.map → chunk-GLB6VV3J.js.map} +0 -0
  347. /package/dist/{chunk-6F5BGIAF.js.map → chunk-GPVGAHXL.js.map} +0 -0
  348. /package/dist/{chunk-XHX67LX6.js.map → chunk-GSBSGT43.js.map} +0 -0
  349. /package/dist/{chunk-645WOU5I.js.map → chunk-H7BZBSP6.js.map} +0 -0
  350. /package/dist/{chunk-GFYFKXWZ.js.map → chunk-HUDPXTXW.js.map} +0 -0
  351. /package/dist/{chunk-L2JQSG5M.js.map → chunk-I7TR2OCU.js.map} +0 -0
  352. /package/dist/{chunk-6SYS5YX3.js.map → chunk-IAOW2L6K.js.map} +0 -0
  353. /package/dist/{chunk-STM4T335.js.map → chunk-IJH6METJ.js.map} +0 -0
  354. /package/dist/{chunk-3XVBGGF3.js.map → chunk-IZRNMXZC.js.map} +0 -0
  355. /package/dist/{chunk-Y5ZHZKZD.js.map → chunk-JM6U3SAE.js.map} +0 -0
  356. /package/dist/{chunk-74AY3S7U.js.map → chunk-JNLYHJG7.js.map} +0 -0
  357. /package/dist/{chunk-QOUKZ2N3.js.map → chunk-JOOGPZJE.js.map} +0 -0
  358. /package/dist/{chunk-EGNONTVT.js.map → chunk-JORSMFL6.js.map} +0 -0
  359. /package/dist/{chunk-JF3MEIHO.js.map → chunk-JQCP5QW3.js.map} +0 -0
  360. /package/dist/{chunk-POGPI5GR.js.map → chunk-JRTKYFFY.js.map} +0 -0
  361. /package/dist/{chunk-5UPOKIFP.js.map → chunk-JT3IXJIL.js.map} +0 -0
  362. /package/dist/{chunk-6DTIYAAN.js.map → chunk-JZFJZLJY.js.map} +0 -0
  363. /package/dist/{chunk-6QOL4G76.js.map → chunk-KQXRVELF.js.map} +0 -0
  364. /package/dist/{chunk-KOAVX2IT.js.map → chunk-L74NWAD4.js.map} +0 -0
  365. /package/dist/{chunk-5DCS4C4S.js.map → chunk-LJXBFVI2.js.map} +0 -0
  366. /package/dist/{chunk-OKKBOTRA.js.map → chunk-LPD6I2U6.js.map} +0 -0
  367. /package/dist/{chunk-N7DVBHU3.js.map → chunk-LR7RW4T6.js.map} +0 -0
  368. /package/dist/{chunk-FZVZYU5C.js.map → chunk-LXN737FX.js.map} +0 -0
  369. /package/dist/{chunk-7ZRRJ6RG.js.map → chunk-MGYZENUY.js.map} +0 -0
  370. /package/dist/{chunk-YQ6SLFNZ.js.map → chunk-MPJDQC4G.js.map} +0 -0
  371. /package/dist/{chunk-5ANUG4JZ.js.map → chunk-NJ2DZYEK.js.map} +0 -0
  372. /package/dist/{chunk-OLH4XCLU.js.map → chunk-O2O4FDAH.js.map} +0 -0
  373. /package/dist/{chunk-CVMAOPAH.js.map → chunk-ODJ2HRFW.js.map} +0 -0
  374. /package/dist/{chunk-2NLLXCJG.js.map → chunk-OWHERGF2.js.map} +0 -0
  375. /package/dist/{chunk-I73ZSORJ.js.map → chunk-OXSTKCYK.js.map} +0 -0
  376. /package/dist/{chunk-RWRHGE75.js.map → chunk-P3YUEPSQ.js.map} +0 -0
  377. /package/dist/{chunk-TPKB7T6Z.js.map → chunk-Q6UZREJK.js.map} +0 -0
  378. /package/dist/{chunk-UJDTXWMT.js.map → chunk-QIMGULGN.js.map} +0 -0
  379. /package/dist/{chunk-E24RUQEN.js.map → chunk-QNIYL5SK.js.map} +0 -0
  380. /package/dist/{chunk-KOY25HUB.js.map → chunk-QYAXMXOB.js.map} +0 -0
  381. /package/dist/{chunk-X247CYL6.js.map → chunk-RIAKNXZB.js.map} +0 -0
  382. /package/dist/{chunk-WCIDCJOQ.js.map → chunk-RO2NNWR5.js.map} +0 -0
  383. /package/dist/{chunk-AYXRK264.js.map → chunk-RROFIAYI.js.map} +0 -0
  384. /package/dist/{chunk-YYGZ4EIY.js.map → chunk-RUAIO5UA.js.map} +0 -0
  385. /package/dist/{chunk-FQFMLOAC.js.map → chunk-RWW5KPIY.js.map} +0 -0
  386. /package/dist/{chunk-E5PVMB3I.js.map → chunk-SDXBR5QO.js.map} +0 -0
  387. /package/dist/{chunk-NH6GZEQN.js.map → chunk-SJHFU52W.js.map} +0 -0
  388. /package/dist/{chunk-FGEY2AGV.js.map → chunk-SRPGA6WN.js.map} +0 -0
  389. /package/dist/{chunk-CUWOSRCS.js.map → chunk-SUKPM6DV.js.map} +0 -0
  390. /package/dist/{chunk-TXP7Q7T5.js.map → chunk-TTQYX7VU.js.map} +0 -0
  391. /package/dist/{chunk-TDCR6PQL.js.map → chunk-TWRPWJJT.js.map} +0 -0
  392. /package/dist/{chunk-I75BWXRZ.js.map → chunk-U66CTIF5.js.map} +0 -0
  393. /package/dist/{chunk-DG4H56SK.js.map → chunk-UIK3U36O.js.map} +0 -0
  394. /package/dist/{chunk-XLEP7SS3.js.map → chunk-UMSH4I3E.js.map} +0 -0
  395. /package/dist/{chunk-X7Y7WX73.js.map → chunk-UVYI6VIX.js.map} +0 -0
  396. /package/dist/{chunk-LHBOFROT.js.map → chunk-V2XMG7GM.js.map} +0 -0
  397. /package/dist/{chunk-ZAP7BKTZ.js.map → chunk-VCOCOBUS.js.map} +0 -0
  398. /package/dist/{chunk-4P4VLPGB.js.map → chunk-VECRSYQG.js.map} +0 -0
  399. /package/dist/{chunk-GGI6FLO6.js.map → chunk-VHBGJAYC.js.map} +0 -0
  400. /package/dist/{chunk-QO5KDQGG.js.map → chunk-VIS23B6O.js.map} +0 -0
  401. /package/dist/{chunk-NMKETOAX.js.map → chunk-VSHWPOUB.js.map} +0 -0
  402. /package/dist/{chunk-UBN5AOLT.js.map → chunk-W5EO4USJ.js.map} +0 -0
  403. /package/dist/{chunk-Y7427SL7.js.map → chunk-WCGJK4NQ.js.map} +0 -0
  404. /package/dist/{chunk-7HXZOWTR.js.map → chunk-WQYS47G4.js.map} +0 -0
  405. /package/dist/{chunk-JH2ALMPR.js.map → chunk-WR5POHQ3.js.map} +0 -0
  406. /package/dist/{chunk-VSSQIBVU.js.map → chunk-WT2VVYBM.js.map} +0 -0
  407. /package/dist/{chunk-EHIKY2IA.js.map → chunk-WU5WTCH5.js.map} +0 -0
  408. /package/dist/{chunk-QKXPOLQD.js.map → chunk-WUBGMV3G.js.map} +0 -0
  409. /package/dist/{chunk-FKTMWDN2.js.map → chunk-WUL6LDJ4.js.map} +0 -0
  410. /package/dist/{chunk-U7HJBODU.js.map → chunk-XKTHJHSQ.js.map} +0 -0
  411. /package/dist/{chunk-JFI3ATK4.js.map → chunk-XMYWYJM2.js.map} +0 -0
  412. /package/dist/{chunk-64S6X4IU.js.map → chunk-XTN54NML.js.map} +0 -0
  413. /package/dist/{chunk-VGFNTY3Y.js.map → chunk-YFXRLRKP.js.map} +0 -0
  414. /package/dist/{chunk-DAOMWGO2.js.map → chunk-ZX5N5D6M.js.map} +0 -0
  415. /package/dist/{chunk-VGRM43X6.js.map → chunk-ZXFKG7VZ.js.map} +0 -0
  416. /package/dist/{resolution-4ZEQPXUZ.js.map → extraction-window-drain.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/secure-store/secure-fs.ts","../src/secure-store/entity-file-read.ts"],"sourcesContent":["/**\n * Transparent file-level encryption for the secure-store module.\n *\n * Issue #690 (PR 3/4) — storage.ts integration layer.\n *\n * This module sits between the raw filesystem and StorageManager.\n * Every memory file is either:\n * - a plain UTF-8 text file (legacy, back-compat), or\n * - a REMNIC-ENC sealed file (AES-256-GCM, see format below).\n *\n * On-disk format\n * --------------\n * Encrypted files begin with a 9-byte magic header:\n *\n * REMNIC-ENC (7 ASCII bytes)\n * VER (1 byte, currently 0x01)\n * FLAGS (1 byte, reserved, must be 0x00)\n *\n * Followed immediately by a `seal()` envelope from `cipher.ts`:\n *\n * [VERSION:1][SALT:16][IV:12][AUTHTAG:16][CIPHERTEXT:...]\n *\n * The magic header makes encrypted files sniffable without attempting\n * a full `open()` call and gives operators a clear signal that the\n * file cannot be read by opening it in an editor.\n *\n * AAD\n * ---\n * The file path relative to the memory root is bound as Associated\n * Authenticated Data (AAD) on both encrypt and decrypt. This means\n * moving or renaming an encrypted file without re-encrypting it will\n * cause auth-tag failure on the next read — the file is tied to its\n * path. Callers that move files must re-encrypt them.\n *\n * Back-compat\n * -----------\n * `readMaybeEncryptedFile` transparently handles both formats: if the\n * file does NOT start with the magic bytes, it is returned as-is (plain\n * text). This lets an operator migrate incrementally: newly-written\n * files are encrypted while existing files continue to be read in plain\n * form until `migrateMemoryDirToEncrypted` is run.\n *\n * Naming: `secure-fs.ts` (not `vault-fs.ts`) — see `kdf.ts` naming note.\n */\n\nimport { createCipheriv, createDecipheriv, randomBytes, randomUUID } from \"node:crypto\";\nimport { lstat, mkdir, open as openFile, readFile, readdir, rename, unlink, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport {\n AUTH_TAG_LENGTH,\n ENVELOPE_HEADER_SIZE,\n ENVELOPE_LAYOUT,\n ENVELOPE_SALT_LENGTH,\n ENVELOPE_VERSION,\n IV_LENGTH,\n generateSalt,\n open as openEnvelope,\n parseEnvelope,\n seal,\n} from \"./cipher.js\";\nimport { RECALL_FALLBACK_DIRS } from \"../utils/category-dir.js\";\nimport { isErrnoCode } from \"../utils/errno.js\";\nimport { readStoreFileBufferTolerant as readStoreFileBuffer } from \"./entity-file-read.js\";\n\n// ---------------------------------------------------------------------------\n// Error classes\n// ---------------------------------------------------------------------------\n\n/**\n * Thrown when a read is attempted but the keyring entry for this\n * store is absent (i.e. `secure-store unlock` has not been run\n * since the last daemon start).\n */\nexport class SecureStoreLockedError extends Error {\n constructor(message = \"secure-store is locked — run `remnic secure-store unlock` to decrypt\") {\n super(message);\n this.name = \"SecureStoreLockedError\";\n }\n}\n\n/**\n * Thrown when `open()` fails because the auth tag does not validate.\n * This covers both wrong-key and tampered-ciphertext scenarios —\n * intentionally indistinguishable from the caller's perspective.\n */\nexport class SecureStoreDecryptError extends Error {\n constructor(message = \"secure-store decryption failed — wrong key or tampered ciphertext\") {\n super(message);\n this.name = \"SecureStoreDecryptError\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Magic header\n// ---------------------------------------------------------------------------\n\n/** Magic bytes: the ASCII string \"REMNIC-ENC\" (10 bytes). */\nexport const MAGIC_BYTES = Buffer.from(\"REMNIC-ENC\", \"ascii\");\n\n/** Current on-disk version byte. */\nexport const FILE_FORMAT_VERSION = 0x01;\n\n/** Reserved flags byte — must be 0x00. */\nexport const FILE_FORMAT_FLAGS = 0x00;\n\n/** Total size of the magic header prefix (magic + version + flags). */\nexport const MAGIC_HEADER_SIZE = MAGIC_BYTES.length + 2; // 12 bytes\n\n/**\n * Exact, deterministic bytes `writeMaybeEncryptedFile` adds to an encrypted\n * file beyond its plaintext body: the magic header prefix plus the fixed\n * envelope header (version + salt + IV + auth tag). AES-256-GCM ciphertext\n * matches the plaintext length and the state-file write path applies no\n * compression, so an encrypted state file is exactly `plaintext +\n * SECURE_STORE_ENVELOPE_OVERHEAD_BYTES` on disk. Compaction reserves this so an\n * encrypted ledger lands strictly below the reader's whole-file decrypt cap\n * (issue #2033).\n */\nexport const SECURE_STORE_ENVELOPE_OVERHEAD_BYTES = MAGIC_HEADER_SIZE + ENVELOPE_HEADER_SIZE;\n\n// ---------------------------------------------------------------------------\n// Detection\n// ---------------------------------------------------------------------------\n\n/**\n * Return true iff `buf` begins with the REMNIC-ENC magic header.\n * Does not validate the envelope; just identifies the format.\n */\nexport function isEncryptedFile(buf: Uint8Array): boolean {\n if (buf.length < MAGIC_HEADER_SIZE) return false;\n const b = Buffer.isBuffer(buf) ? buf : Buffer.from(buf);\n return b.subarray(0, MAGIC_BYTES.length).equals(MAGIC_BYTES);\n}\n\ntype PositionedFileReader = {\n read: (buffer: Buffer, offset: number, length: number, position: number) => Promise<{ bytesRead: number }>;\n};\n\nasync function readPositionedPrefix(handle: PositionedFileReader, buffer: Buffer): Promise<number> {\n let bytesRead = 0;\n while (bytesRead < buffer.length) {\n const result = await handle.read(buffer, bytesRead, buffer.length - bytesRead, bytesRead);\n if (result.bytesRead === 0) break;\n bytesRead += result.bytesRead;\n }\n return bytesRead;\n}\n\n/**\n * True when the REGULAR file at `probePath` is encrypted at rest (its first\n * bytes are the REMNIC-ENC magic header). ENOENT resolves to false — an absent\n * file has nothing to probe. A symlink, FIFO, device, or any other non-regular\n * path is REFUSED with a throw rather than opened: opening a FIFO blocks until a\n * writer appears and a symlink would redirect the probe outside our tree. The\n * ledgers and per-event spills we manage are always regular files, so a\n * non-regular path is an anomaly the caller must treat as unsafe, never follow\n * (#2033). Shared by the CLI ledger recovery probe and the auto-compaction probe\n * so both enforce identical open safety.\n */\nexport async function probeEncryptedRegularFileHeader(probePath: string): Promise<boolean> {\n let entryStat;\n try {\n entryStat = await lstat(probePath);\n } catch (err) {\n if (isErrnoCode(err, \"ENOENT\")) return false;\n throw err;\n }\n if (!entryStat.isFile()) {\n throw new Error(\n `refusing to probe non-regular state path ${probePath} ` +\n `(symlink/FIFO/device); opening it could block or escape containment (#2033)`\n );\n }\n const handle = await openFile(probePath, \"r\");\n try {\n const header = Buffer.alloc(MAGIC_HEADER_SIZE);\n const bytesRead = await readPositionedPrefix(handle, header);\n return isEncryptedFile(header.subarray(0, bytesRead));\n } finally {\n await handle.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Encrypt / decrypt file body\n// ---------------------------------------------------------------------------\n\n/**\n * Encrypt `plain` (UTF-8 content of a memory file) and return a\n * Buffer ready to write to disk.\n *\n * @param plain Plain-text file content (UTF-8 string or Buffer).\n * @param key 32-byte AES-256 key from the keyring.\n * @param aad Optional associated data — defaults to empty if omitted.\n * Callers should pass the file path relative to memoryDir\n * so the ciphertext is bound to its location.\n */\nexport function encryptFileBody(plain: string | Buffer, key: Buffer, aad?: Buffer): Buffer {\n const plainBuf = typeof plain === \"string\" ? Buffer.from(plain, \"utf8\") : plain;\n const salt = generateSalt();\n const envelope = seal(key, salt, plainBuf, aad ? { aad } : {});\n\n const header = Buffer.alloc(MAGIC_HEADER_SIZE);\n MAGIC_BYTES.copy(header, 0);\n header.writeUInt8(FILE_FORMAT_VERSION, MAGIC_BYTES.length);\n header.writeUInt8(FILE_FORMAT_FLAGS, MAGIC_BYTES.length + 1);\n\n return Buffer.concat([header, envelope]);\n}\n\n/**\n * Decrypt a buffer produced by `encryptFileBody` and return the\n * original UTF-8 content.\n *\n * Throws `SecureStoreDecryptError` on auth failure (wrong key or\n * tampered ciphertext). Throws a plain `Error` for structural problems\n * (truncated buffer, wrong magic, unsupported version).\n */\nexport function decryptFileBody(buf: Buffer, key: Buffer, aad?: Buffer): Buffer {\n if (!isEncryptedFile(buf)) {\n throw new Error(\"decryptFileBody: buffer does not start with REMNIC-ENC magic header\");\n }\n const version = buf.readUInt8(MAGIC_BYTES.length);\n if (version !== FILE_FORMAT_VERSION) {\n throw new Error(\n `decryptFileBody: unsupported file format version ${version} (this build supports ${FILE_FORMAT_VERSION})`\n );\n }\n const flags = buf.readUInt8(MAGIC_BYTES.length + 1);\n if (flags !== FILE_FORMAT_FLAGS) {\n throw new Error(`decryptFileBody: unknown flags byte 0x${flags.toString(16).padStart(2, \"0\")}`);\n }\n const envelope = buf.subarray(MAGIC_HEADER_SIZE);\n parseEnvelope(envelope);\n try {\n return openEnvelope(key, envelope, aad ? { aad } : {});\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new SecureStoreDecryptError(`secure-store decryption failed: ${msg}`);\n }\n}\n\nfunction buildHeaderAad(salt: Uint8Array): Buffer {\n const out = Buffer.alloc(1 + ENVELOPE_SALT_LENGTH);\n out.writeUInt8(ENVELOPE_VERSION, 0);\n Buffer.from(salt).copy(out, 1);\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// Path → AAD helper\n// ---------------------------------------------------------------------------\n\n/**\n * Build the AAD buffer for a file at `filePath` relative to\n * `memoryDir`. The AAD binds the ciphertext to its path so a\n * file cannot be silently relocated without re-encryption.\n *\n * When `memoryDir` is supplied and `filePath` is absolute, the\n * relative sub-path is used. Otherwise `filePath` is used verbatim.\n */\nexport function filePathAad(filePath: string, memoryDir?: string): Buffer {\n let rel = filePath;\n if (memoryDir && path.isAbsolute(filePath)) {\n rel = path.relative(memoryDir, filePath);\n }\n return Buffer.from(rel, \"utf8\");\n}\n\n// ---------------------------------------------------------------------------\n// High-level read / write helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Read a file from `filePath`.\n *\n * - If the file is plaintext (no magic header), return its content\n * as-is — back-compat with unencrypted stores.\n * - If the file is encrypted AND `key` is provided, decrypt and return\n * the plaintext content.\n * - If the file is encrypted AND `key` is null, throw\n * `SecureStoreLockedError`.\n *\n * @param filePath Absolute path to the file.\n * @param key 32-byte AES-256 key, or null when the store is locked.\n * @param memoryDir Memory root for path-bound AAD. Should be absolute.\n */\nexport async function readMaybeEncryptedFileBuffer(\n filePath: string,\n key: Buffer | null,\n memoryDir?: string,\n opts?: { aadFilePath?: string }\n): Promise<Buffer> {\n const buf = memoryDir ? await readStoreFileBuffer(filePath, memoryDir) : await readFile(filePath);\n if (!isEncryptedFile(buf)) {\n // Plain file — legacy or unencrypted store.\n return buf;\n }\n // Encrypted — key required.\n if (key === null) {\n throw new SecureStoreLockedError(\n `secure-store is locked — cannot read encrypted file at ${filePath}. Run \\`remnic secure-store unlock\\` to decrypt.`\n );\n }\n return decryptFileBodyForPath(buf, key, filePath, memoryDir, opts?.aadFilePath);\n}\n\nexport async function readMaybeEncryptedFile(\n filePath: string,\n key: Buffer | null,\n memoryDir?: string\n): Promise<string> {\n return (await readMaybeEncryptedFileBuffer(filePath, key, memoryDir)).toString(\"utf8\");\n}\n\nexport interface WriteMaybeEncryptedFileOptions {\n /**\n * File mode bits. Default 0o600 (owner read/write only).\n * Applied only on create; existing files inherit their existing mode.\n */\n mode?: number;\n /**\n * If true, write atomically via a temp file + rename (CLAUDE.md gotcha #54).\n * Default true.\n */\n atomic?: boolean;\n /**\n * ABSOLUTE final path whose canonical AAD (via {@link filePathAad})\n * replaces the physical `filePath`'s. Used by the offline-sync generation\n * transaction: staged ciphertext physically lives under a staging\n * directory but must decrypt at its FINAL published path after the\n * staging directory is swapped into place by rename. Deriving the AAD\n * from the absolute final path through the SAME filePathAad logic as\n * ordinary reads keeps write and read derivations identical on every\n * platform (POSIX and Windows separators alike). (issue #3148, round 4/5)\n */\n aadFilePath?: string;\n}\n\n/**\n * Write `content` to `filePath`.\n *\n * - If `key` is provided and non-null, encrypt the content first.\n * - If `key` is null, write the content as plain UTF-8 (unencrypted store).\n *\n * Writes atomically: content is written to a unique temp file\n * first, then renamed into place (CLAUDE.md gotcha #54 — never delete\n * before write).\n */\nexport async function writeMaybeEncryptedFile(\n filePath: string,\n content: string | Buffer,\n key: Buffer | null,\n options: WriteMaybeEncryptedFileOptions = {},\n memoryDir?: string\n): Promise<void> {\n const { mode = 0o600, atomic = true, aadFilePath } = options;\n await mkdir(path.dirname(filePath), { recursive: true });\n\n let data: Buffer | string;\n if (key !== null) {\n const aad = aadFilePath !== undefined ? filePathAad(aadFilePath, memoryDir) : filePathAad(filePath, memoryDir);\n data = encryptFileBody(content, key, aad);\n } else {\n data = content;\n }\n\n if (atomic) {\n const tempPath = uniqueAtomicTempPath(filePath, \"tmp\");\n try {\n await writeFile(tempPath, data, { mode });\n await rename(tempPath, filePath);\n } catch (err) {\n // Best-effort cleanup of the temp file.\n try {\n await unlink(tempPath);\n } catch {\n // ignore\n }\n throw err;\n }\n } else {\n await writeFile(filePath, data, { mode });\n }\n}\n\nexport async function writeMaybeEncryptedFileFromChunks(\n filePath: string,\n chunks: AsyncIterable<Buffer>,\n key: Buffer | null,\n options: WriteMaybeEncryptedFileOptions = {},\n memoryDir?: string\n): Promise<void> {\n const { mode = 0o600, atomic = true } = options;\n await mkdir(path.dirname(filePath), { recursive: true });\n const writePath = atomic ? `${filePath}.tmp-${process.pid}-${Date.now()}` : filePath;\n let completed = false;\n try {\n const handle = await openFile(writePath, \"w\", mode);\n try {\n if (key !== null) {\n const salt = generateSalt();\n const iv = randomBytes(IV_LENGTH);\n const header = Buffer.alloc(MAGIC_HEADER_SIZE + ENVELOPE_HEADER_SIZE);\n MAGIC_BYTES.copy(header, 0);\n header.writeUInt8(FILE_FORMAT_VERSION, MAGIC_BYTES.length);\n header.writeUInt8(FILE_FORMAT_FLAGS, MAGIC_BYTES.length + 1);\n const envelopeOffset = MAGIC_HEADER_SIZE;\n header.writeUInt8(ENVELOPE_VERSION, envelopeOffset + ENVELOPE_LAYOUT.version);\n salt.copy(header, envelopeOffset + ENVELOPE_LAYOUT.salt);\n iv.copy(header, envelopeOffset + ENVELOPE_LAYOUT.iv);\n await handle.write(header);\n\n const cipher = createCipheriv(\"aes-256-gcm\", key, iv, { authTagLength: AUTH_TAG_LENGTH });\n const aad = filePathAad(filePath, memoryDir);\n cipher.setAAD(Buffer.concat([buildHeaderAad(salt), aad]));\n for await (const chunk of chunks) {\n if (chunk.length === 0) continue;\n const encrypted = cipher.update(chunk);\n if (encrypted.length > 0) await handle.write(encrypted);\n }\n const final = cipher.final();\n if (final.length > 0) await handle.write(final);\n const authTag = cipher.getAuthTag();\n await handle.write(authTag, 0, authTag.length, MAGIC_HEADER_SIZE + ENVELOPE_LAYOUT.authTag);\n } else {\n for await (const chunk of chunks) {\n if (chunk.length > 0) await handle.write(chunk);\n }\n }\n } finally {\n await handle.close();\n }\n if (atomic) {\n await rename(writePath, filePath);\n }\n completed = true;\n } finally {\n if (!completed && atomic) {\n await unlink(writePath).catch(() => {});\n }\n }\n}\n\n/**\n * Stream a file through the secure-store reader without buffering the whole\n * payload. Plaintext files are passed through unchanged; encrypted files are\n * authenticated and decrypted incrementally.\n */\nexport async function* readMaybeEncryptedFileFromChunks(\n filePath: string,\n key: Buffer | null,\n memoryDir?: string,\n chunkSize = 64 * 1024\n): AsyncIterable<Buffer> {\n const handle = await openFile(filePath, \"r\");\n const headerSize = MAGIC_HEADER_SIZE + ENVELOPE_HEADER_SIZE;\n try {\n const prefix = Buffer.alloc(headerSize);\n const prefixBytes = await readPositionedPrefix(handle, prefix);\n const prefixView = prefix.subarray(0, prefixBytes);\n if (!isEncryptedFile(prefixView)) {\n if (prefixBytes > 0) yield Buffer.from(prefixView);\n let position = prefixBytes;\n const buffer = Buffer.alloc(chunkSize);\n for (;;) {\n const { bytesRead } = await handle.read(buffer, 0, buffer.length, position);\n if (bytesRead === 0) break;\n position += bytesRead;\n yield Buffer.from(buffer.subarray(0, bytesRead));\n }\n return;\n }\n if (prefixBytes < headerSize) {\n throw new Error(`encrypted file header is truncated: ${filePath}`);\n }\n if (key === null) {\n throw new SecureStoreLockedError(\n `secure-store is locked — cannot read encrypted file at ${filePath}. ` +\n \"Run `remnic secure-store unlock` to decrypt.\"\n );\n }\n const envelope = prefix.subarray(MAGIC_HEADER_SIZE);\n const version = envelope.readUInt8(ENVELOPE_LAYOUT.version);\n if (version !== ENVELOPE_VERSION) {\n throw new Error(`secure-store: unsupported envelope version ${version}`);\n }\n const salt = envelope.subarray(ENVELOPE_LAYOUT.salt, ENVELOPE_LAYOUT.salt + ENVELOPE_SALT_LENGTH);\n const iv = envelope.subarray(ENVELOPE_LAYOUT.iv, ENVELOPE_LAYOUT.iv + IV_LENGTH);\n const authTag = envelope.subarray(ENVELOPE_LAYOUT.authTag, ENVELOPE_LAYOUT.authTag + AUTH_TAG_LENGTH);\n const decipher = createDecipheriv(\"aes-256-gcm\", key, iv, { authTagLength: AUTH_TAG_LENGTH });\n decipher.setAAD(Buffer.concat([buildHeaderAad(salt), filePathAad(filePath, memoryDir)]));\n decipher.setAuthTag(authTag);\n let position = headerSize;\n const buffer = Buffer.alloc(chunkSize);\n try {\n for (;;) {\n const { bytesRead } = await handle.read(buffer, 0, buffer.length, position);\n if (bytesRead === 0) break;\n position += bytesRead;\n const plain = decipher.update(buffer.subarray(0, bytesRead));\n if (plain.length > 0) yield plain;\n }\n const final = decipher.final();\n if (final.length > 0) yield final;\n } catch (err) {\n if (err instanceof SecureStoreDecryptError) throw err;\n const message = err instanceof Error ? err.message : String(err);\n throw new SecureStoreDecryptError(`secure-store decryption failed: ${message}`);\n }\n } finally {\n await handle.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Migration\n// ---------------------------------------------------------------------------\n\nexport interface MigrateResult {\n /** Number of files successfully encrypted. */\n encrypted: number;\n /** Number of files already encrypted (skipped). */\n skipped: number;\n /** Files that failed to encrypt (path → error message). */\n errors: Array<{ filePath: string; error: string }>;\n}\n\nexport interface DecryptResult {\n /** Number of files successfully decrypted back to plaintext. */\n decrypted: number;\n /** Number of files already plaintext (skipped). */\n skipped: number;\n /** Files that failed to decrypt (path → error message). */\n errors: Array<{ filePath: string; error: string }>;\n}\n\n/**\n * Walk `dir` recursively, find encryptable storage-managed files that are not\n * yet encrypted, and re-write them as encrypted files under `key`.\n *\n * Safety rules per CLAUDE.md gotchas #54 and #25:\n * 1. A page-version snapshot is taken (via `createVersion`) BEFORE\n * each overwrite so the plaintext version is preserved in history.\n * Since this module has no direct access to `page-versioning.ts`\n * internals, callers who have page-versioning configured should\n * pass `onBeforeEncrypt` to take the snapshot.\n * 2. The new encrypted content is written to a temp file first,\n * then renamed atomically — never deleted before written.\n * 3. If encryption of any file fails, the error is recorded and the\n * original file is left intact (partial migration is safe).\n *\n * @param dir Absolute path to the memory directory.\n * @param key 32-byte AES-256 key.\n * @param onBeforeEncrypt Optional callback invoked before encrypting\n * each file. Can be used to take page-version\n * snapshots. Errors here are non-fatal.\n */\nexport async function migrateMemoryDirToEncrypted(\n dir: string,\n key: Buffer,\n onBeforeEncrypt?: (filePath: string) => Promise<void>\n): Promise<MigrateResult> {\n const result: MigrateResult = { encrypted: 0, skipped: 0, errors: [] };\n\n const files = await collectEncryptableStorageFiles(dir);\n for (const filePath of files) {\n try {\n const buf = await readFile(filePath);\n if (isEncryptedFile(buf)) {\n result.skipped++;\n continue;\n }\n // Call optional pre-encryption hook (e.g. page-version snapshot).\n if (onBeforeEncrypt) {\n try {\n await onBeforeEncrypt(filePath);\n } catch {\n // Non-fatal — continue with encryption even if snapshot fails.\n }\n }\n const content = buf.toString(\"utf8\");\n const aad = filePathAad(filePath, dir);\n const encrypted = encryptFileBody(content, key, aad);\n\n // Atomic write: temp → rename (gotcha #54).\n const tempPath = uniqueAtomicTempPath(filePath, \"enc-tmp\");\n try {\n await writeFile(tempPath, encrypted, { mode: 0o600 });\n await rename(tempPath, filePath);\n result.encrypted++;\n } catch (writeErr) {\n // Clean up temp file, leave original intact.\n try {\n const { unlink } = await import(\"node:fs/promises\");\n await unlink(tempPath);\n } catch {\n // ignore\n }\n throw writeErr;\n }\n } catch (err) {\n result.errors.push({\n filePath,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n return result;\n}\n\n/**\n * Walk `dir` recursively, find storage-managed encrypted files, and\n * re-write them as plaintext under the same paths.\n *\n * This is the reversible counterpart to {@link migrateMemoryDirToEncrypted}.\n * It only touches files under the same storage-managed roots, skips\n * plaintext files, skips symlinks, excludes `.secure-store/`, and writes\n * each plaintext replacement via temp-file + rename so a per-file failure\n * leaves the ciphertext intact.\n */\nexport async function decryptMemoryDirToPlaintext(dir: string, key: Buffer): Promise<DecryptResult> {\n const result: DecryptResult = { decrypted: 0, skipped: 0, errors: [] };\n\n const files = await collectStorageManagedFiles(dir, isDecryptableStoragePath);\n for (const filePath of files) {\n try {\n const buf = await readFile(filePath);\n if (!isEncryptedFile(buf)) {\n result.skipped++;\n continue;\n }\n\n const plaintext = decryptFileBodyForPath(buf, key, filePath, dir);\n const tempPath = uniqueAtomicTempPath(filePath, \"dec-tmp\");\n try {\n await writeFile(tempPath, plaintext, { mode: 0o600 });\n await rename(tempPath, filePath);\n result.decrypted++;\n } catch (writeErr) {\n try {\n await unlink(tempPath);\n } catch {\n // ignore cleanup errors; original ciphertext is still intact.\n }\n throw writeErr;\n }\n } catch (err) {\n result.errors.push({\n filePath,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction uniqueAtomicTempPath(filePath: string, label: string): string {\n return `${filePath}.${label}-${process.pid}-${Date.now()}-${randomUUID()}`;\n}\n\nfunction decryptFileBodyForPath(\n buf: Buffer,\n key: Buffer,\n filePath: string,\n memoryDir?: string,\n aadFilePath?: string\n): Buffer {\n // A canonical-AAD stage read (offline-sync generation transaction) binds\n // the FINAL published path, not the physical staging path.\n const aad = aadFilePath !== undefined ? filePathAad(aadFilePath, memoryDir) : filePathAad(filePath, memoryDir);\n try {\n return decryptFileBody(buf, key, aad);\n } catch (err) {\n if (!(err instanceof SecureStoreDecryptError)) {\n throw err;\n }\n const legacyRoot = legacyNamespaceAadRootForFile(filePath, memoryDir);\n if (legacyRoot) {\n try {\n return decryptFileBody(buf, key, filePathAad(filePath, legacyRoot));\n } catch {\n // Fall through to the namespace-reader fallback below.\n }\n }\n\n const topLevelRoot = topLevelAadRootForNamespaceReader(filePath, memoryDir);\n if (topLevelRoot) {\n try {\n return decryptFileBody(buf, key, filePathAad(filePath, topLevelRoot));\n } catch {\n // Preserve the caller-facing error from the canonical decrypt attempt.\n }\n }\n\n throw err;\n }\n}\n\nfunction topLevelAadRootForNamespaceReader(filePath: string, memoryDir?: string): string | null {\n if (!memoryDir || !path.isAbsolute(filePath)) return null;\n const resolvedMemoryDir = path.resolve(memoryDir);\n const rel = path.relative(resolvedMemoryDir, filePath);\n if (rel === \"\" || rel.startsWith(\"..\") || path.isAbsolute(rel)) return null;\n const parts = resolvedMemoryDir.split(path.sep);\n if (parts.length < 3 || parts.at(-2) !== \"namespaces\" || !parts.at(-1)) return null;\n const topLevelRoot = parts.slice(0, -2).join(path.sep) || path.sep;\n const topRel = path.relative(topLevelRoot, filePath);\n if (topRel === \"\" || topRel.startsWith(\"..\") || path.isAbsolute(topRel)) {\n return null;\n }\n const topParts = topRel.split(path.sep);\n if (topParts[0] !== \"namespaces\" || topParts[1] !== parts.at(-1)) {\n return null;\n }\n return topLevelRoot;\n}\n\nfunction legacyNamespaceAadRootForFile(filePath: string, memoryDir?: string): string | null {\n if (!memoryDir || !path.isAbsolute(filePath)) return null;\n const rel = path.relative(memoryDir, filePath);\n if (rel === \"\" || rel.startsWith(\"..\") || path.isAbsolute(rel)) return null;\n const parts = rel.split(path.sep);\n if (parts[0] === \"namespaces\" && parts.length >= 3 && parts[1]) {\n return path.join(memoryDir, \"namespaces\", parts[1]);\n }\n return null;\n}\n\n/**\n * Recursively collect files under `dir` that are read through the\n * storage-layer secure-store helpers, excluding symlinked entries and\n * `.secure-store/` metadata.\n */\nasync function collectEncryptableStorageFiles(dir: string, rootDir = dir): Promise<string[]> {\n return collectStorageManagedFiles(dir, isEncryptableStoragePath, rootDir);\n}\n\n/**\n * Recursively collect regular files under storage-managed roots, excluding\n * symlinked entries and `.secure-store/` metadata. This broader collector is\n * used by the decrypt/disable path so future encrypted sidecars can be\n * restored without requiring extension-specific logic.\n */\nasync function collectStorageManagedFiles(\n dir: string,\n includeFile: (filePath: string, rootDir: string) => boolean,\n rootDir = dir\n): Promise<string[]> {\n const results: string[] = [];\n let scanDir = dir;\n let scanRootDir = rootDir;\n if (path.resolve(dir) === path.resolve(rootDir)) {\n const normalizedRoot = await resolveStorageManagedRootForScan(dir);\n if (!normalizedRoot) return results;\n scanDir = normalizedRoot;\n scanRootDir = normalizedRoot;\n }\n let names: string[];\n try {\n names = await readdir(scanDir, { encoding: \"utf8\" });\n } catch {\n return results;\n }\n for (const name of names) {\n if (name.startsWith(\".secure-store\")) continue;\n const full = path.join(scanDir, name);\n let isDir = false;\n let isFile = false;\n try {\n const s = await lstat(full);\n if (s.isSymbolicLink()) continue;\n isDir = s.isDirectory();\n isFile = s.isFile();\n } catch {\n continue;\n }\n if (isDir) {\n const sub = await collectStorageManagedFiles(full, includeFile, scanRootDir);\n results.push(...sub);\n } else if (isFile && includeFile(full, scanRootDir)) {\n results.push(full);\n }\n }\n return results;\n}\n\nasync function resolveStorageManagedRootForScan(dir: string): Promise<string | null> {\n const lstatPath = normalizePathForLstat(dir);\n let stat: Awaited<ReturnType<typeof lstat>>;\n try {\n stat = await lstat(lstatPath);\n } catch (error) {\n if (isFsErrorWithCode(error, \"ENOENT\")) return null;\n throw error;\n }\n if (stat.isSymbolicLink()) {\n throw new Error(`secure-store migration root must not be a symlink: ${dir}`);\n }\n if (!stat.isDirectory()) {\n throw new Error(`secure-store migration root must be a directory: ${dir}`);\n }\n return lstatPath;\n}\n\nfunction normalizePathForLstat(filePath: string): string {\n return stripTrailingPathSeparators(path.normalize(filePath));\n}\n\nfunction isFsErrorWithCode(error: unknown, code: string): boolean {\n return typeof error === \"object\" && error !== null && (error as { code?: unknown }).code === code;\n}\n\nfunction stripTrailingPathSeparators(filePath: string): string {\n const root = path.parse(filePath).root;\n let end = filePath.length;\n while (\n end > root.length &&\n (filePath[end - 1] === path.sep || filePath[end - 1] === path.posix.sep || filePath[end - 1] === path.win32.sep)\n ) {\n end -= 1;\n }\n return end === filePath.length ? filePath : filePath.slice(0, end);\n}\n\nfunction isEncryptableStoragePath(filePath: string, rootDir: string): boolean {\n const rel = path.relative(rootDir, filePath);\n if (rel === \"\" || rel.startsWith(\"..\") || path.isAbsolute(rel)) return false;\n const normalized = normalizeStorageRelativePath(rel);\n if (normalized === \"profile.md\") return true;\n if (isEncryptableStateSidecar(normalized)) return true;\n if (isEncryptableBufferSessionFile(normalized)) return true;\n if (isEncryptableSummarySidecar(normalized)) return true;\n const firstSegment = normalized.split(\"/\", 1)[0];\n return ENCRYPTABLE_MARKDOWN_STORAGE_ROOTS.has(firstSegment) && normalized.endsWith(\".md\");\n}\n\nfunction isDecryptableStoragePath(filePath: string, rootDir: string): boolean {\n if (isEncryptableStoragePath(filePath, rootDir)) return true;\n const rel = path.relative(rootDir, filePath);\n if (rel === \"\" || rel.startsWith(\"..\") || path.isAbsolute(rel)) return false;\n const normalized = normalizeStorageRelativePath(rel);\n const firstSegment = normalized.split(\"/\", 1)[0];\n return DECRYPTABLE_SIDECAR_ROOTS.has(firstSegment);\n}\n\nfunction normalizeStorageRelativePath(rel: string): string {\n const normalized = rel.split(path.sep).join(\"/\");\n const parts = normalized.split(\"/\");\n if (parts[0] === \"namespaces\" && parts.length >= 3) {\n return parts.slice(2).join(\"/\");\n }\n return normalized;\n}\n\n// Every recall MEMORY category directory (facts/ + the CATEGORY_DIR_MAP memory\n// dirs) must be encryptable at rest — #1546 routes decision/preference/moment/...\n// memories into their own dirs, so hardcoding only facts/corrections/procedures/\n// reasoning-traces would silently write those categories in plaintext on\n// encrypted stores. RECALL_FALLBACK_DIRS is the single source of truth for that\n// set; the extra non-category markdown roots (artifacts/archive/entities/\n// identity) are appended.\n//\n// questions/ is deliberately EXCLUDED (RECALL_FALLBACK_DIRS omits the non-memory\n// queue dirs): the question queue is written/resolved through plain readFile/\n// writeFile (StorageManager.writeQuestion/resolveQuestion), NOT the secure-file\n// helpers. Encrypting questions/ here would make migration seal those files,\n// after which resolveQuestion() would read ciphertext as UTF-8, fail to update\n// the frontmatter, and overwrite/corrupt the file while returning success\n// (codex #1563 review). Keeping questions/ plaintext preserves the queue's\n// existing behavior; #1546 does not change question encryption.\nconst ENCRYPTABLE_MARKDOWN_STORAGE_ROOTS = new Set<string>([\n ...RECALL_FALLBACK_DIRS,\n \"artifacts\",\n \"archive\",\n \"entities\",\n \"identity\",\n]);\n\nconst ENCRYPTABLE_STATE_SIDECARS = new Set([\n \"state/behavior-signals.jsonl\",\n \"state/buffer-surprise-ledger.jsonl\",\n \"state/buffer.json\",\n \"state/compression-guideline-draft-state.json\",\n \"state/compression-guideline-state.json\",\n \"state/compression-guidelines.draft.md\",\n \"state/compression-guidelines.md\",\n \"state/entity-synthesis-queue.json\",\n \"state/fact-hashes.txt\",\n \"state/memory-actions.jsonl\",\n \"state/memory-lifecycle-ledger.jsonl\",\n \"state/meta.json\",\n \"state/reextract-jobs.jsonl\",\n \"state/topics.json\",\n]);\n\nfunction isEncryptableStateSidecar(normalized: string): boolean {\n return ENCRYPTABLE_STATE_SIDECARS.has(normalized);\n}\n\n/**\n * Per-session buffer state files (`state/buffer/<session>.json`) hold the same\n * buffered turns the legacy `state/buffer.json` held, so they are encryptable\n * state sidecars too — otherwise enabling the secure store would leave the\n * daemon's buffered turns in plaintext on disk.\n */\nfunction isEncryptableBufferSessionFile(normalized: string): boolean {\n return normalized.startsWith(\"state/buffer/\") && normalized.endsWith(\".json\");\n}\n\nfunction isEncryptableSummarySidecar(normalized: string): boolean {\n return normalized.startsWith(\"summaries/\") && normalized.endsWith(\".json\");\n}\n\nconst DECRYPTABLE_SIDECAR_ROOTS = new Set([\"state\", \"indexes\", \"index\", \"provenance\"]);\n","import { constants } from \"node:fs\";\nimport { lstat, open, readFile, realpath } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { assertPathInsideRoot } from \"../utils/path-containment.js\";\n\nexport async function readStoreFileBuffer(filePath: string, memoryDir: string): Promise<Buffer> {\n const root = path.resolve(memoryDir);\n const entities = path.join(root, \"entities\");\n const target = path.resolve(filePath);\n const relative = path.relative(entities, target);\n if (relative === \"..\" || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {\n return readFile(filePath);\n }\n const handle = await open(target, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0) | (constants.O_NONBLOCK ?? 0));\n try {\n for (let parent = path.dirname(target); ; parent = path.dirname(parent)) {\n const info = await lstat(parent);\n if (info.isSymbolicLink() || !info.isDirectory()) {\n throw new Error(`Refusing unsafe entity directory: ${parent}.`);\n }\n if (parent === root) break;\n }\n const [opened, entry, realRoot, realEntities, realTarget] = await Promise.all([\n handle.stat(), lstat(target), realpath(root), realpath(entities), realpath(target),\n ]);\n if (!opened.isFile() || entry.isSymbolicLink() || !entry.isFile()\n || opened.dev !== entry.dev || opened.ino !== entry.ino\n || realEntities !== path.join(realRoot, \"entities\")) {\n throw new Error(`Refusing unsafe entity file: ${filePath}.`);\n }\n assertPathInsideRoot(realEntities, realTarget, filePath);\n return await handle.readFile();\n } finally {\n await handle.close();\n }\n}\n\n\n/**\n * Atomic-replacement-tolerant variant: entity writes ship as temp-file +\n * rename, so an open/lstat identity mismatch during a concurrent write is an\n * ordinary race, not tampering. Retry a bounded number of times with fresh\n * containment checks before surfacing the mismatch as unsafe.\n */\nexport async function readStoreFileBufferTolerant(filePath: string, memoryDir: string, attempts = 3): Promise<Buffer> {\n for (let attempt = 0; ; attempt += 1) {\n try {\n return await readStoreFileBuffer(filePath, memoryDir);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const identityRace = /unsafe entity (?:file|directory)/.test(message);\n if (!identityRace || attempt >= attempts - 1) throw err;\n }\n }\n}\n"],"mappings":"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","names":["lstat","readFile","path","open","open","lstat","path","readFile","position","buffer","unlink"]}
1
+ {"version":3,"sources":["../src/secure-store/secure-fs.ts","../src/secure-store/entity-file-read.ts"],"sourcesContent":["/**\n * Transparent file-level encryption for the secure-store module.\n *\n * Issue #690 (PR 3/4) — storage.ts integration layer.\n *\n * This module sits between the raw filesystem and StorageManager.\n * Every memory file is either:\n * - a plain UTF-8 text file (legacy, back-compat), or\n * - a REMNIC-ENC sealed file (AES-256-GCM, see format below).\n *\n * On-disk format\n * --------------\n * Encrypted files begin with a 9-byte magic header:\n *\n * REMNIC-ENC (7 ASCII bytes)\n * VER (1 byte, currently 0x01)\n * FLAGS (1 byte, reserved, must be 0x00)\n *\n * Followed immediately by a `seal()` envelope from `cipher.ts`:\n *\n * [VERSION:1][SALT:16][IV:12][AUTHTAG:16][CIPHERTEXT:...]\n *\n * The magic header makes encrypted files sniffable without attempting\n * a full `open()` call and gives operators a clear signal that the\n * file cannot be read by opening it in an editor.\n *\n * AAD\n * ---\n * The file path relative to the memory root is bound as Associated\n * Authenticated Data (AAD) on both encrypt and decrypt. This means\n * moving or renaming an encrypted file without re-encrypting it will\n * cause auth-tag failure on the next read — the file is tied to its\n * path. Callers that move files must re-encrypt them.\n *\n * Back-compat\n * -----------\n * `readMaybeEncryptedFile` transparently handles both formats: if the\n * file does NOT start with the magic bytes, it is returned as-is (plain\n * text). This lets an operator migrate incrementally: newly-written\n * files are encrypted while existing files continue to be read in plain\n * form until `migrateMemoryDirToEncrypted` is run.\n *\n * Naming: `secure-fs.ts` (not `vault-fs.ts`) — see `kdf.ts` naming note.\n */\n\nimport { createCipheriv, createDecipheriv, randomBytes, randomUUID } from \"node:crypto\";\nimport { lstat, mkdir, open as openFile, readFile, readdir, rename, unlink, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport {\n AUTH_TAG_LENGTH,\n ENVELOPE_HEADER_SIZE,\n ENVELOPE_LAYOUT,\n ENVELOPE_SALT_LENGTH,\n ENVELOPE_VERSION,\n IV_LENGTH,\n generateSalt,\n open as openEnvelope,\n parseEnvelope,\n seal,\n} from \"./cipher.js\";\nimport { RECALL_FALLBACK_DIRS } from \"../utils/category-dir.js\";\nimport { isErrnoCode } from \"../utils/errno.js\";\nimport { readStoreFileBufferTolerant as readStoreFileBuffer } from \"./entity-file-read.js\";\n\n// ---------------------------------------------------------------------------\n// Error classes\n// ---------------------------------------------------------------------------\n\n/**\n * Thrown when a read is attempted but the keyring entry for this\n * store is absent (i.e. `secure-store unlock` has not been run\n * since the last daemon start).\n */\nexport class SecureStoreLockedError extends Error {\n constructor(message = \"secure-store is locked — run `remnic secure-store unlock` to decrypt\") {\n super(message);\n this.name = \"SecureStoreLockedError\";\n }\n}\n\n/**\n * Thrown when `open()` fails because the auth tag does not validate.\n * This covers both wrong-key and tampered-ciphertext scenarios —\n * intentionally indistinguishable from the caller's perspective.\n */\nexport class SecureStoreDecryptError extends Error {\n constructor(message = \"secure-store decryption failed — wrong key or tampered ciphertext\") {\n super(message);\n this.name = \"SecureStoreDecryptError\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Magic header\n// ---------------------------------------------------------------------------\n\n/** Magic bytes: the ASCII string \"REMNIC-ENC\" (10 bytes). */\nexport const MAGIC_BYTES = Buffer.from(\"REMNIC-ENC\", \"ascii\");\n\n/** Current on-disk version byte. */\nexport const FILE_FORMAT_VERSION = 0x01;\n\n/** Reserved flags byte — must be 0x00. */\nexport const FILE_FORMAT_FLAGS = 0x00;\n\n/** Total size of the magic header prefix (magic + version + flags). */\nexport const MAGIC_HEADER_SIZE = MAGIC_BYTES.length + 2; // 12 bytes\n\n/**\n * Exact, deterministic bytes `writeMaybeEncryptedFile` adds to an encrypted\n * file beyond its plaintext body: the magic header prefix plus the fixed\n * envelope header (version + salt + IV + auth tag). AES-256-GCM ciphertext\n * matches the plaintext length and the state-file write path applies no\n * compression, so an encrypted state file is exactly `plaintext +\n * SECURE_STORE_ENVELOPE_OVERHEAD_BYTES` on disk. Compaction reserves this so an\n * encrypted ledger lands strictly below the reader's whole-file decrypt cap\n * (issue #2033).\n */\nexport const SECURE_STORE_ENVELOPE_OVERHEAD_BYTES = MAGIC_HEADER_SIZE + ENVELOPE_HEADER_SIZE;\n\n// ---------------------------------------------------------------------------\n// Detection\n// ---------------------------------------------------------------------------\n\n/**\n * Return true iff `buf` begins with the REMNIC-ENC magic header.\n * Does not validate the envelope; just identifies the format.\n */\nexport function isEncryptedFile(buf: Uint8Array): boolean {\n if (buf.length < MAGIC_HEADER_SIZE) return false;\n const b = Buffer.isBuffer(buf) ? buf : Buffer.from(buf);\n return b.subarray(0, MAGIC_BYTES.length).equals(MAGIC_BYTES);\n}\n\ntype PositionedFileReader = {\n read: (buffer: Buffer, offset: number, length: number, position: number) => Promise<{ bytesRead: number }>;\n};\n\nasync function readPositionedPrefix(handle: PositionedFileReader, buffer: Buffer): Promise<number> {\n let bytesRead = 0;\n while (bytesRead < buffer.length) {\n const result = await handle.read(buffer, bytesRead, buffer.length - bytesRead, bytesRead);\n if (result.bytesRead === 0) break;\n bytesRead += result.bytesRead;\n }\n return bytesRead;\n}\n\n/**\n * True when the REGULAR file at `probePath` is encrypted at rest (its first\n * bytes are the REMNIC-ENC magic header). ENOENT resolves to false — an absent\n * file has nothing to probe. A symlink, FIFO, device, or any other non-regular\n * path is REFUSED with a throw rather than opened: opening a FIFO blocks until a\n * writer appears and a symlink would redirect the probe outside our tree. The\n * ledgers and per-event spills we manage are always regular files, so a\n * non-regular path is an anomaly the caller must treat as unsafe, never follow\n * (#2033). Shared by the CLI ledger recovery probe and the auto-compaction probe\n * so both enforce identical open safety.\n */\nexport async function probeEncryptedRegularFileHeader(probePath: string): Promise<boolean> {\n let entryStat;\n try {\n entryStat = await lstat(probePath);\n } catch (err) {\n if (isErrnoCode(err, \"ENOENT\")) return false;\n throw err;\n }\n if (!entryStat.isFile()) {\n throw new Error(\n `refusing to probe non-regular state path ${probePath} ` +\n `(symlink/FIFO/device); opening it could block or escape containment (#2033)`\n );\n }\n const handle = await openFile(probePath, \"r\");\n try {\n const header = Buffer.alloc(MAGIC_HEADER_SIZE);\n const bytesRead = await readPositionedPrefix(handle, header);\n return isEncryptedFile(header.subarray(0, bytesRead));\n } finally {\n await handle.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Encrypt / decrypt file body\n// ---------------------------------------------------------------------------\n\n/**\n * Encrypt `plain` (UTF-8 content of a memory file) and return a\n * Buffer ready to write to disk.\n *\n * @param plain Plain-text file content (UTF-8 string or Buffer).\n * @param key 32-byte AES-256 key from the keyring.\n * @param aad Optional associated data — defaults to empty if omitted.\n * Callers should pass the file path relative to memoryDir\n * so the ciphertext is bound to its location.\n */\nexport function encryptFileBody(plain: string | Buffer, key: Buffer, aad?: Buffer): Buffer {\n const plainBuf = typeof plain === \"string\" ? Buffer.from(plain, \"utf8\") : plain;\n const salt = generateSalt();\n const envelope = seal(key, salt, plainBuf, aad ? { aad } : {});\n\n const header = Buffer.alloc(MAGIC_HEADER_SIZE);\n MAGIC_BYTES.copy(header, 0);\n header.writeUInt8(FILE_FORMAT_VERSION, MAGIC_BYTES.length);\n header.writeUInt8(FILE_FORMAT_FLAGS, MAGIC_BYTES.length + 1);\n\n return Buffer.concat([header, envelope]);\n}\n\n/**\n * Decrypt a buffer produced by `encryptFileBody` and return the\n * original UTF-8 content.\n *\n * Throws `SecureStoreDecryptError` on auth failure (wrong key or\n * tampered ciphertext). Throws a plain `Error` for structural problems\n * (truncated buffer, wrong magic, unsupported version).\n */\nexport function decryptFileBody(buf: Buffer, key: Buffer, aad?: Buffer): Buffer {\n if (!isEncryptedFile(buf)) {\n throw new Error(\"decryptFileBody: buffer does not start with REMNIC-ENC magic header\");\n }\n const version = buf.readUInt8(MAGIC_BYTES.length);\n if (version !== FILE_FORMAT_VERSION) {\n throw new Error(\n `decryptFileBody: unsupported file format version ${version} (this build supports ${FILE_FORMAT_VERSION})`\n );\n }\n const flags = buf.readUInt8(MAGIC_BYTES.length + 1);\n if (flags !== FILE_FORMAT_FLAGS) {\n throw new Error(`decryptFileBody: unknown flags byte 0x${flags.toString(16).padStart(2, \"0\")}`);\n }\n const envelope = buf.subarray(MAGIC_HEADER_SIZE);\n parseEnvelope(envelope);\n try {\n return openEnvelope(key, envelope, aad ? { aad } : {});\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new SecureStoreDecryptError(`secure-store decryption failed: ${msg}`);\n }\n}\n\nfunction buildHeaderAad(salt: Uint8Array): Buffer {\n const out = Buffer.alloc(1 + ENVELOPE_SALT_LENGTH);\n out.writeUInt8(ENVELOPE_VERSION, 0);\n Buffer.from(salt).copy(out, 1);\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// Path → AAD helper\n// ---------------------------------------------------------------------------\n\n/**\n * Build the AAD buffer for a file at `filePath` relative to\n * `memoryDir`. The AAD binds the ciphertext to its path so a\n * file cannot be silently relocated without re-encryption.\n *\n * When `memoryDir` is supplied and `filePath` is absolute, the\n * relative sub-path is used. Otherwise `filePath` is used verbatim.\n */\nexport function filePathAad(filePath: string, memoryDir?: string): Buffer {\n let rel = filePath;\n if (memoryDir && path.isAbsolute(filePath)) {\n rel = path.relative(memoryDir, filePath);\n }\n return Buffer.from(rel, \"utf8\");\n}\n\n// ---------------------------------------------------------------------------\n// High-level read / write helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Read a file from `filePath`.\n *\n * - If the file is plaintext (no magic header), return its content\n * as-is — back-compat with unencrypted stores.\n * - If the file is encrypted AND `key` is provided, decrypt and return\n * the plaintext content.\n * - If the file is encrypted AND `key` is null, throw\n * `SecureStoreLockedError`.\n *\n * @param filePath Absolute path to the file.\n * @param key 32-byte AES-256 key, or null when the store is locked.\n * @param memoryDir Memory root for path-bound AAD. Should be absolute.\n */\nexport async function readMaybeEncryptedFileBuffer(\n filePath: string,\n key: Buffer | null,\n memoryDir?: string,\n opts?: { aadFilePath?: string }\n): Promise<Buffer> {\n const buf = memoryDir ? await readStoreFileBuffer(filePath, memoryDir) : await readFile(filePath);\n if (!isEncryptedFile(buf)) {\n // Plain file — legacy or unencrypted store.\n return buf;\n }\n // Encrypted — key required.\n if (key === null) {\n throw new SecureStoreLockedError(\n `secure-store is locked — cannot read encrypted file at ${filePath}. Run \\`remnic secure-store unlock\\` to decrypt.`\n );\n }\n return decryptFileBodyForPath(buf, key, filePath, memoryDir, opts?.aadFilePath);\n}\n\nexport async function readMaybeEncryptedFile(\n filePath: string,\n key: Buffer | null,\n memoryDir?: string\n): Promise<string> {\n return (await readMaybeEncryptedFileBuffer(filePath, key, memoryDir)).toString(\"utf8\");\n}\n\nexport interface WriteMaybeEncryptedFileOptions {\n /**\n * File mode bits. Default 0o600 (owner read/write only).\n * Applied only on create; existing files inherit their existing mode.\n */\n mode?: number;\n /**\n * If true, write atomically via a temp file + rename (CLAUDE.md gotcha #54).\n * Default true.\n */\n atomic?: boolean;\n /**\n * ABSOLUTE final path whose canonical AAD (via {@link filePathAad})\n * replaces the physical `filePath`'s. Used by the offline-sync generation\n * transaction: staged ciphertext physically lives under a staging\n * directory but must decrypt at its FINAL published path after the\n * staging directory is swapped into place by rename. Deriving the AAD\n * from the absolute final path through the SAME filePathAad logic as\n * ordinary reads keeps write and read derivations identical on every\n * platform (POSIX and Windows separators alike). (issue #3148, round 4/5)\n */\n aadFilePath?: string;\n}\n\n/**\n * Write `content` to `filePath`.\n *\n * - If `key` is provided and non-null, encrypt the content first.\n * - If `key` is null, write the content as plain UTF-8 (unencrypted store).\n *\n * Writes atomically: content is written to a unique temp file\n * first, then renamed into place (CLAUDE.md gotcha #54 — never delete\n * before write).\n */\nexport async function writeMaybeEncryptedFile(\n filePath: string,\n content: string | Buffer,\n key: Buffer | null,\n options: WriteMaybeEncryptedFileOptions = {},\n memoryDir?: string\n): Promise<void> {\n const { mode = 0o600, atomic = true, aadFilePath } = options;\n await mkdir(path.dirname(filePath), { recursive: true });\n\n let data: Buffer | string;\n if (key !== null) {\n const aad = aadFilePath !== undefined ? filePathAad(aadFilePath, memoryDir) : filePathAad(filePath, memoryDir);\n data = encryptFileBody(content, key, aad);\n } else {\n data = content;\n }\n\n if (atomic) {\n const tempPath = uniqueAtomicTempPath(filePath, \"tmp\");\n try {\n await writeFile(tempPath, data, { mode });\n await rename(tempPath, filePath);\n } catch (err) {\n // Best-effort cleanup of the temp file.\n try {\n await unlink(tempPath);\n } catch {\n // ignore\n }\n throw err;\n }\n } else {\n await writeFile(filePath, data, { mode });\n }\n}\n\nexport async function writeMaybeEncryptedFileFromChunks(\n filePath: string,\n chunks: AsyncIterable<Buffer>,\n key: Buffer | null,\n options: WriteMaybeEncryptedFileOptions = {},\n memoryDir?: string\n): Promise<void> {\n const { mode = 0o600, atomic = true } = options;\n await mkdir(path.dirname(filePath), { recursive: true });\n const writePath = atomic ? `${filePath}.tmp-${process.pid}-${Date.now()}` : filePath;\n let completed = false;\n try {\n const handle = await openFile(writePath, \"w\", mode);\n try {\n if (key !== null) {\n const salt = generateSalt();\n const iv = randomBytes(IV_LENGTH);\n const header = Buffer.alloc(MAGIC_HEADER_SIZE + ENVELOPE_HEADER_SIZE);\n MAGIC_BYTES.copy(header, 0);\n header.writeUInt8(FILE_FORMAT_VERSION, MAGIC_BYTES.length);\n header.writeUInt8(FILE_FORMAT_FLAGS, MAGIC_BYTES.length + 1);\n const envelopeOffset = MAGIC_HEADER_SIZE;\n header.writeUInt8(ENVELOPE_VERSION, envelopeOffset + ENVELOPE_LAYOUT.version);\n salt.copy(header, envelopeOffset + ENVELOPE_LAYOUT.salt);\n iv.copy(header, envelopeOffset + ENVELOPE_LAYOUT.iv);\n await handle.write(header);\n\n const cipher = createCipheriv(\"aes-256-gcm\", key, iv, { authTagLength: AUTH_TAG_LENGTH });\n const aad = filePathAad(filePath, memoryDir);\n cipher.setAAD(Buffer.concat([buildHeaderAad(salt), aad]));\n for await (const chunk of chunks) {\n if (chunk.length === 0) continue;\n const encrypted = cipher.update(chunk);\n if (encrypted.length > 0) await handle.write(encrypted);\n }\n const final = cipher.final();\n if (final.length > 0) await handle.write(final);\n const authTag = cipher.getAuthTag();\n await handle.write(authTag, 0, authTag.length, MAGIC_HEADER_SIZE + ENVELOPE_LAYOUT.authTag);\n } else {\n for await (const chunk of chunks) {\n if (chunk.length > 0) await handle.write(chunk);\n }\n }\n } finally {\n await handle.close();\n }\n if (atomic) {\n await rename(writePath, filePath);\n }\n completed = true;\n } finally {\n if (!completed && atomic) {\n await unlink(writePath).catch(() => {});\n }\n }\n}\n\n/**\n * Stream a file through the secure-store reader without buffering the whole\n * payload. Plaintext files are passed through unchanged; encrypted files are\n * authenticated and decrypted incrementally.\n */\nexport async function* readMaybeEncryptedFileFromChunks(\n filePath: string,\n key: Buffer | null,\n memoryDir?: string,\n chunkSize = 64 * 1024\n): AsyncIterable<Buffer> {\n const handle = await openFile(filePath, \"r\");\n const headerSize = MAGIC_HEADER_SIZE + ENVELOPE_HEADER_SIZE;\n try {\n const prefix = Buffer.alloc(headerSize);\n const prefixBytes = await readPositionedPrefix(handle, prefix);\n const prefixView = prefix.subarray(0, prefixBytes);\n if (!isEncryptedFile(prefixView)) {\n if (prefixBytes > 0) yield Buffer.from(prefixView);\n let position = prefixBytes;\n const buffer = Buffer.alloc(chunkSize);\n for (;;) {\n const { bytesRead } = await handle.read(buffer, 0, buffer.length, position);\n if (bytesRead === 0) break;\n position += bytesRead;\n yield Buffer.from(buffer.subarray(0, bytesRead));\n }\n return;\n }\n if (prefixBytes < headerSize) {\n throw new Error(`encrypted file header is truncated: ${filePath}`);\n }\n if (key === null) {\n throw new SecureStoreLockedError(\n `secure-store is locked — cannot read encrypted file at ${filePath}. ` +\n \"Run `remnic secure-store unlock` to decrypt.\"\n );\n }\n const envelope = prefix.subarray(MAGIC_HEADER_SIZE);\n const version = envelope.readUInt8(ENVELOPE_LAYOUT.version);\n if (version !== ENVELOPE_VERSION) {\n throw new Error(`secure-store: unsupported envelope version ${version}`);\n }\n const salt = envelope.subarray(ENVELOPE_LAYOUT.salt, ENVELOPE_LAYOUT.salt + ENVELOPE_SALT_LENGTH);\n const iv = envelope.subarray(ENVELOPE_LAYOUT.iv, ENVELOPE_LAYOUT.iv + IV_LENGTH);\n const authTag = envelope.subarray(ENVELOPE_LAYOUT.authTag, ENVELOPE_LAYOUT.authTag + AUTH_TAG_LENGTH);\n const decipher = createDecipheriv(\"aes-256-gcm\", key, iv, { authTagLength: AUTH_TAG_LENGTH });\n decipher.setAAD(Buffer.concat([buildHeaderAad(salt), filePathAad(filePath, memoryDir)]));\n decipher.setAuthTag(authTag);\n let position = headerSize;\n const buffer = Buffer.alloc(chunkSize);\n try {\n for (;;) {\n const { bytesRead } = await handle.read(buffer, 0, buffer.length, position);\n if (bytesRead === 0) break;\n position += bytesRead;\n const plain = decipher.update(buffer.subarray(0, bytesRead));\n if (plain.length > 0) yield plain;\n }\n const final = decipher.final();\n if (final.length > 0) yield final;\n } catch (err) {\n if (err instanceof SecureStoreDecryptError) throw err;\n const message = err instanceof Error ? err.message : String(err);\n throw new SecureStoreDecryptError(`secure-store decryption failed: ${message}`);\n }\n } finally {\n await handle.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Migration\n// ---------------------------------------------------------------------------\n\nexport interface MigrateResult {\n /** Number of files successfully encrypted. */\n encrypted: number;\n /** Number of files already encrypted (skipped). */\n skipped: number;\n /** Files that failed to encrypt (path → error message). */\n errors: Array<{ filePath: string; error: string }>;\n}\n\nexport interface DecryptResult {\n /** Number of files successfully decrypted back to plaintext. */\n decrypted: number;\n /** Number of files already plaintext (skipped). */\n skipped: number;\n /** Files that failed to decrypt (path → error message). */\n errors: Array<{ filePath: string; error: string }>;\n}\n\n/**\n * Walk `dir` recursively, find encryptable storage-managed files that are not\n * yet encrypted, and re-write them as encrypted files under `key`.\n *\n * Safety rules per CLAUDE.md gotchas #54 and #25:\n * 1. A page-version snapshot is taken (via `createVersion`) BEFORE\n * each overwrite so the plaintext version is preserved in history.\n * Since this module has no direct access to `page-versioning.ts`\n * internals, callers who have page-versioning configured should\n * pass `onBeforeEncrypt` to take the snapshot.\n * 2. The new encrypted content is written to a temp file first,\n * then renamed atomically — never deleted before written.\n * 3. If encryption of any file fails, the error is recorded and the\n * original file is left intact (partial migration is safe).\n *\n * @param dir Absolute path to the memory directory.\n * @param key 32-byte AES-256 key.\n * @param onBeforeEncrypt Optional callback invoked before encrypting\n * each file. Can be used to take page-version\n * snapshots. Errors here are non-fatal.\n */\nexport async function migrateMemoryDirToEncrypted(\n dir: string,\n key: Buffer,\n onBeforeEncrypt?: (filePath: string) => Promise<void>\n): Promise<MigrateResult> {\n const result: MigrateResult = { encrypted: 0, skipped: 0, errors: [] };\n\n const files = await collectEncryptableStorageFiles(dir);\n for (const filePath of files) {\n try {\n const buf = await readFile(filePath);\n if (isEncryptedFile(buf)) {\n result.skipped++;\n continue;\n }\n // Call optional pre-encryption hook (e.g. page-version snapshot).\n if (onBeforeEncrypt) {\n try {\n await onBeforeEncrypt(filePath);\n } catch {\n // Non-fatal — continue with encryption even if snapshot fails.\n }\n }\n const content = buf.toString(\"utf8\");\n const aad = filePathAad(filePath, dir);\n const encrypted = encryptFileBody(content, key, aad);\n\n // Atomic write: temp → rename (gotcha #54).\n const tempPath = uniqueAtomicTempPath(filePath, \"enc-tmp\");\n try {\n await writeFile(tempPath, encrypted, { mode: 0o600 });\n await rename(tempPath, filePath);\n result.encrypted++;\n } catch (writeErr) {\n // Clean up temp file, leave original intact.\n try {\n const { unlink } = await import(\"node:fs/promises\");\n await unlink(tempPath);\n } catch {\n // ignore\n }\n throw writeErr;\n }\n } catch (err) {\n result.errors.push({\n filePath,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n return result;\n}\n\n/**\n * Walk `dir` recursively, find storage-managed encrypted files, and\n * re-write them as plaintext under the same paths.\n *\n * This is the reversible counterpart to {@link migrateMemoryDirToEncrypted}.\n * It only touches files under the same storage-managed roots, skips\n * plaintext files, skips symlinks, excludes `.secure-store/`, and writes\n * each plaintext replacement via temp-file + rename so a per-file failure\n * leaves the ciphertext intact.\n */\nexport async function decryptMemoryDirToPlaintext(dir: string, key: Buffer): Promise<DecryptResult> {\n const result: DecryptResult = { decrypted: 0, skipped: 0, errors: [] };\n\n const files = await collectStorageManagedFiles(dir, isDecryptableStoragePath);\n for (const filePath of files) {\n try {\n const buf = await readFile(filePath);\n if (!isEncryptedFile(buf)) {\n result.skipped++;\n continue;\n }\n\n const plaintext = decryptFileBodyForPath(buf, key, filePath, dir);\n const tempPath = uniqueAtomicTempPath(filePath, \"dec-tmp\");\n try {\n await writeFile(tempPath, plaintext, { mode: 0o600 });\n await rename(tempPath, filePath);\n result.decrypted++;\n } catch (writeErr) {\n try {\n await unlink(tempPath);\n } catch {\n // ignore cleanup errors; original ciphertext is still intact.\n }\n throw writeErr;\n }\n } catch (err) {\n result.errors.push({\n filePath,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction uniqueAtomicTempPath(filePath: string, label: string): string {\n return `${filePath}.${label}-${process.pid}-${Date.now()}-${randomUUID()}`;\n}\n\nfunction decryptFileBodyForPath(\n buf: Buffer,\n key: Buffer,\n filePath: string,\n memoryDir?: string,\n aadFilePath?: string\n): Buffer {\n // A canonical-AAD stage read (offline-sync generation transaction) binds\n // the FINAL published path, not the physical staging path.\n const aad = aadFilePath !== undefined ? filePathAad(aadFilePath, memoryDir) : filePathAad(filePath, memoryDir);\n try {\n return decryptFileBody(buf, key, aad);\n } catch (err) {\n if (!(err instanceof SecureStoreDecryptError)) {\n throw err;\n }\n const legacyRoot = legacyNamespaceAadRootForFile(filePath, memoryDir);\n if (legacyRoot) {\n try {\n return decryptFileBody(buf, key, filePathAad(filePath, legacyRoot));\n } catch {\n // Fall through to the namespace-reader fallback below.\n }\n }\n\n const topLevelRoot = topLevelAadRootForNamespaceReader(filePath, memoryDir);\n if (topLevelRoot) {\n try {\n return decryptFileBody(buf, key, filePathAad(filePath, topLevelRoot));\n } catch {\n // Preserve the caller-facing error from the canonical decrypt attempt.\n }\n }\n\n throw err;\n }\n}\n\nfunction topLevelAadRootForNamespaceReader(filePath: string, memoryDir?: string): string | null {\n if (!memoryDir || !path.isAbsolute(filePath)) return null;\n const resolvedMemoryDir = path.resolve(memoryDir);\n const rel = path.relative(resolvedMemoryDir, filePath);\n if (rel === \"\" || rel.startsWith(\"..\") || path.isAbsolute(rel)) return null;\n const parts = resolvedMemoryDir.split(path.sep);\n if (parts.length < 3 || parts.at(-2) !== \"namespaces\" || !parts.at(-1)) return null;\n const topLevelRoot = parts.slice(0, -2).join(path.sep) || path.sep;\n const topRel = path.relative(topLevelRoot, filePath);\n if (topRel === \"\" || topRel.startsWith(\"..\") || path.isAbsolute(topRel)) {\n return null;\n }\n const topParts = topRel.split(path.sep);\n if (topParts[0] !== \"namespaces\" || topParts[1] !== parts.at(-1)) {\n return null;\n }\n return topLevelRoot;\n}\n\nfunction legacyNamespaceAadRootForFile(filePath: string, memoryDir?: string): string | null {\n if (!memoryDir || !path.isAbsolute(filePath)) return null;\n const rel = path.relative(memoryDir, filePath);\n if (rel === \"\" || rel.startsWith(\"..\") || path.isAbsolute(rel)) return null;\n const parts = rel.split(path.sep);\n if (parts[0] === \"namespaces\" && parts.length >= 3 && parts[1]) {\n return path.join(memoryDir, \"namespaces\", parts[1]);\n }\n return null;\n}\n\n/**\n * Recursively collect files under `dir` that are read through the\n * storage-layer secure-store helpers, excluding symlinked entries and\n * `.secure-store/` metadata.\n */\nasync function collectEncryptableStorageFiles(dir: string, rootDir = dir): Promise<string[]> {\n return collectStorageManagedFiles(dir, isEncryptableStoragePath, rootDir);\n}\n\n/**\n * Recursively collect regular files under storage-managed roots, excluding\n * symlinked entries and `.secure-store/` metadata. This broader collector is\n * used by the decrypt/disable path so future encrypted sidecars can be\n * restored without requiring extension-specific logic.\n */\nasync function collectStorageManagedFiles(\n dir: string,\n includeFile: (filePath: string, rootDir: string) => boolean,\n rootDir = dir\n): Promise<string[]> {\n const results: string[] = [];\n let scanDir = dir;\n let scanRootDir = rootDir;\n if (path.resolve(dir) === path.resolve(rootDir)) {\n const normalizedRoot = await resolveStorageManagedRootForScan(dir);\n if (!normalizedRoot) return results;\n scanDir = normalizedRoot;\n scanRootDir = normalizedRoot;\n }\n let names: string[];\n try {\n names = await readdir(scanDir, { encoding: \"utf8\" });\n } catch {\n return results;\n }\n for (const name of names) {\n if (name.startsWith(\".secure-store\")) continue;\n const full = path.join(scanDir, name);\n let isDir = false;\n let isFile = false;\n try {\n const s = await lstat(full);\n if (s.isSymbolicLink()) continue;\n isDir = s.isDirectory();\n isFile = s.isFile();\n } catch {\n continue;\n }\n if (isDir) {\n const sub = await collectStorageManagedFiles(full, includeFile, scanRootDir);\n results.push(...sub);\n } else if (isFile && includeFile(full, scanRootDir)) {\n results.push(full);\n }\n }\n return results;\n}\n\nasync function resolveStorageManagedRootForScan(dir: string): Promise<string | null> {\n const lstatPath = normalizePathForLstat(dir);\n let stat: Awaited<ReturnType<typeof lstat>>;\n try {\n stat = await lstat(lstatPath);\n } catch (error) {\n if (isFsErrorWithCode(error, \"ENOENT\")) return null;\n throw error;\n }\n if (stat.isSymbolicLink()) {\n throw new Error(`secure-store migration root must not be a symlink: ${dir}`);\n }\n if (!stat.isDirectory()) {\n throw new Error(`secure-store migration root must be a directory: ${dir}`);\n }\n return lstatPath;\n}\n\nfunction normalizePathForLstat(filePath: string): string {\n return stripTrailingPathSeparators(path.normalize(filePath));\n}\n\nfunction isFsErrorWithCode(error: unknown, code: string): boolean {\n return typeof error === \"object\" && error !== null && (error as { code?: unknown }).code === code;\n}\n\nfunction stripTrailingPathSeparators(filePath: string): string {\n const root = path.parse(filePath).root;\n let end = filePath.length;\n while (\n end > root.length &&\n (filePath[end - 1] === path.sep || filePath[end - 1] === path.posix.sep || filePath[end - 1] === path.win32.sep)\n ) {\n end -= 1;\n }\n return end === filePath.length ? filePath : filePath.slice(0, end);\n}\n\nfunction isEncryptableStoragePath(filePath: string, rootDir: string): boolean {\n const rel = path.relative(rootDir, filePath);\n if (rel === \"\" || rel.startsWith(\"..\") || path.isAbsolute(rel)) return false;\n const normalized = normalizeStorageRelativePath(rel);\n if (normalized === \"profile.md\") return true;\n if (isEncryptableStateSidecar(normalized)) return true;\n if (isEncryptableBufferSessionFile(normalized)) return true;\n if (isEncryptableSummarySidecar(normalized)) return true;\n const firstSegment = normalized.split(\"/\", 1)[0];\n return ENCRYPTABLE_MARKDOWN_STORAGE_ROOTS.has(firstSegment) && normalized.endsWith(\".md\");\n}\n\nfunction isDecryptableStoragePath(filePath: string, rootDir: string): boolean {\n if (isEncryptableStoragePath(filePath, rootDir)) return true;\n const rel = path.relative(rootDir, filePath);\n if (rel === \"\" || rel.startsWith(\"..\") || path.isAbsolute(rel)) return false;\n const normalized = normalizeStorageRelativePath(rel);\n const firstSegment = normalized.split(\"/\", 1)[0];\n return DECRYPTABLE_SIDECAR_ROOTS.has(firstSegment);\n}\n\nfunction normalizeStorageRelativePath(rel: string): string {\n const normalized = rel.split(path.sep).join(\"/\");\n const parts = normalized.split(\"/\");\n if (parts[0] === \"namespaces\" && parts.length >= 3) {\n return parts.slice(2).join(\"/\");\n }\n return normalized;\n}\n\n// Every recall MEMORY category directory (facts/ + the CATEGORY_DIR_MAP memory\n// dirs) must be encryptable at rest — #1546 routes decision/preference/moment/...\n// memories into their own dirs, so hardcoding only facts/corrections/procedures/\n// reasoning-traces would silently write those categories in plaintext on\n// encrypted stores. RECALL_FALLBACK_DIRS is the single source of truth for that\n// set; the extra non-category markdown roots (artifacts/archive/entities/\n// identity) are appended.\n//\n// questions/ is deliberately EXCLUDED (RECALL_FALLBACK_DIRS omits the non-memory\n// queue dirs): the question queue is written/resolved through plain readFile/\n// writeFile (StorageManager.writeQuestion/resolveQuestion), NOT the secure-file\n// helpers. Encrypting questions/ here would make migration seal those files,\n// after which resolveQuestion() would read ciphertext as UTF-8, fail to update\n// the frontmatter, and overwrite/corrupt the file while returning success\n// (codex #1563 review). Keeping questions/ plaintext preserves the queue's\n// existing behavior; #1546 does not change question encryption.\nconst ENCRYPTABLE_MARKDOWN_STORAGE_ROOTS = new Set<string>([\n ...RECALL_FALLBACK_DIRS,\n \"artifacts\",\n \"archive\",\n \"entities\",\n \"identity\",\n]);\n\nconst ENCRYPTABLE_STATE_SIDECARS = new Set([\n \"state/behavior-signals.jsonl\",\n \"state/buffer-surprise-ledger.jsonl\",\n \"state/buffer.json\",\n \"state/compression-guideline-draft-state.json\",\n \"state/compression-guideline-state.json\",\n \"state/compression-guidelines.draft.md\",\n \"state/compression-guidelines.md\",\n \"state/entity-synthesis-queue.json\",\n \"state/fact-hashes.txt\",\n \"state/memory-actions.jsonl\",\n \"state/memory-lifecycle-ledger.jsonl\",\n \"state/meta.json\",\n \"state/reextract-jobs.jsonl\",\n \"state/topics.json\",\n]);\n\nfunction isEncryptableStateSidecar(normalized: string): boolean {\n // state/dropped/ holds copies of extraction-buffer turns dropped after\n // repeated extraction failures — same content sensitivity as buffer.json.\n return ENCRYPTABLE_STATE_SIDECARS.has(normalized) || normalized.startsWith(\"state/dropped/\");\n}\n\n/**\n * Per-session buffer state files (`state/buffer/<session>.json`) hold the same\n * buffered turns the legacy `state/buffer.json` held, so they are encryptable\n * state sidecars too — otherwise enabling the secure store would leave the\n * daemon's buffered turns in plaintext on disk.\n */\nfunction isEncryptableBufferSessionFile(normalized: string): boolean {\n return normalized.startsWith(\"state/buffer/\") && normalized.endsWith(\".json\");\n}\n\nfunction isEncryptableSummarySidecar(normalized: string): boolean {\n return normalized.startsWith(\"summaries/\") && normalized.endsWith(\".json\");\n}\n\nconst DECRYPTABLE_SIDECAR_ROOTS = new Set([\"state\", \"indexes\", \"index\", \"provenance\"]);\n","import { constants } from \"node:fs\";\nimport { lstat, open, readFile, realpath } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { assertPathInsideRoot } from \"../utils/path-containment.js\";\n\nexport async function readStoreFileBuffer(filePath: string, memoryDir: string): Promise<Buffer> {\n const root = path.resolve(memoryDir);\n const entities = path.join(root, \"entities\");\n const target = path.resolve(filePath);\n const relative = path.relative(entities, target);\n if (relative === \"..\" || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {\n return readFile(filePath);\n }\n const handle = await open(target, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0) | (constants.O_NONBLOCK ?? 0));\n try {\n for (let parent = path.dirname(target); ; parent = path.dirname(parent)) {\n const info = await lstat(parent);\n if (info.isSymbolicLink() || !info.isDirectory()) {\n throw new Error(`Refusing unsafe entity directory: ${parent}.`);\n }\n if (parent === root) break;\n }\n const [opened, entry, realRoot, realEntities, realTarget] = await Promise.all([\n handle.stat(), lstat(target), realpath(root), realpath(entities), realpath(target),\n ]);\n if (!opened.isFile() || entry.isSymbolicLink() || !entry.isFile()\n || opened.dev !== entry.dev || opened.ino !== entry.ino\n || realEntities !== path.join(realRoot, \"entities\")) {\n throw new Error(`Refusing unsafe entity file: ${filePath}.`);\n }\n assertPathInsideRoot(realEntities, realTarget, filePath);\n return await handle.readFile();\n } finally {\n await handle.close();\n }\n}\n\n\n/**\n * Atomic-replacement-tolerant variant: entity writes ship as temp-file +\n * rename, so an open/lstat identity mismatch during a concurrent write is an\n * ordinary race, not tampering. Retry a bounded number of times with fresh\n * containment checks before surfacing the mismatch as unsafe.\n */\nexport async function readStoreFileBufferTolerant(filePath: string, memoryDir: string, attempts = 3): Promise<Buffer> {\n for (let attempt = 0; ; attempt += 1) {\n try {\n return await readStoreFileBuffer(filePath, memoryDir);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const identityRace = /unsafe entity (?:file|directory)/.test(message);\n if (!identityRace || attempt >= attempts - 1) throw err;\n }\n }\n}\n"],"mappings":"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","names":["lstat","readFile","path","open","open","lstat","path","readFile","position","buffer","unlink"]}
@@ -7,22 +7,22 @@ import {
7
7
  import {
8
8
  openclawReplayNormalizer
9
9
  } from "./chunk-H4CKS5B2.js";
10
+ import {
11
+ importMarkdownBundle
12
+ } from "./chunk-HDDRVXX4.js";
13
+ import {
14
+ importSqlite
15
+ } from "./chunk-PYHOAXTI.js";
10
16
  import {
11
17
  exportJsonBundle
12
18
  } from "./chunk-UP6MOYCB.js";
13
19
  import {
14
20
  exportOkfBundle,
15
21
  parseIncludeStatus
16
- } from "./chunk-6F5BGIAF.js";
22
+ } from "./chunk-GPVGAHXL.js";
17
23
  import {
18
24
  exportSqlite
19
25
  } from "./chunk-BV2TIZ4F.js";
20
- import {
21
- importMarkdownBundle
22
- } from "./chunk-HDDRVXX4.js";
23
- import {
24
- importSqlite
25
- } from "./chunk-PYHOAXTI.js";
26
26
  import {
27
27
  detectImportFormat
28
28
  } from "./chunk-OZKVVUJB.js";
@@ -31,7 +31,7 @@ import {
31
31
  } from "./chunk-2DL3OFLD.js";
32
32
  import {
33
33
  backupMemoryDir
34
- } from "./chunk-2NLLXCJG.js";
34
+ } from "./chunk-OWHERGF2.js";
35
35
  import {
36
36
  exportMarkdownBundle
37
37
  } from "./chunk-765K3SAT.js";
@@ -53,6 +53,9 @@ import {
53
53
  import {
54
54
  WebDavServer
55
55
  } from "./chunk-PYIFUBRK.js";
56
+ import {
57
+ rebuildMemoryLifecycleLedger
58
+ } from "./chunk-LJXBFVI2.js";
56
59
  import {
57
60
  rebuildObservations
58
61
  } from "./chunk-WHCD4Z3D.js";
@@ -62,21 +65,18 @@ import {
62
65
  import {
63
66
  migrateObservations
64
67
  } from "./chunk-EDR6CVWZ.js";
65
- import {
66
- rebuildMemoryLifecycleLedger
67
- } from "./chunk-5DCS4C4S.js";
68
- import {
69
- lintOkfDir
70
- } from "./chunk-L2JQSG5M.js";
71
- import {
72
- runCompatChecks
73
- } from "./chunk-2524MOWK.js";
74
68
  import {
75
69
  getBulkImportSource,
76
70
  listBulkImportSources,
77
71
  registerBulkImportSource,
78
72
  runBulkImportPipeline
79
73
  } from "./chunk-HSSERO3E.js";
74
+ import {
75
+ lintOkfDir
76
+ } from "./chunk-I7TR2OCU.js";
77
+ import {
78
+ runCompatChecks
79
+ } from "./chunk-2524MOWK.js";
80
80
  import {
81
81
  getUtilityLearningStatus,
82
82
  learnUtilityPromotionWeights
@@ -87,21 +87,21 @@ import {
87
87
  } from "./chunk-EOU6V4IN.js";
88
88
  import {
89
89
  searchVerifiedEpisodes
90
- } from "./chunk-QOUKZ2N3.js";
90
+ } from "./chunk-JOOGPZJE.js";
91
91
  import {
92
92
  ThreadingManager
93
93
  } from "./chunk-R7WYAXPO.js";
94
94
  import {
95
95
  promoteSemanticRuleFromMemory
96
- } from "./chunk-OLHN4ZNT.js";
96
+ } from "./chunk-4LLV2N3R.js";
97
97
  import {
98
98
  searchVerifiedSemanticRules
99
- } from "./chunk-I75BWXRZ.js";
99
+ } from "./chunk-U66CTIF5.js";
100
100
  import {
101
101
  buildResumeBundleFromState,
102
102
  getResumeBundleStatus,
103
103
  recordResumeBundle
104
- } from "./chunk-YYGZ4EIY.js";
104
+ } from "./chunk-RUAIO5UA.js";
105
105
  import {
106
106
  getWorkProductLedgerStatus,
107
107
  recordWorkProductLedgerEntry,
@@ -112,16 +112,7 @@ import {
112
112
  } from "./chunk-HDN7IVIO.js";
113
113
  import {
114
114
  parseXrayCliOptions
115
- } from "./chunk-N7DVBHU3.js";
116
- import {
117
- collectPatternMemories,
118
- explainPatternMemory,
119
- parsePatternsExplainOptions,
120
- parsePatternsListOptions,
121
- parseStrictCliDate,
122
- renderPatternExplain,
123
- renderPatternsList
124
- } from "./chunk-3T74IZB3.js";
115
+ } from "./chunk-LR7RW4T6.js";
125
116
  import {
126
117
  readRuntimePolicySnapshot,
127
118
  sanitizeRuntimePolicyValues
@@ -134,12 +125,21 @@ import {
134
125
  runOperatorInventory,
135
126
  runOperatorRepair,
136
127
  runOperatorSetup
137
- } from "./chunk-CVMAOPAH.js";
128
+ } from "./chunk-ODJ2HRFW.js";
138
129
  import {
139
130
  analyzeSessionIntegrity,
140
131
  applySessionRepair,
141
132
  planSessionRepair
142
133
  } from "./chunk-KFY3SGN7.js";
134
+ import {
135
+ collectPatternMemories,
136
+ explainPatternMemory,
137
+ parsePatternsExplainOptions,
138
+ parsePatternsListOptions,
139
+ parseStrictCliDate,
140
+ renderPatternExplain,
141
+ renderPatternsList
142
+ } from "./chunk-3T74IZB3.js";
143
143
  import {
144
144
  GITHUB_CONNECTOR_ID,
145
145
  GMAIL_CONNECTOR_ID,
@@ -176,7 +176,7 @@ import {
176
176
  rebuildMemoryProjection,
177
177
  repairMemoryProjection,
178
178
  verifyMemoryProjection
179
- } from "./chunk-X247CYL6.js";
179
+ } from "./chunk-RIAKNXZB.js";
180
180
  import {
181
181
  buildConnectorRowsFromDefinitions,
182
182
  parseConnectorsListOptions,
@@ -197,7 +197,7 @@ import {
197
197
  } from "./chunk-6TBIWIAG.js";
198
198
  import {
199
199
  parseConfig
200
- } from "./chunk-3PLAL2KP.js";
200
+ } from "./chunk-FJ264QBP.js";
201
201
  import {
202
202
  applyManagedRegion,
203
203
  expandVaultTemplateTokens,
@@ -210,7 +210,7 @@ import {
210
210
  import {
211
211
  EngramAccessHttpServer,
212
212
  MeetingsInputError
213
- } from "./chunk-FQFMLOAC.js";
213
+ } from "./chunk-RWW5KPIY.js";
214
214
  import {
215
215
  WearablesInputError
216
216
  } from "./chunk-V7IZKJI3.js";
@@ -220,10 +220,10 @@ import {
220
220
  loadChatSession,
221
221
  processChatMessage,
222
222
  sessionBelongsToPrincipal
223
- } from "./chunk-MCPSMWUC.js";
223
+ } from "./chunk-CR72WZ3M.js";
224
224
  import {
225
225
  EngramAccessService
226
- } from "./chunk-TAEJ2MRR.js";
226
+ } from "./chunk-7IANFJ24.js";
227
227
  import {
228
228
  WorkStorage
229
229
  } from "./chunk-MPC7TYTZ.js";
@@ -240,39 +240,39 @@ import {
240
240
  parseRecallExplainFormat,
241
241
  renderRecallExplain,
242
242
  renderXray
243
- } from "./chunk-FKTMWDN2.js";
243
+ } from "./chunk-WUL6LDJ4.js";
244
244
  import {
245
245
  readAggregateExtractionWatermark
246
- } from "./chunk-OLH4XCLU.js";
246
+ } from "./chunk-O2O4FDAH.js";
247
247
  import {
248
248
  getObjectiveStateStoreStatus
249
249
  } from "./chunk-4RNBLLZR.js";
250
250
  import {
251
251
  journalPath
252
- } from "./chunk-6JEEY42N.js";
252
+ } from "./chunk-AER3HD3B.js";
253
253
  import {
254
254
  searchHarmonicRetrieval
255
- } from "./chunk-JF3MEIHO.js";
255
+ } from "./chunk-JQCP5QW3.js";
256
+ import {
257
+ getCueAnchorStoreStatus
258
+ } from "./chunk-LXATZLSC.js";
256
259
  import {
257
260
  listMemoryGovernanceRuns,
258
261
  readMemoryGovernanceRunArtifact,
259
262
  restoreMemoryGovernanceRun,
260
263
  runMemoryGovernance
261
- } from "./chunk-TYLNWE4D.js";
264
+ } from "./chunk-B7VKRCAG.js";
262
265
  import {
263
266
  listNamespaces,
264
267
  runNamespaceMigration,
265
268
  verifyNamespaces
266
- } from "./chunk-WCIDCJOQ.js";
267
- import {
268
- getCueAnchorStoreStatus
269
- } from "./chunk-LXATZLSC.js";
269
+ } from "./chunk-RO2NNWR5.js";
270
270
  import {
271
271
  resolveNamespaceChildRoot
272
272
  } from "./chunk-3UXOZBHV.js";
273
273
  import {
274
274
  analyzeGraphHealth
275
- } from "./chunk-QKXPOLQD.js";
275
+ } from "./chunk-WUBGMV3G.js";
276
276
  import {
277
277
  hostSecretRefResolver,
278
278
  loadHostSecretRefResolver,
@@ -300,10 +300,10 @@ import {
300
300
  } from "./chunk-QDW3E4RD.js";
301
301
  import {
302
302
  getCausalTrajectoryStoreStatus
303
- } from "./chunk-4P4VLPGB.js";
303
+ } from "./chunk-VECRSYQG.js";
304
304
  import {
305
305
  meetingIdDate
306
- } from "./chunk-WCBLPKG3.js";
306
+ } from "./chunk-5KZNWERU.js";
307
307
  import {
308
308
  isValidTranscriptDate
309
309
  } from "./chunk-UHVXE24Y.js";
@@ -327,14 +327,14 @@ import {
327
327
  import {
328
328
  RECALL_DISCLOSURE_LEVELS,
329
329
  isRecallDisclosure
330
- } from "./chunk-UFM3PJDT.js";
330
+ } from "./chunk-PARM6OYL.js";
331
331
  import {
332
332
  activityDateInTimezone,
333
333
  isValidActivityDate
334
334
  } from "./chunk-353C5UNV.js";
335
335
  import {
336
336
  composeMemoryEnvelope
337
- } from "./chunk-TDCR6PQL.js";
337
+ } from "./chunk-TWRPWJJT.js";
338
338
  import {
339
339
  buildActionConfidenceInputFromOptions,
340
340
  evaluateActionConfidence,
@@ -342,7 +342,7 @@ import {
342
342
  } from "./chunk-Z2YFLXHU.js";
343
343
  import {
344
344
  STATE_FILE_MAX_DECRYPT_BYTES
345
- } from "./chunk-UJDTXWMT.js";
345
+ } from "./chunk-QIMGULGN.js";
346
346
  import {
347
347
  inferMemoryStatus,
348
348
  isArchivedMemoryPath,
@@ -390,7 +390,7 @@ import {
390
390
  import {
391
391
  SECURE_STORE_ENVELOPE_OVERHEAD_BYTES,
392
392
  probeEncryptedRegularFileHeader
393
- } from "./chunk-TYGLV6ZG.js";
393
+ } from "./chunk-EYGOM6AI.js";
394
394
  import {
395
395
  assertPathInsideRoot,
396
396
  pathIsInside
@@ -6702,7 +6702,7 @@ function registerCli(api, orchestrator, registerOptions = {}) {
6702
6702
  }
6703
6703
  }
6704
6704
  if (sourceArchive.endsWith(".enc")) {
6705
- const { isEncryptedCapsuleFile } = await import("./capsule-crypto-7FJQINUR.js");
6705
+ const { isEncryptedCapsuleFile } = await import("./capsule-crypto-GWVG7LGC.js");
6706
6706
  const encDetected = await isEncryptedCapsuleFile(sourceArchive).catch(() => true);
6707
6707
  if (encDetected) {
6708
6708
  throw new Error(
@@ -6865,7 +6865,7 @@ function registerCli(api, orchestrator, registerOptions = {}) {
6865
6865
  const { parseExportBundle } = await import("./transfer/types.js");
6866
6866
  let decryptedBuf;
6867
6867
  try {
6868
- const { decryptCapsuleFileInMemory } = await import("./capsule-crypto-7FJQINUR.js");
6868
+ const { decryptCapsuleFileInMemory } = await import("./capsule-crypto-GWVG7LGC.js");
6869
6869
  decryptedBuf = await decryptCapsuleFileInMemory(archivePath, memoryDir);
6870
6870
  } catch (decErr) {
6871
6871
  const msg = decErr instanceof Error ? decErr.message : String(decErr);
@@ -9355,7 +9355,7 @@ Semantic consolidation complete. clusters=${result.clustersFound}, consolidated=
9355
9355
  console.error("--verb is required. Must be one of: keep-a, keep-b, merge, both-valid, needs-more-context");
9356
9356
  process.exit(1);
9357
9357
  }
9358
- const { isValidResolutionVerb, executeResolution } = await import("./resolution-4ZEQPXUZ.js");
9358
+ const { isValidResolutionVerb, executeResolution } = await import("./resolution-NNYG37YB.js");
9359
9359
  if (!isValidResolutionVerb(verb)) {
9360
9360
  console.error(`Invalid verb: ${verb}. Must be one of: keep-a, keep-b, merge, both-valid, needs-more-context`);
9361
9361
  process.exit(1);
@@ -10140,4 +10140,4 @@ export {
10140
10140
  listMemoryMarkdownFilePaths,
10141
10141
  registerCli
10142
10142
  };
10143
- //# sourceMappingURL=chunk-SWZOAQUM.js.map
10143
+ //# sourceMappingURL=chunk-F2IU6A2L.js.map
@@ -11,7 +11,7 @@ import {
11
11
  } from "./chunk-5MA7ZLPD.js";
12
12
  import {
13
13
  LocalLlmClient
14
- } from "./chunk-74AY3S7U.js";
14
+ } from "./chunk-JNLYHJG7.js";
15
15
  import {
16
16
  completeBackgroundGeneration,
17
17
  parseBackgroundGenerationJson
@@ -798,4 +798,4 @@ export {
798
798
  summarizeHourlyStatus,
799
799
  HourlySummarizer
800
800
  };
801
- //# sourceMappingURL=chunk-2AKIVSHP.js.map
801
+ //# sourceMappingURL=chunk-FAKRTS3Y.js.map
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-4Q26RNSR.js";
4
4
  import {
5
5
  isRecallDisclosure
6
- } from "./chunk-UFM3PJDT.js";
6
+ } from "./chunk-PARM6OYL.js";
7
7
  import {
8
8
  EngramAccessInputError
9
9
  } from "./chunk-3MUKQQ7A.js";
@@ -165,4 +165,4 @@ export {
165
165
  handleRecallWhyHttpQuery,
166
166
  handleRecallXrayHttpQuery
167
167
  };
168
- //# sourceMappingURL=chunk-MZWHIAOL.js.map
168
+ //# sourceMappingURL=chunk-FC3ZFEXF.js.map