@remnic/core 9.69.86 → 9.69.88

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 (513) hide show
  1. package/dist/access-admin-ops-surface.js +74 -67
  2. package/dist/access-authorization-probe.js +75 -68
  3. package/dist/access-boundary.js +74 -67
  4. package/dist/access-citation-namespace.js +2 -2
  5. package/dist/access-cli.js +158 -151
  6. package/dist/access-cli.js.map +1 -1
  7. package/dist/access-extraction-force-flush.d.ts.map +1 -1
  8. package/dist/access-extraction-force-flush.js +74 -67
  9. package/dist/access-http-briefing.js +75 -68
  10. package/dist/access-http-offline-stream.d.ts.map +1 -1
  11. package/dist/access-http-offline-stream.js +1 -1
  12. package/dist/access-http-query.js +76 -69
  13. package/dist/access-http.js +100 -93
  14. package/dist/access-identity-continuity-surface.js +74 -67
  15. package/dist/access-lcm-surface.js +74 -67
  16. package/dist/access-mcp-output-schemas.d.ts.map +1 -1
  17. package/dist/access-mcp-output-schemas.js +1 -1
  18. package/dist/access-mcp.d.ts.map +1 -1
  19. package/dist/access-mcp.js +89 -82
  20. package/dist/access-namespace-preflight.js +74 -67
  21. package/dist/access-observe-write-category.js +19 -13
  22. package/dist/access-observe-write-surface.js +74 -67
  23. package/dist/access-offline-manifest.js +15 -10
  24. package/dist/access-operations-batch.js +78 -71
  25. package/dist/access-operations.js +85 -78
  26. package/dist/access-recall-concurrency.js +74 -67
  27. package/dist/access-recall-diagnostics.js +4 -4
  28. package/dist/access-recall-response.js +74 -67
  29. package/dist/access-recall-surface.js +74 -67
  30. package/dist/access-schema.js +15 -7
  31. package/dist/access-service-helpers.d.ts +1 -0
  32. package/dist/access-service-helpers.d.ts.map +1 -1
  33. package/dist/access-service-helpers.js +1 -1
  34. package/dist/access-service.d.ts.map +1 -1
  35. package/dist/access-service.js +74 -67
  36. package/dist/access-surface-catalog.js +76 -69
  37. package/dist/access-surface-catalog.js.map +1 -1
  38. package/dist/active-recall.js +34 -28
  39. package/dist/active-recall.js.map +1 -1
  40. package/dist/adapters/index.js +4 -4
  41. package/dist/adapters/registry.js +2 -2
  42. package/dist/{auto-sync-WEUDMXUW.js → auto-sync-U5N3CB5Y.js} +10 -10
  43. package/dist/background-generation.js +2 -2
  44. package/dist/briefing.js +18 -18
  45. package/dist/buffer.js +9 -8
  46. package/dist/calibration.js +2 -2
  47. package/dist/{capsule-crypto-GWVG7LGC.js → capsule-crypto-YO5QJ6L3.js} +2 -2
  48. package/dist/{capsule-merge-3MNURAQ3.js → capsule-merge-ZCXUPLNJ.js} +2 -2
  49. package/dist/causal-behavior.js +2 -2
  50. package/dist/causal-chain.js +2 -2
  51. package/dist/causal-consolidation.js +21 -21
  52. package/dist/causal-retrieval.js +2 -2
  53. package/dist/causal-trajectory-graph.js +3 -3
  54. package/dist/causal-trajectory.js +1 -1
  55. package/dist/{chunk-JE334SIA.js → chunk-22DYJLFN.js} +2 -2
  56. package/dist/{chunk-DGAWEJVI.js → chunk-2DLWLNO4.js} +3 -3
  57. package/dist/{chunk-J3IWHCMF.js → chunk-2DT7PIDZ.js} +3 -3
  58. package/dist/{chunk-NKFLHW7J.js → chunk-2NAD4YCI.js} +2 -2
  59. package/dist/{chunk-SV72F4DS.js → chunk-2WLMWP2T.js} +2 -2
  60. package/dist/{chunk-IVH7INFM.js → chunk-3O5BB45T.js} +7 -7
  61. package/dist/{chunk-RDEUG2M2.js → chunk-3SGTV7RD.js} +6 -6
  62. package/dist/{chunk-QOPB6KE3.js → chunk-3TB2XQ3C.js} +2 -2
  63. package/dist/{chunk-C7JAUNFC.js → chunk-4KN2JLQV.js} +2 -2
  64. package/dist/{chunk-TIQZNDZ5.js → chunk-4MP7XR5J.js} +2 -2
  65. package/dist/chunk-4NSLQ7EU.js +137 -0
  66. package/dist/chunk-4NSLQ7EU.js.map +1 -0
  67. package/dist/{chunk-7DE2NOON.js → chunk-4O4XCBOS.js} +3 -3
  68. package/dist/{chunk-SLEAJ67D.js → chunk-4OQET73E.js} +4 -4
  69. package/dist/{chunk-Q3THVX75.js → chunk-4RL3JOB3.js} +2 -2
  70. package/dist/{chunk-GDYU6VF4.js → chunk-5AYUHSAU.js} +2 -2
  71. package/dist/{chunk-B47OHVFT.js → chunk-5E6RYRYW.js} +7 -7
  72. package/dist/{chunk-2U3R3DDH.js → chunk-5FAGCOZD.js} +9 -9
  73. package/dist/{chunk-CRKWSSS4.js → chunk-5MNUBT7T.js} +2 -2
  74. package/dist/{chunk-CK5KOVHW.js → chunk-5R627MYY.js} +3 -2
  75. package/dist/{chunk-CK5KOVHW.js.map → chunk-5R627MYY.js.map} +1 -1
  76. package/dist/chunk-5X4MXNEU.js +64 -0
  77. package/dist/chunk-5X4MXNEU.js.map +1 -0
  78. package/dist/{chunk-52II7PB6.js → chunk-645WOU5I.js} +4 -4
  79. package/dist/{chunk-7G6WUOLB.js → chunk-66DKTOBY.js} +2 -2
  80. package/dist/{chunk-AZ2X5LNA.js → chunk-6LYAEM54.js} +2 -2
  81. package/dist/{chunk-DUJYRVUD.js → chunk-6O2VUWV6.js} +3 -3
  82. package/dist/{chunk-IMAP236M.js → chunk-6UQFJULG.js} +2 -2
  83. package/dist/chunk-75PT4N7Q.js +75 -0
  84. package/dist/chunk-75PT4N7Q.js.map +1 -0
  85. package/dist/{chunk-2I6VHX6Q.js → chunk-76M5OK7V.js} +2 -2
  86. package/dist/{chunk-OEBLZZKK.js → chunk-7XG7KMIW.js} +2 -2
  87. package/dist/{chunk-FYAU4S6E.js → chunk-A4VSOUCB.js} +3 -3
  88. package/dist/{chunk-GEX5NI36.js → chunk-AE5U5NMS.js} +2 -2
  89. package/dist/{chunk-UNJOB4DY.js → chunk-AEHVZY3X.js} +2 -2
  90. package/dist/{chunk-ML2MVAEZ.js → chunk-AEU4EQGV.js} +1 -1
  91. package/dist/{chunk-3SG4FGDV.js → chunk-AWD36R4D.js} +5 -2
  92. package/dist/chunk-AWD36R4D.js.map +1 -0
  93. package/dist/{chunk-JVY556FW.js → chunk-AWG3TIAY.js} +2 -2
  94. package/dist/{chunk-JX6USXZR.js → chunk-AXC4WSF3.js} +4 -4
  95. package/dist/{chunk-BOUFVFV2.js → chunk-AZSGSRIM.js} +2 -2
  96. package/dist/{chunk-VZBW6CDG.js → chunk-B3O3IRZN.js} +1 -1
  97. package/dist/{chunk-4V44AA7O.js → chunk-BGI4NUAO.js} +2 -2
  98. package/dist/{chunk-BNPLDVM6.js → chunk-BJVO4H5C.js} +2 -2
  99. package/dist/{chunk-S42HZILI.js → chunk-BLVR4WYU.js} +3 -3
  100. package/dist/{chunk-JXMECXMF.js → chunk-BOZJD5PK.js} +2 -2
  101. package/dist/{chunk-6DTJBTHL.js → chunk-BUQKSQXL.js} +15 -3
  102. package/dist/chunk-BUQKSQXL.js.map +1 -0
  103. package/dist/{chunk-OWHERGF2.js → chunk-BXLOS5AJ.js} +2 -2
  104. package/dist/{chunk-EVBI7S4J.js → chunk-C6U57KG4.js} +4 -4
  105. package/dist/{chunk-RB776UH3.js → chunk-C7V3KPKJ.js} +235 -93
  106. package/dist/chunk-C7V3KPKJ.js.map +1 -0
  107. package/dist/{chunk-T4BE5KJM.js → chunk-C7ZDKWPS.js} +4 -4
  108. package/dist/{chunk-HKFJ3PF3.js → chunk-CRXRZA3G.js} +2 -2
  109. package/dist/{chunk-NMBX4D7P.js → chunk-CZJA2EQE.js} +2 -2
  110. package/dist/{chunk-BJX3VQWZ.js → chunk-D5BSQR63.js} +2 -2
  111. package/dist/{chunk-MFZACYZ6.js → chunk-DGS34QSY.js} +2 -2
  112. package/dist/{chunk-UVYI6VIX.js → chunk-DQEMWVMT.js} +1 -1
  113. package/dist/{chunk-DQISIT63.js → chunk-DZ42MDZE.js} +3 -3
  114. package/dist/{chunk-5ELENZTA.js → chunk-EDIH6X2P.js} +2 -2
  115. package/dist/{chunk-3BTFBQLO.js → chunk-EFHNFTWB.js} +49 -3
  116. package/dist/chunk-EFHNFTWB.js.map +1 -0
  117. package/dist/{chunk-C3QXITIP.js → chunk-EQUD7FP4.js} +2 -2
  118. package/dist/chunk-ETUZFFPU.js +218 -0
  119. package/dist/chunk-ETUZFFPU.js.map +1 -0
  120. package/dist/{chunk-YNDLCWXS.js → chunk-EZ25VE3G.js} +4 -4
  121. package/dist/{chunk-IA3X2Y4K.js → chunk-FJLQXMTU.js} +3 -3
  122. package/dist/{chunk-TY446V34.js → chunk-FKD656D2.js} +2 -2
  123. package/dist/{chunk-CLXTDOKV.js → chunk-FUNBJQYR.js} +2 -2
  124. package/dist/{chunk-CLSYE5OZ.js → chunk-G2STTRYB.js} +2 -2
  125. package/dist/{chunk-SQDKX4RW.js → chunk-GWM6T5EQ.js} +2 -2
  126. package/dist/{chunk-AXDOF4VI.js → chunk-GZDTVYK2.js} +2 -2
  127. package/dist/{chunk-55FCXCXJ.js → chunk-H56VY4BY.js} +7 -7
  128. package/dist/{chunk-OM5BIPBR.js → chunk-H5BJWPUI.js} +4 -4
  129. package/dist/{chunk-7QK5TUTW.js → chunk-HLB7GKGG.js} +2 -2
  130. package/dist/{chunk-CHDGJJV6.js → chunk-HWLIMQE2.js} +2 -2
  131. package/dist/{chunk-QB75LKQQ.js → chunk-IOSZHHJC.js} +34 -15
  132. package/dist/chunk-IOSZHHJC.js.map +1 -0
  133. package/dist/{chunk-NAFPHFDM.js → chunk-JNLEJE4A.js} +2 -2
  134. package/dist/{chunk-7UUKRS3J.js → chunk-JVPWRNHA.js} +3 -3
  135. package/dist/{chunk-6ET2VII7.js → chunk-JYG2334B.js} +2 -2
  136. package/dist/chunk-K6T33R4Q.js +535 -0
  137. package/dist/chunk-K6T33R4Q.js.map +1 -0
  138. package/dist/{chunk-2QG4SLZI.js → chunk-KF7PQ7G2.js} +2 -2
  139. package/dist/{chunk-WIKO4SCR.js → chunk-LHRQBMJN.js} +2 -2
  140. package/dist/{chunk-IFCKHPY4.js → chunk-LO4UKPKX.js} +2 -2
  141. package/dist/{chunk-TEV2JGU5.js → chunk-LU7E2MXE.js} +19 -19
  142. package/dist/{chunk-RJLV4BKR.js → chunk-LV5H6FTN.js} +2 -2
  143. package/dist/{chunk-IBGEHKQ6.js → chunk-LW3HKRL7.js} +3 -3
  144. package/dist/{chunk-3XQYM4RE.js → chunk-M6C7SOR6.js} +2 -2
  145. package/dist/{chunk-JN72EPOE.js → chunk-MAAL6OZV.js} +7 -7
  146. package/dist/chunk-MAAL6OZV.js.map +1 -0
  147. package/dist/{chunk-MY2PLXSM.js → chunk-MCTM4YPL.js} +2 -1
  148. package/dist/{chunk-MY2PLXSM.js.map → chunk-MCTM4YPL.js.map} +1 -1
  149. package/dist/{chunk-T4QGUXCV.js → chunk-MXCAJKMA.js} +9 -9
  150. package/dist/{chunk-BMYZTZ5D.js → chunk-MY7B3HYC.js} +2 -2
  151. package/dist/{chunk-NB3LRY4T.js → chunk-N4PYB235.js} +2 -2
  152. package/dist/{chunk-S3YM4SZQ.js → chunk-N7QV3CMX.js} +2 -2
  153. package/dist/{chunk-3GN2PEOT.js → chunk-NYKIUQL6.js} +2 -2
  154. package/dist/{chunk-WQZZDN5M.js → chunk-OCFXVDBZ.js} +322 -376
  155. package/dist/chunk-OCFXVDBZ.js.map +1 -0
  156. package/dist/{chunk-L2LBMKSI.js → chunk-OK52IY3W.js} +10 -10
  157. package/dist/{chunk-WB2I2EQK.js → chunk-P2ODTYOF.js} +2 -2
  158. package/dist/{chunk-RIEX7NOZ.js → chunk-PEBHPCX2.js} +2 -2
  159. package/dist/{chunk-VYCOJKSX.js → chunk-PO7NXNGC.js} +2 -2
  160. package/dist/{chunk-G6SVZWPB.js → chunk-PZX3AQLF.js} +44 -1
  161. package/dist/chunk-PZX3AQLF.js.map +1 -0
  162. package/dist/{chunk-XAITGGX5.js → chunk-Q4AUIFUA.js} +4 -4
  163. package/dist/{chunk-EU3N5MK3.js → chunk-QAW55ZHJ.js} +4 -4
  164. package/dist/{chunk-SVZWVZ7E.js → chunk-QB57WIS6.js} +101 -93
  165. package/dist/chunk-QB57WIS6.js.map +1 -0
  166. package/dist/{chunk-I4G4UWQA.js → chunk-QI2E6ISQ.js} +1 -1
  167. package/dist/{chunk-CK5U7JGH.js → chunk-QI7K7BZ5.js} +2 -2
  168. package/dist/{chunk-VTPASHTO.js → chunk-R3X2T2LP.js} +3 -3
  169. package/dist/{chunk-I53NAHZ4.js → chunk-RBITPCMK.js} +10 -9
  170. package/dist/chunk-RBITPCMK.js.map +1 -0
  171. package/dist/{chunk-EJPF6XDQ.js → chunk-RDJ6C7NZ.js} +3 -3
  172. package/dist/{chunk-C2WWQ4OR.js → chunk-RMMA44HE.js} +1 -1
  173. package/dist/chunk-RMMA44HE.js.map +1 -0
  174. package/dist/{chunk-QC56VZXC.js → chunk-S2GE2K36.js} +85 -82
  175. package/dist/{chunk-QC56VZXC.js.map → chunk-S2GE2K36.js.map} +1 -1
  176. package/dist/{chunk-2UPTRDGM.js → chunk-SCXURCVT.js} +3 -3
  177. package/dist/{chunk-S55WTPFJ.js → chunk-STEOT4DI.js} +20 -20
  178. package/dist/chunk-STEOT4DI.js.map +1 -0
  179. package/dist/{chunk-N3MNDVJO.js → chunk-SWBC3UER.js} +2 -2
  180. package/dist/{chunk-ZTHGFTMW.js → chunk-TKC5LZ43.js} +2 -2
  181. package/dist/chunk-TYT4TEQV.js +45 -0
  182. package/dist/chunk-TYT4TEQV.js.map +1 -0
  183. package/dist/{chunk-YRLHEEQT.js → chunk-U6VC6RP3.js} +2 -2
  184. package/dist/chunk-UCPGOUDZ.js +726 -0
  185. package/dist/chunk-UCPGOUDZ.js.map +1 -0
  186. package/dist/{chunk-MOQPEFLN.js → chunk-UP75KL3L.js} +2 -2
  187. package/dist/{chunk-D5F7TKMH.js → chunk-V4JZCTMK.js} +2 -2
  188. package/dist/{chunk-NAYBHNGN.js → chunk-VALKBYBN.js} +2 -2
  189. package/dist/{chunk-BUKK5SWA.js → chunk-VDX2J7OX.js} +2 -2
  190. package/dist/{chunk-H36X4NRH.js → chunk-VVX5PZFS.js} +2 -2
  191. package/dist/{chunk-RCQKAOOB.js → chunk-W3XXFPRI.js} +52 -52
  192. package/dist/{chunk-W75BBBSY.js → chunk-WKY7N3L7.js} +11 -11
  193. package/dist/{chunk-GZAGLCQH.js → chunk-X5SHE2O3.js} +3 -3
  194. package/dist/{chunk-OAOBL6OY.js → chunk-XOV7NRY5.js} +5 -5
  195. package/dist/{chunk-FZVROZKC.js → chunk-XVZA6VXC.js} +4 -4
  196. package/dist/{chunk-RGMVMVMF.js → chunk-YBA52OKG.js} +2 -1
  197. package/dist/chunk-YBA52OKG.js.map +1 -0
  198. package/dist/{chunk-IP5VLSLO.js → chunk-YJ7MW627.js} +2 -2
  199. package/dist/{chunk-HS75MCQF.js → chunk-YRULQW5J.js} +2 -2
  200. package/dist/{chunk-DINRT75P.js → chunk-YSBL7VPO.js} +2 -2
  201. package/dist/{chunk-QWUJWBZ5.js → chunk-Z3O53WZR.js} +2 -2
  202. package/dist/{chunk-KERNN6BC.js → chunk-Z5LP6ILP.js} +2 -2
  203. package/dist/{chunk-5FNZ3G6X.js → chunk-Z6EZCN6Z.js} +5 -5
  204. package/dist/chunk-ZJP66Q4I.js +48 -0
  205. package/dist/chunk-ZJP66Q4I.js.map +1 -0
  206. package/dist/{chunk-JS3S2HVG.js → chunk-ZZZ4I455.js} +2 -2
  207. package/dist/cli.js +122 -115
  208. package/dist/coding/export-okf-codegraph.js +19 -19
  209. package/dist/coding/pre-action-gate.js +2 -2
  210. package/dist/compounding/engine.js +19 -19
  211. package/dist/config.js +34 -28
  212. package/dist/connectors/codex-materialize-runner.js +18 -18
  213. package/dist/connectors/index.js +18 -18
  214. package/dist/console/state.d.ts +30 -0
  215. package/dist/console/state.d.ts.map +1 -1
  216. package/dist/contradiction/index.js +7 -7
  217. package/dist/{contradiction-scan-5EJYRKD3.js → contradiction-scan-TAGMOUQ2.js} +3 -3
  218. package/dist/convergence-refresh.js +20 -20
  219. package/dist/corpus-watermark.js +20 -20
  220. package/dist/correction/correction-access-wiring.d.ts.map +1 -1
  221. package/dist/dashboard-runtime.js +2 -2
  222. package/dist/deep-recall-http-glue.js +15 -8
  223. package/dist/deep-recall-policy-llm.js +3 -3
  224. package/dist/deep-recall.js +2 -2
  225. package/dist/embedding-fallback.d.ts +41 -31
  226. package/dist/embedding-fallback.d.ts.map +1 -1
  227. package/dist/embedding-fallback.js +9 -1
  228. package/dist/embedding-generation-lock.d.ts +49 -0
  229. package/dist/embedding-generation-lock.d.ts.map +1 -0
  230. package/dist/embedding-generation-lock.js +19 -0
  231. package/dist/embedding-index-storage.d.ts +276 -0
  232. package/dist/embedding-index-storage.d.ts.map +1 -0
  233. package/dist/embedding-index-storage.js +28 -0
  234. package/dist/entity-retrieval.js +19 -19
  235. package/dist/event-order-recall.js +3 -3
  236. package/dist/explicit-capture.js +6 -6
  237. package/dist/external-wiki-access.js +75 -68
  238. package/dist/extraction-faithfulness.js +3 -3
  239. package/dist/extraction-judge.js +3 -3
  240. package/dist/extraction-liveness.d.ts +9 -0
  241. package/dist/extraction-liveness.d.ts.map +1 -1
  242. package/dist/extraction-liveness.js +2 -1
  243. package/dist/extraction-llm-health.d.ts +32 -0
  244. package/dist/extraction-llm-health.d.ts.map +1 -0
  245. package/dist/extraction-llm-health.js +17 -0
  246. package/dist/extraction-span-config.js +3 -2
  247. package/dist/extraction-span-materialize.js +2 -2
  248. package/dist/extraction.js +29 -28
  249. package/dist/fallback-llm.js +2 -2
  250. package/dist/{first-start-migration-GAT7G733.js → first-start-migration-7OATHJKB.js} +4 -4
  251. package/dist/{graph-edge-decay-PVKDKLQK.js → graph-edge-decay-MKIRZ762.js} +6 -6
  252. package/dist/graph-jsonl.js +4 -4
  253. package/dist/graph-snapshot.js +4 -4
  254. package/dist/graph.js +5 -5
  255. package/dist/harmonic-retrieval.js +1 -1
  256. package/dist/index.d.ts +2 -0
  257. package/dist/index.d.ts.map +1 -1
  258. package/dist/index.js +237 -206
  259. package/dist/json-extract.d.ts.map +1 -1
  260. package/dist/json-extract.js +1 -1
  261. package/dist/lcm/engine.js +2 -2
  262. package/dist/lcm/index.js +5 -5
  263. package/dist/local-llm.d.ts.map +1 -1
  264. package/dist/local-llm.js +2 -2
  265. package/dist/location/index.js +21 -21
  266. package/dist/maintenance/memory-governance.js +18 -18
  267. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +18 -18
  268. package/dist/maintenance/rebuild-memory-projection.js +19 -19
  269. package/dist/memory-browse-operations.js +75 -68
  270. package/dist/memory-browse.js +3 -3
  271. package/dist/memory-subject.js +3 -3
  272. package/dist/namespaces/migrate.js +19 -19
  273. package/dist/namespaces/search.js +4 -4
  274. package/dist/namespaces/storage.js +18 -18
  275. package/dist/offline-sync-embedding-generation.d.ts +207 -0
  276. package/dist/offline-sync-embedding-generation.d.ts.map +1 -0
  277. package/dist/offline-sync-embedding-generation.js +42 -0
  278. package/dist/offline-sync-embedding-generation.js.map +1 -0
  279. package/dist/offline-sync-exclude-globs.d.ts +15 -0
  280. package/dist/offline-sync-exclude-globs.d.ts.map +1 -1
  281. package/dist/offline-sync-exclude-globs.js +11 -3
  282. package/dist/offline-sync-file-io.d.ts +7 -0
  283. package/dist/offline-sync-file-io.d.ts.map +1 -1
  284. package/dist/offline-sync-file-io.js +14 -2
  285. package/dist/offline-sync-impression-drain.js +3 -3
  286. package/dist/offline-sync-runtime-state.d.ts +5 -0
  287. package/dist/offline-sync-runtime-state.d.ts.map +1 -0
  288. package/dist/offline-sync-runtime-state.js +14 -0
  289. package/dist/offline-sync-runtime-state.js.map +1 -0
  290. package/dist/offline-sync-snapshot-stream.d.ts +26 -0
  291. package/dist/offline-sync-snapshot-stream.d.ts.map +1 -0
  292. package/dist/offline-sync-snapshot-stream.js +28 -0
  293. package/dist/offline-sync-snapshot-stream.js.map +1 -0
  294. package/dist/offline-sync-upload-staging.d.ts +34 -0
  295. package/dist/offline-sync-upload-staging.d.ts.map +1 -0
  296. package/dist/offline-sync-upload-staging.js +26 -0
  297. package/dist/offline-sync-upload-staging.js.map +1 -0
  298. package/dist/offline-sync.d.ts +46 -27
  299. package/dist/offline-sync.d.ts.map +1 -1
  300. package/dist/offline-sync.js +21 -8
  301. package/dist/operator-doctor-corpus.js +21 -21
  302. package/dist/operator-toolkit.js +46 -40
  303. package/dist/orchestration/extraction-run.d.ts.map +1 -1
  304. package/dist/orchestration/maintenance.js +30 -30
  305. package/dist/orchestrator.d.ts.map +1 -1
  306. package/dist/orchestrator.js +159 -152
  307. package/dist/recall-navigation-operations.js +75 -68
  308. package/dist/recall-navigation.js +74 -67
  309. package/dist/recall-planner-llm.js +2 -2
  310. package/dist/recall-state-view-admission.js +3 -3
  311. package/dist/recall-why-service.js +6 -6
  312. package/dist/recall-why.js +3 -3
  313. package/dist/reconcile/manifest.js +4 -4
  314. package/dist/{resolution-UYJU5JUA.js → resolution-K4A3VPKZ.js} +8 -8
  315. package/dist/resolution-K4A3VPKZ.js.map +1 -0
  316. package/dist/resume-bundles.js +35 -29
  317. package/dist/salvage-envelope.js +5 -5
  318. package/dist/search/factory.js +3 -3
  319. package/dist/search/index.js +7 -7
  320. package/dist/secure-store/index.js +2 -2
  321. package/dist/secure-store/secure-fs.d.ts +14 -1
  322. package/dist/secure-store/secure-fs.d.ts.map +1 -1
  323. package/dist/semantic-consolidation.js +19 -19
  324. package/dist/semantic-rule-promotion.js +18 -18
  325. package/dist/semantic-rule-verifier.js +18 -18
  326. package/dist/source-agent-qualifier.js +23 -22
  327. package/dist/standup/build.js +18 -18
  328. package/dist/{state-ITXJKXRE.js → state-JB47KHYV.js} +2 -2
  329. package/dist/state-JB47KHYV.js.map +1 -0
  330. package/dist/storage.d.ts +9 -3
  331. package/dist/storage.d.ts.map +1 -1
  332. package/dist/storage.js +17 -17
  333. package/dist/subject-config.js +4 -4
  334. package/dist/summarizer.js +6 -6
  335. package/dist/support-passport/index.js +77 -70
  336. package/dist/{trace-ZFVRH4GO.js → trace-LXXCMZJA.js} +3 -3
  337. package/dist/transfer/backup.js +2 -2
  338. package/dist/transfer/capsule-export.js +2 -2
  339. package/dist/transfer/capsule-import.js +3 -3
  340. package/dist/transfer/export-okf.js +19 -19
  341. package/dist/{tui-55UJMJX7.js → tui-2Z5WOV7L.js} +3 -3
  342. package/dist/tui-2Z5WOV7L.js.map +1 -0
  343. package/dist/verified-recall.js +18 -18
  344. package/dist/who-knows-cli.js +19 -19
  345. package/dist/who-knows.js +18 -18
  346. package/dist/write-envelope.js +4 -4
  347. package/package.json +2 -2
  348. package/src/access-extraction-force-flush.ts +15 -7
  349. package/src/access-http-offline-stream.ts +3 -0
  350. package/src/access-mcp-output-schemas.ts +1 -0
  351. package/src/access-mcp.ts +90 -28
  352. package/src/access-service-health.test.ts +41 -5
  353. package/src/access-service-helpers.ts +1 -0
  354. package/src/access-service-observe-lcm-parity.test.ts +56 -24
  355. package/src/access-service.ts +7 -18
  356. package/src/console/state.test.ts +56 -0
  357. package/src/console/state.ts +83 -0
  358. package/src/correction/correction-access-wiring.ts +36 -8
  359. package/src/correction/correction-classify-parse.test.ts +178 -0
  360. package/src/embedding-fallback.ts +325 -141
  361. package/src/embedding-generation-lock.ts +141 -0
  362. package/src/embedding-index-storage.test.ts +245 -0
  363. package/src/embedding-index-storage.ts +1068 -0
  364. package/src/extraction-liveness.ts +13 -1
  365. package/src/extraction-llm-health.test.ts +103 -0
  366. package/src/extraction-llm-health.ts +71 -0
  367. package/src/index.ts +3 -3
  368. package/src/json-extract.ts +6 -0
  369. package/src/local-llm-thinking.test.ts +12 -0
  370. package/src/local-llm.ts +1 -0
  371. package/src/offline-sync-embedding-generation.test.ts +2173 -0
  372. package/src/offline-sync-embedding-generation.ts +885 -0
  373. package/src/offline-sync-exclude-globs.ts +112 -0
  374. package/src/offline-sync-file-io.ts +48 -0
  375. package/src/offline-sync-runtime-state.ts +48 -0
  376. package/src/offline-sync-snapshot-stream.ts +99 -0
  377. package/src/offline-sync-upload-staging.ts +291 -0
  378. package/src/offline-sync.test.ts +164 -1
  379. package/src/offline-sync.ts +458 -453
  380. package/src/orchestration/extraction-run.test.ts +55 -1
  381. package/src/orchestration/extraction-run.ts +13 -0
  382. package/src/orchestrator.ts +2 -1
  383. package/src/secure-store/secure-fs.ts +26 -6
  384. package/src/storage.ts +11 -6
  385. package/dist/chunk-3BTFBQLO.js.map +0 -1
  386. package/dist/chunk-3SG4FGDV.js.map +0 -1
  387. package/dist/chunk-6DTJBTHL.js.map +0 -1
  388. package/dist/chunk-AREGMX2J.js +0 -50
  389. package/dist/chunk-AREGMX2J.js.map +0 -1
  390. package/dist/chunk-C2WWQ4OR.js.map +0 -1
  391. package/dist/chunk-G6SVZWPB.js.map +0 -1
  392. package/dist/chunk-I53NAHZ4.js.map +0 -1
  393. package/dist/chunk-JN72EPOE.js.map +0 -1
  394. package/dist/chunk-QB75LKQQ.js.map +0 -1
  395. package/dist/chunk-RB776UH3.js.map +0 -1
  396. package/dist/chunk-RGMVMVMF.js.map +0 -1
  397. package/dist/chunk-S55WTPFJ.js.map +0 -1
  398. package/dist/chunk-SVZWVZ7E.js.map +0 -1
  399. package/dist/chunk-WQZZDN5M.js.map +0 -1
  400. /package/dist/{auto-sync-WEUDMXUW.js.map → auto-sync-U5N3CB5Y.js.map} +0 -0
  401. /package/dist/{capsule-crypto-GWVG7LGC.js.map → capsule-crypto-YO5QJ6L3.js.map} +0 -0
  402. /package/dist/{capsule-merge-3MNURAQ3.js.map → capsule-merge-ZCXUPLNJ.js.map} +0 -0
  403. /package/dist/{chunk-JE334SIA.js.map → chunk-22DYJLFN.js.map} +0 -0
  404. /package/dist/{chunk-DGAWEJVI.js.map → chunk-2DLWLNO4.js.map} +0 -0
  405. /package/dist/{chunk-J3IWHCMF.js.map → chunk-2DT7PIDZ.js.map} +0 -0
  406. /package/dist/{chunk-NKFLHW7J.js.map → chunk-2NAD4YCI.js.map} +0 -0
  407. /package/dist/{chunk-SV72F4DS.js.map → chunk-2WLMWP2T.js.map} +0 -0
  408. /package/dist/{chunk-IVH7INFM.js.map → chunk-3O5BB45T.js.map} +0 -0
  409. /package/dist/{chunk-RDEUG2M2.js.map → chunk-3SGTV7RD.js.map} +0 -0
  410. /package/dist/{chunk-QOPB6KE3.js.map → chunk-3TB2XQ3C.js.map} +0 -0
  411. /package/dist/{chunk-C7JAUNFC.js.map → chunk-4KN2JLQV.js.map} +0 -0
  412. /package/dist/{chunk-TIQZNDZ5.js.map → chunk-4MP7XR5J.js.map} +0 -0
  413. /package/dist/{chunk-7DE2NOON.js.map → chunk-4O4XCBOS.js.map} +0 -0
  414. /package/dist/{chunk-SLEAJ67D.js.map → chunk-4OQET73E.js.map} +0 -0
  415. /package/dist/{chunk-Q3THVX75.js.map → chunk-4RL3JOB3.js.map} +0 -0
  416. /package/dist/{chunk-GDYU6VF4.js.map → chunk-5AYUHSAU.js.map} +0 -0
  417. /package/dist/{chunk-B47OHVFT.js.map → chunk-5E6RYRYW.js.map} +0 -0
  418. /package/dist/{chunk-2U3R3DDH.js.map → chunk-5FAGCOZD.js.map} +0 -0
  419. /package/dist/{chunk-CRKWSSS4.js.map → chunk-5MNUBT7T.js.map} +0 -0
  420. /package/dist/{chunk-52II7PB6.js.map → chunk-645WOU5I.js.map} +0 -0
  421. /package/dist/{chunk-7G6WUOLB.js.map → chunk-66DKTOBY.js.map} +0 -0
  422. /package/dist/{chunk-AZ2X5LNA.js.map → chunk-6LYAEM54.js.map} +0 -0
  423. /package/dist/{chunk-DUJYRVUD.js.map → chunk-6O2VUWV6.js.map} +0 -0
  424. /package/dist/{chunk-IMAP236M.js.map → chunk-6UQFJULG.js.map} +0 -0
  425. /package/dist/{chunk-2I6VHX6Q.js.map → chunk-76M5OK7V.js.map} +0 -0
  426. /package/dist/{chunk-OEBLZZKK.js.map → chunk-7XG7KMIW.js.map} +0 -0
  427. /package/dist/{chunk-FYAU4S6E.js.map → chunk-A4VSOUCB.js.map} +0 -0
  428. /package/dist/{chunk-GEX5NI36.js.map → chunk-AE5U5NMS.js.map} +0 -0
  429. /package/dist/{chunk-UNJOB4DY.js.map → chunk-AEHVZY3X.js.map} +0 -0
  430. /package/dist/{chunk-ML2MVAEZ.js.map → chunk-AEU4EQGV.js.map} +0 -0
  431. /package/dist/{chunk-JVY556FW.js.map → chunk-AWG3TIAY.js.map} +0 -0
  432. /package/dist/{chunk-JX6USXZR.js.map → chunk-AXC4WSF3.js.map} +0 -0
  433. /package/dist/{chunk-BOUFVFV2.js.map → chunk-AZSGSRIM.js.map} +0 -0
  434. /package/dist/{chunk-VZBW6CDG.js.map → chunk-B3O3IRZN.js.map} +0 -0
  435. /package/dist/{chunk-4V44AA7O.js.map → chunk-BGI4NUAO.js.map} +0 -0
  436. /package/dist/{chunk-BNPLDVM6.js.map → chunk-BJVO4H5C.js.map} +0 -0
  437. /package/dist/{chunk-S42HZILI.js.map → chunk-BLVR4WYU.js.map} +0 -0
  438. /package/dist/{chunk-JXMECXMF.js.map → chunk-BOZJD5PK.js.map} +0 -0
  439. /package/dist/{chunk-OWHERGF2.js.map → chunk-BXLOS5AJ.js.map} +0 -0
  440. /package/dist/{chunk-EVBI7S4J.js.map → chunk-C6U57KG4.js.map} +0 -0
  441. /package/dist/{chunk-T4BE5KJM.js.map → chunk-C7ZDKWPS.js.map} +0 -0
  442. /package/dist/{chunk-HKFJ3PF3.js.map → chunk-CRXRZA3G.js.map} +0 -0
  443. /package/dist/{chunk-NMBX4D7P.js.map → chunk-CZJA2EQE.js.map} +0 -0
  444. /package/dist/{chunk-BJX3VQWZ.js.map → chunk-D5BSQR63.js.map} +0 -0
  445. /package/dist/{chunk-MFZACYZ6.js.map → chunk-DGS34QSY.js.map} +0 -0
  446. /package/dist/{chunk-UVYI6VIX.js.map → chunk-DQEMWVMT.js.map} +0 -0
  447. /package/dist/{chunk-DQISIT63.js.map → chunk-DZ42MDZE.js.map} +0 -0
  448. /package/dist/{chunk-5ELENZTA.js.map → chunk-EDIH6X2P.js.map} +0 -0
  449. /package/dist/{chunk-C3QXITIP.js.map → chunk-EQUD7FP4.js.map} +0 -0
  450. /package/dist/{chunk-YNDLCWXS.js.map → chunk-EZ25VE3G.js.map} +0 -0
  451. /package/dist/{chunk-IA3X2Y4K.js.map → chunk-FJLQXMTU.js.map} +0 -0
  452. /package/dist/{chunk-TY446V34.js.map → chunk-FKD656D2.js.map} +0 -0
  453. /package/dist/{chunk-CLXTDOKV.js.map → chunk-FUNBJQYR.js.map} +0 -0
  454. /package/dist/{chunk-CLSYE5OZ.js.map → chunk-G2STTRYB.js.map} +0 -0
  455. /package/dist/{chunk-SQDKX4RW.js.map → chunk-GWM6T5EQ.js.map} +0 -0
  456. /package/dist/{chunk-AXDOF4VI.js.map → chunk-GZDTVYK2.js.map} +0 -0
  457. /package/dist/{chunk-55FCXCXJ.js.map → chunk-H56VY4BY.js.map} +0 -0
  458. /package/dist/{chunk-OM5BIPBR.js.map → chunk-H5BJWPUI.js.map} +0 -0
  459. /package/dist/{chunk-7QK5TUTW.js.map → chunk-HLB7GKGG.js.map} +0 -0
  460. /package/dist/{chunk-CHDGJJV6.js.map → chunk-HWLIMQE2.js.map} +0 -0
  461. /package/dist/{chunk-NAFPHFDM.js.map → chunk-JNLEJE4A.js.map} +0 -0
  462. /package/dist/{chunk-7UUKRS3J.js.map → chunk-JVPWRNHA.js.map} +0 -0
  463. /package/dist/{chunk-6ET2VII7.js.map → chunk-JYG2334B.js.map} +0 -0
  464. /package/dist/{chunk-2QG4SLZI.js.map → chunk-KF7PQ7G2.js.map} +0 -0
  465. /package/dist/{chunk-WIKO4SCR.js.map → chunk-LHRQBMJN.js.map} +0 -0
  466. /package/dist/{chunk-IFCKHPY4.js.map → chunk-LO4UKPKX.js.map} +0 -0
  467. /package/dist/{chunk-TEV2JGU5.js.map → chunk-LU7E2MXE.js.map} +0 -0
  468. /package/dist/{chunk-RJLV4BKR.js.map → chunk-LV5H6FTN.js.map} +0 -0
  469. /package/dist/{chunk-IBGEHKQ6.js.map → chunk-LW3HKRL7.js.map} +0 -0
  470. /package/dist/{chunk-3XQYM4RE.js.map → chunk-M6C7SOR6.js.map} +0 -0
  471. /package/dist/{chunk-T4QGUXCV.js.map → chunk-MXCAJKMA.js.map} +0 -0
  472. /package/dist/{chunk-BMYZTZ5D.js.map → chunk-MY7B3HYC.js.map} +0 -0
  473. /package/dist/{chunk-NB3LRY4T.js.map → chunk-N4PYB235.js.map} +0 -0
  474. /package/dist/{chunk-S3YM4SZQ.js.map → chunk-N7QV3CMX.js.map} +0 -0
  475. /package/dist/{chunk-3GN2PEOT.js.map → chunk-NYKIUQL6.js.map} +0 -0
  476. /package/dist/{chunk-L2LBMKSI.js.map → chunk-OK52IY3W.js.map} +0 -0
  477. /package/dist/{chunk-WB2I2EQK.js.map → chunk-P2ODTYOF.js.map} +0 -0
  478. /package/dist/{chunk-RIEX7NOZ.js.map → chunk-PEBHPCX2.js.map} +0 -0
  479. /package/dist/{chunk-VYCOJKSX.js.map → chunk-PO7NXNGC.js.map} +0 -0
  480. /package/dist/{chunk-XAITGGX5.js.map → chunk-Q4AUIFUA.js.map} +0 -0
  481. /package/dist/{chunk-EU3N5MK3.js.map → chunk-QAW55ZHJ.js.map} +0 -0
  482. /package/dist/{chunk-I4G4UWQA.js.map → chunk-QI2E6ISQ.js.map} +0 -0
  483. /package/dist/{chunk-CK5U7JGH.js.map → chunk-QI7K7BZ5.js.map} +0 -0
  484. /package/dist/{chunk-VTPASHTO.js.map → chunk-R3X2T2LP.js.map} +0 -0
  485. /package/dist/{chunk-EJPF6XDQ.js.map → chunk-RDJ6C7NZ.js.map} +0 -0
  486. /package/dist/{chunk-2UPTRDGM.js.map → chunk-SCXURCVT.js.map} +0 -0
  487. /package/dist/{chunk-N3MNDVJO.js.map → chunk-SWBC3UER.js.map} +0 -0
  488. /package/dist/{chunk-ZTHGFTMW.js.map → chunk-TKC5LZ43.js.map} +0 -0
  489. /package/dist/{chunk-YRLHEEQT.js.map → chunk-U6VC6RP3.js.map} +0 -0
  490. /package/dist/{chunk-MOQPEFLN.js.map → chunk-UP75KL3L.js.map} +0 -0
  491. /package/dist/{chunk-D5F7TKMH.js.map → chunk-V4JZCTMK.js.map} +0 -0
  492. /package/dist/{chunk-NAYBHNGN.js.map → chunk-VALKBYBN.js.map} +0 -0
  493. /package/dist/{chunk-BUKK5SWA.js.map → chunk-VDX2J7OX.js.map} +0 -0
  494. /package/dist/{chunk-H36X4NRH.js.map → chunk-VVX5PZFS.js.map} +0 -0
  495. /package/dist/{chunk-RCQKAOOB.js.map → chunk-W3XXFPRI.js.map} +0 -0
  496. /package/dist/{chunk-W75BBBSY.js.map → chunk-WKY7N3L7.js.map} +0 -0
  497. /package/dist/{chunk-GZAGLCQH.js.map → chunk-X5SHE2O3.js.map} +0 -0
  498. /package/dist/{chunk-OAOBL6OY.js.map → chunk-XOV7NRY5.js.map} +0 -0
  499. /package/dist/{chunk-FZVROZKC.js.map → chunk-XVZA6VXC.js.map} +0 -0
  500. /package/dist/{chunk-IP5VLSLO.js.map → chunk-YJ7MW627.js.map} +0 -0
  501. /package/dist/{chunk-HS75MCQF.js.map → chunk-YRULQW5J.js.map} +0 -0
  502. /package/dist/{chunk-DINRT75P.js.map → chunk-YSBL7VPO.js.map} +0 -0
  503. /package/dist/{chunk-QWUJWBZ5.js.map → chunk-Z3O53WZR.js.map} +0 -0
  504. /package/dist/{chunk-KERNN6BC.js.map → chunk-Z5LP6ILP.js.map} +0 -0
  505. /package/dist/{chunk-5FNZ3G6X.js.map → chunk-Z6EZCN6Z.js.map} +0 -0
  506. /package/dist/{chunk-JS3S2HVG.js.map → chunk-ZZZ4I455.js.map} +0 -0
  507. /package/dist/{contradiction-scan-5EJYRKD3.js.map → contradiction-scan-TAGMOUQ2.js.map} +0 -0
  508. /package/dist/{graph-edge-decay-PVKDKLQK.js.map → embedding-generation-lock.js.map} +0 -0
  509. /package/dist/{resolution-UYJU5JUA.js.map → embedding-index-storage.js.map} +0 -0
  510. /package/dist/{state-ITXJKXRE.js.map → extraction-llm-health.js.map} +0 -0
  511. /package/dist/{first-start-migration-GAT7G733.js.map → first-start-migration-7OATHJKB.js.map} +0 -0
  512. /package/dist/{tui-55UJMJX7.js.map → graph-edge-decay-MKIRZ762.js.map} +0 -0
  513. /package/dist/{trace-ZFVRH4GO.js.map → trace-LXXCMZJA.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/console/state.ts"],"sourcesContent":["/**\n * Structured engine-state aggregator for the operator console (issue #688).\n *\n * This module is the data layer for the upcoming console TUI / HTTP /\n * MCP surfaces. It collects a point-in-time snapshot of the engine's\n * runtime state — buffer contents, extraction queue depth, recent\n * dedup decisions, the tail of the maintenance/observation ledger,\n * QMD probe status, and daemon metadata — into a single\n * JSON-serializable shape.\n *\n * Design contract:\n * - Each subsystem read is wrapped in try/catch so one failure\n * never crashes the whole snapshot. Failed reads return null /\n * empty values and append a string to `errors`.\n * - The output MUST be JSON-serializable so the CLI / HTTP / MCP\n * surfaces can pipe it directly through `JSON.stringify`.\n * - Read-only: this module never mutates orchestrator state.\n *\n * PR 1/3 of issue #688 wires only the data layer + a CLI flag. The\n * TUI (PR 2/3), HTTP `/console/state`, MCP `engram.console_state`,\n * SSE live updates, and trace replay (PR 3/3) land separately.\n */\n\nimport { promises as fs, createReadStream } from \"node:fs\";\nimport { createInterface } from \"node:readline\";\nimport path from \"node:path\";\n\n/**\n * Public state read off the orchestrator. We intentionally type this\n * as a permissive duck-typed shape rather than importing the concrete\n * `Orchestrator` class — the aggregator must tolerate missing fields\n * (e.g. tests that pass a stub) without throwing. Each accessor is\n * defensively read at call time.\n */\nexport interface ConsoleStateOrchestratorLike {\n config?: {\n memoryDir?: string;\n };\n buffer?: {\n getTurns?: (bufferKey?: string) => Array<{ content?: string }>;\n };\n qmd?: {\n debugStatus?: () => string;\n isAvailable?: () => boolean;\n isDaemonMode?: () => boolean;\n };\n /**\n * Optional console-specific read surface added by future slices.\n * The aggregator falls back to placeholders when these are absent\n * so PR 1/3 ships without coupling to private orchestrator state.\n */\n getConsoleExtractionQueueDepth?: () => number;\n getConsoleExtractionRecentVerdicts?: () => ReadonlyArray<ConsoleExtractionVerdict>;\n getConsoleDedupRecentDecisions?: () => ReadonlyArray<ConsoleDedupDecision>;\n /**\n * Process / daemon metadata. Hosts that run remnic as a long-lived\n * daemon (gateway, MCP server) can override these; otherwise the\n * aggregator falls back to `process.uptime()` and the package\n * version read from disk.\n */\n getConsoleDaemonInfo?: () => ConsoleDaemonInfo;\n /**\n * Optional faithfulness gate distribution (issue #1576). Returns the\n * per-orchestrator running counters when the gate is active, or undefined\n * when the gate is off (so the snapshot omits the block).\n */\n getConsoleFaithfulnessDistribution?: () => ConsoleFaithfulnessDistribution | undefined;\n}\n\nexport interface ConsoleBufferState {\n /** Number of turns currently held in the default buffer slot. */\n turnsCount: number;\n /** Approximate byte size of buffered content (UTF-8). */\n byteCount: number;\n}\n\nexport interface ConsoleExtractionVerdict {\n /** ISO-8601 timestamp the verdict was recorded. */\n ts: string;\n /** Verdict kind — typically \"accept\" | \"reject\" | \"defer\". */\n kind: string;\n /** Optional short reason from the judge or fallback. */\n reason?: string;\n}\n\nexport interface ConsoleExtractionQueueState {\n /** Number of pending extraction tasks queued in the orchestrator. */\n depth: number;\n /** Most recent judge verdicts, newest last. Capped at 25 entries. */\n recentVerdicts: ConsoleExtractionVerdict[];\n}\n\nexport interface ConsoleDedupDecision {\n /** ISO-8601 timestamp the decision was recorded. */\n ts: string;\n /** Outcome of the dedup check — \"duplicate\" | \"novel\" | etc. */\n decision: string;\n /** Optional content fingerprint (first 16 chars of a hash). */\n fingerprint?: string;\n /** Optional similarity score that drove the decision. */\n similarity?: number;\n}\n\nexport interface ConsoleMaintenanceLedgerEvent {\n /** ISO-8601 timestamp from the ledger row. */\n ts: string;\n /** Event category (e.g. \"EXTRACTION_JUDGE_VERDICT\"). */\n category: string;\n /** Compact one-line summary. */\n summary: string;\n}\n\nexport interface ConsoleQmdProbeState {\n /** Whether QMD is reachable via CLI or daemon. */\n available: boolean;\n /** Whether the daemon transport is in use. */\n daemonMode: boolean;\n /** Raw debug-status string from `qmd.debugStatus()`. */\n debug: string;\n}\n\nexport interface ConsoleDaemonInfo {\n /** Process uptime in milliseconds. */\n uptimeMs: number;\n /** Package / daemon version (best-effort). */\n version: string;\n}\n\n/**\n * Faithfulness gate verdict distribution (issue #1576). Surfaced via\n * console_state so `remnic doctor` can render how the gate is performing.\n */\nexport interface ConsoleFaithfulnessDistribution {\n entailed: number;\n contradicted: number;\n unsupported: number;\n unchecked: number;\n skippedNoSpan: number;\n}\n\nexport interface ConsoleStateSnapshot {\n /** ISO-8601 capture timestamp. */\n capturedAt: string;\n bufferState: ConsoleBufferState;\n extractionQueue: ConsoleExtractionQueueState;\n dedupRecent: ConsoleDedupDecision[];\n maintenanceLedgerTail: ConsoleMaintenanceLedgerEvent[];\n qmdProbe: ConsoleQmdProbeState;\n daemon: ConsoleDaemonInfo;\n /** Faithfulness gate distribution (issue #1576). Absent when the gate is off. */\n faithfulness?: ConsoleFaithfulnessDistribution;\n /**\n * Subsystem read errors. One entry per failed reader keyed by\n * subsystem name (e.g. `\"bufferState: ...\"`). An empty array means\n * every section was read cleanly.\n */\n errors: string[];\n}\n\nconst MAX_LEDGER_TAIL = 50;\nconst MAX_VERDICT_TAIL = 25;\nconst MAX_DEDUP_TAIL = 10;\n\n/**\n * Gather a `ConsoleStateSnapshot` from the orchestrator. Each\n * subsystem read is independent; a thrown error in one reader is\n * captured in `errors` and the corresponding section is filled with\n * empty / placeholder values.\n */\nexport async function gatherConsoleState(\n orchestrator: ConsoleStateOrchestratorLike,\n): Promise<ConsoleStateSnapshot> {\n const errors: string[] = [];\n const capturedAt = new Date().toISOString();\n\n const bufferState = readBufferState(orchestrator, errors);\n const extractionQueue = readExtractionQueue(orchestrator, errors);\n const dedupRecent = readDedupRecent(orchestrator, errors);\n const maintenanceLedgerTail = await readMaintenanceLedgerTail(\n orchestrator,\n errors,\n );\n const qmdProbe = readQmdProbe(orchestrator, errors);\n const daemon = readDaemonInfo(orchestrator, errors);\n\n // Faithfulness gate distribution (issue #1576). Optional — absent when\n // the gate is off or the orchestrator does not expose the accessor.\n let faithfulness: ConsoleFaithfulnessDistribution | undefined;\n try {\n if (typeof orchestrator.getConsoleFaithfulnessDistribution === \"function\") {\n faithfulness = orchestrator.getConsoleFaithfulnessDistribution();\n }\n } catch (err) {\n errors.push(`faithfulness: ${describeError(err)}`);\n }\n\n return {\n capturedAt,\n bufferState,\n extractionQueue,\n dedupRecent,\n maintenanceLedgerTail,\n qmdProbe,\n daemon,\n ...(faithfulness ? { faithfulness } : {}),\n errors,\n };\n}\n\nfunction readBufferState(\n orchestrator: ConsoleStateOrchestratorLike,\n errors: string[],\n): ConsoleBufferState {\n try {\n const getTurns = orchestrator.buffer?.getTurns;\n if (typeof getTurns !== \"function\") {\n return { turnsCount: 0, byteCount: 0 };\n }\n const turns = getTurns.call(orchestrator.buffer) ?? [];\n let byteCount = 0;\n for (const turn of turns) {\n const content = typeof turn?.content === \"string\" ? turn.content : \"\";\n byteCount += Buffer.byteLength(content, \"utf8\");\n }\n return { turnsCount: turns.length, byteCount };\n } catch (err) {\n errors.push(`bufferState: ${describeError(err)}`);\n return { turnsCount: 0, byteCount: 0 };\n }\n}\n\nfunction readExtractionQueue(\n orchestrator: ConsoleStateOrchestratorLike,\n errors: string[],\n): ConsoleExtractionQueueState {\n try {\n const depth =\n typeof orchestrator.getConsoleExtractionQueueDepth === \"function\"\n ? orchestrator.getConsoleExtractionQueueDepth()\n : 0;\n const verdicts =\n typeof orchestrator.getConsoleExtractionRecentVerdicts === \"function\"\n ? orchestrator.getConsoleExtractionRecentVerdicts()\n : [];\n const recentVerdicts = (verdicts ?? [])\n .slice(-MAX_VERDICT_TAIL)\n .map((v) => ({\n ts: typeof v?.ts === \"string\" ? v.ts : \"\",\n kind: typeof v?.kind === \"string\" ? v.kind : \"unknown\",\n ...(typeof v?.reason === \"string\" ? { reason: v.reason } : {}),\n }));\n return { depth: Number.isFinite(depth) ? depth : 0, recentVerdicts };\n } catch (err) {\n errors.push(`extractionQueue: ${describeError(err)}`);\n return { depth: 0, recentVerdicts: [] };\n }\n}\n\nfunction readDedupRecent(\n orchestrator: ConsoleStateOrchestratorLike,\n errors: string[],\n): ConsoleDedupDecision[] {\n try {\n // Cursor Medium: call directly on `orchestrator` to preserve the\n // `this` binding. Extracting the method into a local and invoking\n // it bare loses `this`, so a non-arrow implementation that\n // references `this` would fail at runtime. Mirrors how\n // `readExtractionQueue` and `readMaintenanceLedgerTail` invoke\n // their orchestrator methods.\n if (typeof orchestrator.getConsoleDedupRecentDecisions !== \"function\") return [];\n const raw = orchestrator.getConsoleDedupRecentDecisions() ?? [];\n return raw.slice(-MAX_DEDUP_TAIL).map((d) => ({\n ts: typeof d?.ts === \"string\" ? d.ts : \"\",\n decision: typeof d?.decision === \"string\" ? d.decision : \"unknown\",\n ...(typeof d?.fingerprint === \"string\"\n ? { fingerprint: d.fingerprint }\n : {}),\n ...(typeof d?.similarity === \"number\" && Number.isFinite(d.similarity)\n ? { similarity: d.similarity }\n : {}),\n }));\n } catch (err) {\n errors.push(`dedupRecent: ${describeError(err)}`);\n return [];\n }\n}\n\n/**\n * Stream a JSONL file line-by-line, parse each row, project it into a\n * console event, and keep only the `topN` most-recent items by `ts`\n * (descending). Memory is bounded at O(2 * topN) regardless of file\n * size — we sort-and-truncate whenever the working buffer grows past\n * `2 * topN`. This satisfies BOTH:\n *\n * 1. \"Don't load the whole file into memory\" — streaming + bounded\n * buffer means a multi-GB ledger never blows up RAM.\n * 2. \"Don't miss recent rows just because the file isn't sorted by\n * recency\" — `rebuilt-observations.jsonl` is written sorted by\n * `(sessionKey, hour)`, NOT by global ts, so a byte-tail-only\n * read silently dropped recent-but-lexicographically-earlier\n * sessions. Streaming top-N visits every row and keeps the\n * globally-most-recent ones.\n *\n * Returns an array sorted oldest-first within the top-N window. Rows\n * with no parseable `ts` are dropped (they would otherwise compare\n * undefined under any sort).\n */\nasync function streamLedgerTopN<T extends { ts: string }>(\n ledgerPath: string,\n topN: number,\n project: (parsed: Record<string, unknown>) => T | null,\n): Promise<T[]> {\n let stream: ReturnType<typeof createReadStream>;\n try {\n // `fs.access`-style probe so we can quietly return `[]` for\n // ENOENT without hitting the readline error path.\n await fs.stat(ledgerPath);\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\") return [];\n throw err;\n }\n try {\n stream = createReadStream(ledgerPath, { encoding: \"utf-8\" });\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\") return [];\n throw err;\n }\n const rl = createInterface({ input: stream, crlfDelay: Infinity });\n const cap = Math.max(1, topN) * 2;\n const buf: T[] = [];\n const truncate = () => {\n // Sort descending by ts, keep top-N, drop the rest.\n buf.sort((a, b) => {\n const aMs = Date.parse(a.ts);\n const bMs = Date.parse(b.ts);\n if (Number.isFinite(aMs) && Number.isFinite(bMs)) return bMs - aMs;\n if (Number.isFinite(aMs)) return -1;\n if (Number.isFinite(bMs)) return 1;\n return 0;\n });\n if (buf.length > topN) buf.length = topN;\n };\n try {\n for await (const line of rl) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n let parsed: unknown;\n try {\n parsed = JSON.parse(trimmed);\n } catch {\n continue;\n }\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n continue;\n }\n const event = project(parsed as Record<string, unknown>);\n if (event === null) continue;\n // Drop rows we couldn't even date-stamp — they can't be sorted\n // meaningfully against the rest of the corpus.\n if (!event.ts || !Number.isFinite(Date.parse(event.ts))) continue;\n buf.push(event);\n if (buf.length >= cap) truncate();\n }\n } finally {\n rl.close();\n stream.close();\n }\n truncate();\n return buf.reverse(); // oldest-first within the top-N window\n}\n\nfunction projectLedgerRow(p: Record<string, unknown>): ConsoleMaintenanceLedgerEvent | null {\n // The two canonical writers produce different row shapes:\n // - extraction-judge-telemetry: `{ts, verdictKind, reason, candidateCategory, ...}`\n // - rebuild-observations / migrate-observations:\n // `{sessionKey, hour, turnCount, userTurns, assistantTurns, rebuiltAt}`\n // Map both into the canonical {ts, category, summary} shape.\n const ts =\n typeof p.ts === \"string\" && p.ts.length > 0\n ? p.ts\n : typeof p.hour === \"string\" && p.hour.length > 0\n ? p.hour\n : typeof p.rebuiltAt === \"string\" && p.rebuiltAt.length > 0\n ? p.rebuiltAt\n : \"\";\n const category =\n typeof p.category === \"string\" && p.category.length > 0\n ? p.category\n : typeof p.verdictKind === \"string\"\n ? \"judge-verdict\"\n : typeof p.turnCount === \"number\"\n ? \"observation\"\n : \"unknown\";\n const summary = summarizeLedgerEvent(p);\n return { ts, category, summary };\n}\n\nasync function readMaintenanceLedgerTail(\n orchestrator: ConsoleStateOrchestratorLike,\n errors: string[],\n): Promise<ConsoleMaintenanceLedgerEvent[]> {\n try {\n const memoryDir = orchestrator.config?.memoryDir;\n if (!memoryDir || typeof memoryDir !== \"string\") return [];\n const ledgerDir = path.join(memoryDir, \"state\", \"observation-ledger\");\n // Codex review converged on two competing constraints:\n // 1. Don't load the whole file into memory (multi-GB ledgers\n // would OOM the console surface).\n // 2. Don't miss recent rows just because the file isn't sorted\n // by recency (`rebuilt-observations.jsonl` is sorted by\n // sessionKey/hour, so byte-tail reads silently drop recent\n // lexicographically-earlier sessions).\n // The right answer is a streaming bounded top-N: visit every row,\n // but keep only the MAX_LEDGER_TAIL most-recent in memory at any\n // time. Memory stays O(MAX_LEDGER_TAIL); recency ordering is\n // globally correct.\n const verdictsTopN = await streamLedgerTopN(\n path.join(ledgerDir, \"extraction-judge-verdicts.jsonl\"),\n MAX_LEDGER_TAIL,\n projectLedgerRow,\n );\n const rebuiltTopN = await streamLedgerTopN(\n path.join(ledgerDir, \"rebuilt-observations.jsonl\"),\n MAX_LEDGER_TAIL,\n projectLedgerRow,\n );\n // Merge the two source top-N windows and select the global\n // top-N. Both inputs already arrive oldest-first within their\n // respective windows; sort the merged set descending by ts and\n // take the most-recent MAX_LEDGER_TAIL.\n const events: ConsoleMaintenanceLedgerEvent[] = [...verdictsTopN, ...rebuiltTopN];\n if (events.length === 0) return [];\n events.sort((a, b) => {\n const aMs = Date.parse(a.ts);\n const bMs = Date.parse(b.ts);\n if (Number.isFinite(aMs) && Number.isFinite(bMs)) return bMs - aMs;\n if (Number.isFinite(aMs)) return -1;\n if (Number.isFinite(bMs)) return 1;\n return 0;\n });\n const tail = events.slice(0, MAX_LEDGER_TAIL);\n // Reverse so the caller sees oldest-first within the tail window.\n return tail.reverse();\n } catch (err) {\n errors.push(`maintenanceLedgerTail: ${describeError(err)}`);\n return [];\n }\n}\n\nfunction summarizeLedgerEvent(p: Record<string, unknown>): string {\n const parts: string[] = [];\n // Judge-verdict rows.\n if (typeof p.verdictKind === \"string\") parts.push(`verdict=${p.verdictKind}`);\n if (typeof p.reason === \"string\" && p.reason.length > 0) {\n const trimmed = p.reason.length > 80 ? `${p.reason.slice(0, 80)}…` : p.reason;\n parts.push(`reason=${trimmed}`);\n }\n if (typeof p.candidateCategory === \"string\") {\n parts.push(`cat=${p.candidateCategory}`);\n }\n // Canonical rebuilt-observations rows (codex P2).\n if (typeof p.sessionKey === \"string\" && p.sessionKey.length > 0) {\n parts.push(`session=${p.sessionKey}`);\n }\n if (typeof p.turnCount === \"number\" && Number.isFinite(p.turnCount)) {\n parts.push(`turns=${p.turnCount}`);\n }\n if (typeof p.userTurns === \"number\" && Number.isFinite(p.userTurns)) {\n parts.push(`u=${p.userTurns}`);\n }\n if (typeof p.assistantTurns === \"number\" && Number.isFinite(p.assistantTurns)) {\n parts.push(`a=${p.assistantTurns}`);\n }\n if (parts.length === 0) {\n return typeof p.category === \"string\" ? p.category : \"event\";\n }\n return parts.join(\" \");\n}\n\nfunction readQmdProbe(\n orchestrator: ConsoleStateOrchestratorLike,\n errors: string[],\n): ConsoleQmdProbeState {\n try {\n const qmd = orchestrator.qmd;\n if (!qmd) {\n return { available: false, daemonMode: false, debug: \"qmd unavailable\" };\n }\n const available =\n typeof qmd.isAvailable === \"function\" ? qmd.isAvailable() : false;\n const daemonMode =\n typeof qmd.isDaemonMode === \"function\" ? qmd.isDaemonMode() : false;\n const debug =\n typeof qmd.debugStatus === \"function\" ? qmd.debugStatus() : \"\";\n return { available, daemonMode, debug };\n } catch (err) {\n errors.push(`qmdProbe: ${describeError(err)}`);\n return { available: false, daemonMode: false, debug: \"\" };\n }\n}\n\nfunction readDaemonInfo(\n orchestrator: ConsoleStateOrchestratorLike,\n errors: string[],\n): ConsoleDaemonInfo {\n try {\n if (typeof orchestrator.getConsoleDaemonInfo === \"function\") {\n const info = orchestrator.getConsoleDaemonInfo();\n return {\n uptimeMs: Number.isFinite(info?.uptimeMs) ? info.uptimeMs : 0,\n version: typeof info?.version === \"string\" ? info.version : \"unknown\",\n };\n }\n // Fallback: process uptime + best-effort package version. This is\n // only meaningful when the orchestrator runs in the same process\n // as the CLI (which is the case for `remnic console --state-only`).\n const uptimeMs = Math.round((process.uptime?.() ?? 0) * 1000);\n return { uptimeMs, version: resolvePackageVersion() };\n } catch (err) {\n errors.push(`daemon: ${describeError(err)}`);\n return { uptimeMs: 0, version: \"unknown\" };\n }\n}\n\nfunction resolvePackageVersion(): string {\n // Best-effort, sync, never throws. We avoid importing package.json\n // statically because tsup bundles into a single file at runtime.\n try {\n const env = process.env?.REMNIC_VERSION;\n if (typeof env === \"string\" && env.length > 0) return env;\n } catch {\n // ignore\n }\n return \"unknown\";\n}\n\nfunction describeError(err: unknown): string {\n if (err instanceof Error) return err.message;\n try {\n return String(err);\n } catch {\n return \"unknown error\";\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/local-llm.ts"],"sourcesContent":["import { log } from \"./logger.js\";\nimport { estimateTokenCount } from \"./token-estimate.js\";\nimport type { PluginConfig } from \"./types.js\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport type { ModelRegistry } from \"./model-registry.js\";\nimport { launchProcessSync } from \"./runtime/child-process.js\";\nimport { mergeEnv, readEnvVar } from \"./runtime/env.js\";\nimport { resolveLocalLlmCapabilities } from \"./capabilities.js\";\nimport { resolvePipelineProcessingCapabilities } from \"./capabilities.js\";\nimport { ChatTransport } from \"./local-llm-transport.js\";\nimport {\n extractNonRecoverableBackendReason,\n extractNonRecoverableBackendReasonFromErrorText,\n isAbortError,\n probeFetch,\n resolveUnavailableVerdict,\n type ProbeFetchResult,\n normalizeBackendTripReason,\n waitForRetryBackoff,\n SingleFlightProbe,\n type LocalLlmChatCompletionOptions,\n type LocalLlmChatCompletionResult,\n} from \"./local-llm-helpers.js\";\nimport {\n isObjectRecord,\n orderedLocalServers,\n type LocalLlmType,\n} from \"./local-llm-servers.js\";\n\nexport type { LocalLlmType } from \"./local-llm-servers.js\";\n\n/** Trim trailing slash characters without backtracking regex. */\nfunction trimTrailingSlashes(s: string): string {\n let end = s.length;\n while (end > 0 && s[end - 1] === \"/\") end--;\n return s.substring(0, end);\n}\n\nfunction stripTrailingV1Path(s: string): string {\n return s.endsWith(\"/v1\") ? s.slice(0, -3) : s;\n}\n\nfunction isLlamaCppPropsResponse(value: unknown): boolean {\n return (\n isObjectRecord(value) &&\n isObjectRecord(value.default_generation_settings) &&\n typeof value.total_slots === \"number\" &&\n (\n typeof value.model_path === \"string\" ||\n typeof value.chat_template === \"string\" ||\n typeof value.build_info === \"string\"\n )\n );\n}\n\nfunction isLlamaCppModelsResponse(value: unknown): boolean {\n if (!isObjectRecord(value) || !Array.isArray(value.data)) {\n return false;\n }\n return value.data.some((entry) => {\n if (!isObjectRecord(entry)) return false;\n if (entry.owned_by === \"llamacpp\") return true;\n if (typeof entry.id === \"string\" && entry.id.endsWith(\".gguf\")) return true;\n const meta = entry.meta;\n return (\n isObjectRecord(meta) &&\n (\"n_ctx_train\" in meta || \"n_params\" in meta || \"vocab_type\" in meta)\n );\n });\n}\n\nfunction isLmStudioApiV1ModelsResponse(value: unknown): boolean {\n if (!isObjectRecord(value) || !Array.isArray(value.models)) {\n return false;\n }\n return value.models.some((entry) => {\n if (!isObjectRecord(entry)) return false;\n return (\n typeof entry.key === \"string\" &&\n typeof entry.display_name === \"string\" &&\n (\n typeof entry.format === \"string\" ||\n typeof entry.max_context_length === \"number\" ||\n Array.isArray(entry.loaded_instances)\n )\n );\n });\n}\n\nfunction isLmStudioApiV0ModelsResponse(value: unknown): boolean {\n if (!isObjectRecord(value) || !Array.isArray(value.data)) {\n return false;\n }\n return value.data.some((entry) => {\n if (!isObjectRecord(entry)) return false;\n return (\n typeof entry.id === \"string\" &&\n typeof entry.publisher === \"string\" &&\n (\n typeof entry.compatibility_type === \"string\" ||\n typeof entry.max_context_length === \"number\" ||\n typeof entry.state === \"string\"\n )\n );\n });\n}\n\nfunction isLmStudioNativeModelsResponse(value: unknown): boolean {\n return isLmStudioApiV1ModelsResponse(value) || isLmStudioApiV0ModelsResponse(value);\n}\n\n/**\n * Backends known to honor `chat_template_kwargs: { enable_thinking: false }`\n * on OpenAI-compatible `/v1/chat/completions`. LM Studio, vLLM, and\n * llama.cpp forward this field to the jinja chat template, where thinking-capable\n * models (Qwen 3.5, Gemma 4, DeepSeek) suppress reasoning tokens.\n *\n * Strict OpenAI-compatible backends (standard OpenAI, Azure OpenAI, some\n * proxies) reject unknown request fields with 400 — which trips the\n * `localLlm400*` cooldown path. `LocalLlmClient` therefore only injects\n * the kwarg when the detected backend is in this set; unknown / `generic`\n * / `ollama` / `mlx` fail open (no injection, no 400 risk). Issue #548.\n */\nconst THINKING_COMPATIBLE_BACKENDS: ReadonlySet<LocalLlmType> = new Set([\n \"lmstudio\",\n \"vllm\",\n \"llamacpp\",\n]);\n\nconst THINKING_SUPPRESSED_OPERATIONS: ReadonlySet<string> = new Set([\n \"extraction\",\n \"extraction-judge\",\n \"extraction-faithfulness\",\n \"contradiction-judge\",\n \"contradiction_verification\",\n \"link_suggestion\",\n \"memory_summarization\",\n \"proactive_extraction\",\n \"lcm-summarize\",\n \"day_summary\",\n \"hourly_summary\",\n \"hourly_summary_extended\",\n]);\n\nexport interface LocalModelInfo {\n id: string;\n contextWindow?: number;\n maxTokens?: number;\n}\n\nexport type LocalLlmRequestPriority = \"recall-critical\" | \"background\";\n\n// LocalLlmChatCompletionOptions and LocalLlmChatCompletionResult live in\n// local-llm-helpers.ts so this file stays under its size ceiling (#1995).\ninterface LocalLlmQueuedRequest {\n messages: Array<{ role: string; content: string }>;\n options: LocalLlmChatCompletionOptions;\n priority: LocalLlmRequestPriority;\n enqueuedAtMs: number;\n resolve: (value: LocalLlmChatCompletionResult | null) => void;\n}\n\nconst LOCAL_LLM_GLOBAL_BACKEND_STATE = \"__openclawEngramLocalLlmBackendState\";\n\ntype LocalLlmBackendState = {\n untilMs: number;\n reason: string;\n};\n/**\n * Local LLM client for OpenAI-compatible endpoints (LM Studio, Ollama, MLX, etc.)\n *\n * Based on openclaw-tactician's provider detection patterns for consistency.\n * Provides privacy-preserving, cost-effective LLM operations with\n * graceful fallback to cloud providers when local LLM is unavailable.\n */\nexport class LocalLlmClient {\n private config: PluginConfig;\n private isAvailable: boolean | null = null;\n private lastHealthCheck: number = 0;\n private readonly availabilityProbe = new SingleFlightProbe();\n private detectedType: LocalLlmType | null = null;\n private cachedModelInfo: LocalModelInfo | null = null;\n private cachedLmsContext: number | null = null;\n private lastLmsCheck: number = 0;\n private consecutive400s: number = 0;\n private cooldownUntilMs: number = 0;\n private modelRegistry?: ModelRegistry;\n private _disableThinking: boolean = false;\n private readonly requestQueues: Record<LocalLlmRequestPriority, LocalLlmQueuedRequest[]> = {\n \"recall-critical\": [],\n background: [],\n };\n private readonly queueProcessing = new Set<LocalLlmRequestPriority>();\n private queueDrainScheduled: boolean = false;\n private static readonly HEALTH_CHECK_INTERVAL_MS = 60000; // 1 minute\n private static readonly LMS_CACHE_INTERVAL_MS = 30000; // 30 seconds\n private readonly chatTransport = new ChatTransport();\n\n constructor(config: PluginConfig, modelRegistry?: ModelRegistry) {\n this.config = config;\n this.modelRegistry = modelRegistry;\n }\n\n /**\n * Request thinking/reasoning suppression on the next chat completion.\n *\n * When `true`, the client will inject\n * `chat_template_kwargs: { enable_thinking: false }` into the request\n * body — **but only when the detected backend is known to support it**\n * (LM Studio, vLLM; see `THINKING_COMPATIBLE_BACKENDS`). Strict\n * OpenAI-compat backends reject unknown fields with 400; on those the\n * client fails open (thinking runs normally). This is the safe\n * default for Remnic extraction-style operations: measurable latency\n * win on thinking-capable backends, zero risk on others. Consolidation\n * operations keep thinking enabled unless explicitly added to\n * `THINKING_SUPPRESSED_OPERATIONS`. Issues #548 and #979.\n */\n set disableThinking(value: boolean) {\n this._disableThinking = value;\n }\n\n private resolveHomeDir(): string {\n return this.config.localLlmHomeDir || readEnvVar(\"HOME\") || os.homedir();\n }\n\n private buildRequestHeaders(base: Record<string, string> = {}): Record<string, string> {\n const headers: Record<string, string> = {\n ...base,\n ...(this.config.localLlmHeaders ?? {}),\n };\n if (this.config.localLlmApiKey && this.config.localLlmAuthHeader !== false) {\n headers.Authorization = `Bearer ${this.config.localLlmApiKey}`;\n }\n return headers;\n }\n\n\n /**\n * Set the ModelRegistry for caching detected capabilities\n */\n setModelRegistry(registry: ModelRegistry): void {\n this.modelRegistry = registry;\n }\n\n /**\n * Get the detected server type (null if not detected)\n */\n getDetectedType(): LocalLlmType | null {\n return this.detectedType;\n }\n\n private getBackendKey(): string {\n return trimTrailingSlashes(\n this.config.localLlmUrl.replace(\"localhost\", \"127.0.0.1\"),\n ).replace(/\\/v1$/, \"\");\n }\n\n private getGlobalBackendState(): Map<string, LocalLlmBackendState> {\n const globalAny = globalThis as typeof globalThis & {\n [LOCAL_LLM_GLOBAL_BACKEND_STATE]?: Map<string, LocalLlmBackendState>;\n };\n if (!globalAny[LOCAL_LLM_GLOBAL_BACKEND_STATE]) {\n globalAny[LOCAL_LLM_GLOBAL_BACKEND_STATE] = new Map();\n }\n return globalAny[LOCAL_LLM_GLOBAL_BACKEND_STATE];\n }\n\n private getTrippedBackendState(now: number): LocalLlmBackendState | null {\n const state = this.getGlobalBackendState().get(this.getBackendKey()) ?? null;\n if (!state) return null;\n if (state.untilMs <= now) {\n this.getGlobalBackendState().delete(this.getBackendKey());\n this.lastHealthCheck = 0;\n return null;\n }\n return state;\n }\n\n private markBackendUnavailable(reason: string, durationMs: number): void {\n const normalizedReason = normalizeBackendTripReason(reason);\n if (durationMs > 0) {\n const untilMs = Date.now() + durationMs;\n this.getGlobalBackendState().set(this.getBackendKey(), { untilMs, reason: normalizedReason });\n } else {\n this.getGlobalBackendState().delete(this.getBackendKey());\n }\n this.isAvailable = false;\n this.lastHealthCheck = 0;\n log.warn(\n `local LLM backend unavailable for ${durationMs}ms: model=${this.config.localLlmModel} reason=${normalizedReason}`,\n );\n }\n\n\n /**\n * Fetch with timeout for health checks. Body lives in the sibling helper so\n * this file stays under its size ceiling (issue #1995).\n */\n private async fetchWithTimeout(\n url: string,\n timeoutMs: number = 2000,\n headers?: Record<string, string>,\n signal?: AbortSignal,\n ): Promise<ProbeFetchResult> {\n return await probeFetch(url, {\n timeoutMs,\n headers: this.buildRequestHeaders({ Accept: \"application/json\", ...(headers ?? {}) }),\n signal,\n });\n }\n\n private async probeLmStudioNativeModels(\n probeBaseUrl: string,\n signal?: AbortSignal,\n ): Promise<{ matched: boolean; unauthorized: boolean }> {\n let unauthorized = false;\n for (const endpoint of [\"/api/v1/models\", \"/api/v0/models\"]) {\n if (signal?.aborted) return { matched: false, unauthorized };\n const probe = await this.fetchWithTimeout(`${probeBaseUrl}${endpoint}`, 2000, undefined, signal);\n if (signal?.aborted) return { matched: false, unauthorized };\n if (probe.ok && isLmStudioNativeModelsResponse(probe.data)) {\n return { matched: true, unauthorized };\n }\n if (probe.status === 401 || probe.status === 403) {\n unauthorized = true;\n }\n }\n return { matched: false, unauthorized };\n }\n\n /**\n * Check if local LLM is available\n * Uses 127.0.0.1 instead of localhost to avoid DNS issues (consistent with tactician)\n */\n async checkAvailability(signal?: AbortSignal): Promise<boolean> {\n if (signal?.aborted) return false;\n // Cache health check results for 1 minute\n const now = Date.now();\n const trippedState = this.getTrippedBackendState(now);\n if (trippedState) {\n this.isAvailable = false;\n this.lastHealthCheck = 0;\n log.info(\n `local LLM availability: backend circuit open for ${Math.max(0, trippedState.untilMs - now)}ms (${trippedState.reason})`,\n );\n return false;\n }\n if (this.isAvailable !== null && now - this.lastHealthCheck < LocalLlmClient.HEALTH_CHECK_INTERVAL_MS) {\n return this.isAvailable;\n }\n return await this.availabilityProbe.run((probeSignal) => this.probeAvailability(probeSignal), signal);\n }\n\n private async probeAvailability(signal?: AbortSignal): Promise<boolean> {\n const now = Date.now();\n // Probe server-native endpoints from the server root even when users configure\n // the OpenAI-compatible `/v1` base URL for chat completions.\n const configuredBaseUrl = trimTrailingSlashes(\n this.config.localLlmUrl.replace(\"localhost\", \"127.0.0.1\"),\n );\n const probeBaseUrl = stripTrailingV1Path(configuredBaseUrl);\n let sawUnauthorizedProbe = false;\n // A probe that TIMED OUT says the event loop was busy, not that the backend\n // is down (issue #2210). Tracked separately so a loaded daemon cannot cache\n // itself into a blackout.\n let sawAbortedProbe = false;\n // Several server shapes share a probe URL (LiteLLM and Ollama both use\n // `/`); fetch each URL once per pass.\n const probed = new Map<string, ProbeFetchResult>();\n const probeOnce = async (url: string): Promise<ProbeFetchResult> => {\n const cached = probed.get(url);\n if (cached) return cached;\n const result = await this.fetchWithTimeout(url, 2000, undefined, signal);\n probed.set(url, result);\n return result;\n };\n\n // Try to detect which server type is running\n if (signal?.aborted) return false;\n for (const serverConfig of orderedLocalServers(configuredBaseUrl)) {\n const healthUrl = `${probeBaseUrl}${serverConfig.healthEndpoint}`;\n log.debug(`checking ${serverConfig.type} at ${healthUrl}`);\n\n const result = await probeOnce(healthUrl);\n if (result.aborted) sawAbortedProbe = true;\n if (signal?.aborted) return false;\n if (result.ok && serverConfig.detectFn(result.data)) {\n if (serverConfig.type === \"mlx\") {\n const lmStudioProbe = await this.probeLmStudioNativeModels(probeBaseUrl, signal);\n if (signal?.aborted) return false;\n if (lmStudioProbe.unauthorized) {\n sawUnauthorizedProbe = true;\n }\n if (lmStudioProbe.matched) {\n this.isAvailable = true;\n this.detectedType = \"lmstudio\";\n this.lastHealthCheck = now;\n log.info(`detected lmstudio at ${configuredBaseUrl}`);\n return true;\n }\n }\n if (serverConfig.type === \"llamacpp\") {\n let sawLlamaCppSignal = false;\n const propsProbe = await this.fetchWithTimeout(`${probeBaseUrl}/props`, 2000, undefined, signal);\n if (propsProbe.aborted) sawAbortedProbe = true;\n if (signal?.aborted) return false;\n if (propsProbe.ok && isLlamaCppPropsResponse(propsProbe.data)) {\n sawLlamaCppSignal = true;\n }\n if (propsProbe.status === 401 || propsProbe.status === 403) {\n sawUnauthorizedProbe = true;\n }\n\n const modelsUrl = `${probeBaseUrl}${serverConfig.modelsEndpoint}`;\n const modelsProbe = await this.fetchWithTimeout(modelsUrl, 2000, undefined, signal);\n if (modelsProbe.aborted) sawAbortedProbe = true;\n if (signal?.aborted) return false;\n if (modelsProbe.ok && isLlamaCppModelsResponse(modelsProbe.data)) {\n sawLlamaCppSignal = true;\n }\n if (modelsProbe.status === 401 || modelsProbe.status === 403) {\n sawUnauthorizedProbe = true;\n continue;\n }\n\n const authConfigured =\n Boolean(this.config.localLlmApiKey) &&\n this.config.localLlmAuthHeader !== false;\n if (!sawLlamaCppSignal || (authConfigured && !modelsProbe.ok)) {\n continue;\n }\n }\n this.isAvailable = true;\n this.detectedType = serverConfig.type;\n this.lastHealthCheck = now;\n log.info(`detected ${serverConfig.type} at ${configuredBaseUrl}`);\n return true;\n }\n if (result.status === 401 || result.status === 403) {\n sawUnauthorizedProbe = true;\n }\n }\n if (signal?.aborted) return false;\n\n // Generic check if specific detection failed\n try {\n const modelsUrl = `${probeBaseUrl}/v1/models`;\n const result = await this.fetchWithTimeout(modelsUrl, 2000, undefined, signal);\n if (result.aborted) sawAbortedProbe = true;\n if (signal?.aborted) return false;\n if (result.ok) {\n this.isAvailable = true;\n this.detectedType = \"generic\";\n this.lastHealthCheck = now;\n log.info(`detected generic OpenAI-compatible server at ${configuredBaseUrl}`);\n return true;\n }\n if (result.status === 401 || result.status === 403) {\n sawUnauthorizedProbe = true;\n }\n } catch {\n // Fall through to unavailable\n }\n\n const wasAvailable = this.isAvailable;\n this.detectedType = null;\n if (sawUnauthorizedProbe) {\n log.warn(\n `local LLM availability probe was unauthorized at ${configuredBaseUrl}; verify localLlmApiKey and localLlmAuthHeader settings`,\n );\n }\n const verdict = resolveUnavailableVerdict({\n baseUrl: configuredBaseUrl,\n sawAbortedProbe,\n wasAvailable,\n });\n this.isAvailable = verdict.cacheVerdict ? false : null;\n this.lastHealthCheck = verdict.cacheVerdict ? now : 0;\n if (verdict.warning) log.warn(verdict.warning);\n log.debug(\"local LLM not available at\", configuredBaseUrl);\n return false;\n }\n\n /**\n * Try to get context window from LM Studio settings.json as fallback.\n * This reads the defaultContextLength setting which is what LM Studio uses\n * when loading models without explicit context configuration.\n */\n private getContextFromLmStudioSettings(): number | null {\n try {\n const homeDir = this.resolveHomeDir();\n const settingsPath = `${homeDir}/.cache/lm-studio/settings.json`;\n\n if (!fs.existsSync(settingsPath)) {\n log.debug(`LM Studio settings: file not found at ${settingsPath}`);\n return null;\n }\n\n const content = fs.readFileSync(settingsPath, \"utf-8\");\n const settings = JSON.parse(content) as {\n defaultContextLength?: {\n type?: string;\n value?: number;\n };\n };\n\n if (settings.defaultContextLength?.value) {\n const contextWindow = settings.defaultContextLength.value;\n log.debug(`LM Studio settings: found default context length: ${contextWindow}`);\n return contextWindow;\n }\n\n return null;\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n log.debug(`LM Studio settings: failed to read - ${errorMsg}`);\n return null;\n }\n }\n\n /**\n * Try to get context window from LMS CLI (LM Studio specific).\n * Uses --json flag for reliable parsing.\n * Returns null if LMS CLI is not available or model not found.\n */\n private getContextFromLmsCli(modelId: string): number | null {\n try {\n // Check if lms CLI exists in common locations.\n // HOME may be absent in launchd environments, so prefer the resolved helper.\n const homeDir = this.resolveHomeDir();\n const lmsPaths = [\n this.config.localLmsCliPath || \"\",\n `${homeDir}/.cache/lm-studio/bin/lms`,\n \"/usr/local/bin/lms\",\n \"/opt/homebrew/bin/lms\",\n ];\n\n const lmsPath = lmsPaths.find((p) => p.length > 0 && fs.existsSync(p));\n if (!lmsPath) {\n log.debug(`LMS CLI: not found in standard locations (checked: ${lmsPaths.join(\", \")})`);\n return null;\n }\n\n // Run lms ps --json to get loaded models with context\n // Use spawnSync with shell and explicit PATH to ensure lms can find its dependencies\n log.debug(`LMS CLI: running: ${lmsPath} ps --json`);\n const existingPath = readEnvVar(\"PATH\") || \"\";\n const result = launchProcessSync(lmsPath, [\"ps\", \"--json\"], {\n encoding: \"utf-8\",\n timeout: 5000,\n shell: false, // Don't use shell for JSON output - more reliable\n env: mergeEnv({\n PATH: `${this.config.localLmsBinDir || `${homeDir}/.cache/lm-studio/bin`}:/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:${existingPath}`,\n HOME: homeDir,\n }),\n });\n\n if (result.error) {\n log.debug(`LMS CLI: spawn error - ${result.error.message}`);\n return null;\n }\n\n if (result.stderr && result.stderr.trim()) {\n log.debug(`LMS CLI: stderr - ${result.stderr.slice(0, 200)}`);\n }\n\n const output = result.stdout || \"\";\n if (!output.trim()) {\n log.debug(\"LMS CLI: empty output - LM Studio may not be running or no models loaded\");\n return null;\n }\n\n // Parse JSON output\n let models: Array<{\n identifier?: string;\n modelKey?: string;\n contextLength?: number;\n maxContextLength?: number;\n }>;\n\n try {\n models = JSON.parse(output) as typeof models;\n } catch (parseErr) {\n log.debug(`LMS CLI: JSON parse error - ${parseErr}`);\n return null;\n }\n\n if (!Array.isArray(models) || models.length === 0) {\n log.debug(\"LMS CLI: no models loaded\");\n return null;\n }\n\n // Find the model matching our configured model ID\n const model = models.find((m) =>\n m.identifier === modelId ||\n m.modelKey === modelId ||\n (m.identifier?.includes(modelId.replace(/@\\d+bit$/, \"\")))\n );\n\n if (!model) {\n log.debug(`LMS CLI: model \"${modelId}\" not found in loaded models: ${models.map(m => m.identifier).join(\", \")}`);\n return null;\n }\n\n // Use contextLength (actual configured) or fall back to maxContextLength (model max)\n const contextWindow = model.contextLength || model.maxContextLength;\n\n if (contextWindow) {\n log.info(`LMS CLI detected context window: ${contextWindow} for ${modelId} (max: ${model.maxContextLength})`);\n return contextWindow;\n }\n\n return null;\n } catch (err) {\n // LMS CLI not available or failed\n const errorMsg = err instanceof Error ? err.message : String(err);\n log.debug(`LMS CLI: failed - ${errorMsg}`);\n return null;\n }\n }\n\n /**\n * Get full model info from LMS CLI including context length and max context length.\n * Returns null if LMS CLI is unavailable or model not found.\n */\n private getLmsModelInfo(modelId: string): { contextLength: number; maxContextLength: number; identifier: string } | null {\n try {\n const result = launchProcessSync(\"lms\", [\"ps\", \"--json\"], {\n encoding: \"utf-8\",\n timeout: 5000,\n shell: false,\n });\n\n if (result.error) {\n return null;\n }\n\n const output = result.stdout || \"\";\n if (!output.trim()) {\n return null;\n }\n\n let models: Array<{\n identifier?: string;\n modelKey?: string;\n contextLength?: number;\n maxContextLength?: number;\n }>;\n\n try {\n models = JSON.parse(output) as typeof models;\n } catch {\n return null;\n }\n\n if (!Array.isArray(models) || models.length === 0) {\n return null;\n }\n\n const model = models.find((m) =>\n m.identifier === modelId ||\n m.modelKey === modelId ||\n (m.identifier?.includes(modelId.replace(/@\\d+bit$/, \"\")))\n );\n\n if (!model || !model.contextLength) {\n return null;\n }\n\n return {\n contextLength: model.contextLength,\n maxContextLength: model.maxContextLength || model.contextLength,\n identifier: model.identifier || modelId,\n };\n } catch {\n return null;\n }\n }\n\n /**\n * Get context window for the configured model, using cache if available.\n * This method caches the result to avoid repeated LMS CLI calls.\n * Order: ModelRegistry (persistent) -> memory cache -> LMS CLI -> settings.json\n */\n getCachedContextWindow(modelId: string): number | null {\n const now = Date.now();\n\n // 1. Check ModelRegistry for persisted context window\n if (this.modelRegistry) {\n const caps = this.modelRegistry.getCapabilities(modelId);\n if (caps.source === \"lmstudio\" && caps.contextWindow) {\n log.debug(`ModelRegistry: using persisted LM Studio context: ${caps.contextWindow}`);\n // Also update memory cache\n this.cachedLmsContext = caps.contextWindow;\n this.lastLmsCheck = now;\n return caps.contextWindow;\n }\n }\n\n // 2. Return in-memory cached value if still valid\n if (this.cachedLmsContext && now - this.lastLmsCheck < LocalLlmClient.LMS_CACHE_INTERVAL_MS) {\n log.debug(`LMS CLI: returning in-memory cached context: ${this.cachedLmsContext}`);\n return this.cachedLmsContext;\n }\n\n // 3. Try LMS CLI (authoritative source)\n const lmsInfo = this.getLmsModelInfo(modelId);\n if (lmsInfo?.contextLength) {\n this.cachedLmsContext = lmsInfo.contextLength;\n this.lastLmsCheck = now;\n // Calculate appropriate output tokens based on context size\n // Use 12.5% of context window, capped at 16K (generous but safe)\n const calculatedOutputTokens = Math.min(Math.floor(lmsInfo.contextLength / 8), 16384);\n const outputTokens = Math.max(calculatedOutputTokens, 4096); // Minimum 4K\n // Persist to ModelRegistry with detected capabilities\n if (this.modelRegistry) {\n this.modelRegistry.setCapabilities(modelId, {\n maxPositionEmbeddings: lmsInfo.maxContextLength || lmsInfo.contextLength,\n contextWindow: lmsInfo.contextLength,\n supportsExtendedContext: (lmsInfo.maxContextLength || lmsInfo.contextLength) > 65536,\n typicalOutputTokens: outputTokens,\n source: \"lmstudio\",\n });\n log.info(`LMS CLI: Stored capabilities for ${modelId}: ${lmsInfo.contextLength} context, ${outputTokens} output tokens`);\n }\n return lmsInfo.contextLength;\n }\n\n // Legacy: Try LMS CLI context only (fallback)\n const legacyContext = this.getContextFromLmsCli(modelId);\n if (legacyContext) {\n this.cachedLmsContext = legacyContext;\n this.lastLmsCheck = now;\n // Persist to ModelRegistry with calculated output tokens\n if (this.modelRegistry) {\n const calculatedOutputTokens = Math.min(Math.floor(legacyContext / 8), 16384);\n const outputTokens = Math.max(calculatedOutputTokens, 4096);\n this.modelRegistry.setCapabilities(modelId, {\n maxPositionEmbeddings: legacyContext,\n contextWindow: legacyContext,\n supportsExtendedContext: false,\n typicalOutputTokens: outputTokens,\n source: \"lmstudio\",\n });\n }\n return legacyContext;\n }\n\n // 4. Fall back to LM Studio settings.json\n const settingsContext = this.getContextFromLmStudioSettings();\n if (settingsContext) {\n log.info(`LM Studio settings: using default context: ${settingsContext}`);\n this.cachedLmsContext = settingsContext;\n this.lastLmsCheck = now;\n return settingsContext;\n }\n\n return null;\n }\n\n /**\n * Clear the LMS context cache. Call this when the model changes.\n */\n clearContextCache(): void {\n this.cachedLmsContext = null;\n this.lastLmsCheck = 0;\n log.debug(\"LMS CLI: context cache cleared\");\n }\n\n private remainingCooldownMs(now: number = Date.now()): number {\n return Math.max(0, this.cooldownUntilMs - now);\n }\n\n private scheduleQueueDrain(): void {\n if (this.queueDrainScheduled) return;\n this.queueDrainScheduled = true;\n\n queueMicrotask(() => {\n this.queueDrainScheduled = false;\n this.startAvailableQueuedRequests();\n });\n }\n\n /** True when a background-priority request occupies or awaits this client's local lane (issue #2011). */\n isBackgroundLaneContended(): boolean {\n return this.queueProcessing.has(\"background\") || this.requestQueues.background.length > 0;\n }\n\n private dequeueQueuedRequest(priority: LocalLlmRequestPriority): LocalLlmQueuedRequest | null {\n const next = this.requestQueues[priority].shift();\n return next ?? null;\n }\n\n private failOpenQueuedRequestsForCooldown(): number {\n let dropped = 0;\n for (const priority of [\"recall-critical\", \"background\"] as const) {\n while (this.requestQueues[priority].length > 0) {\n const queued = this.requestQueues[priority].shift();\n queued?.resolve(null);\n dropped += 1;\n }\n }\n return dropped;\n }\n\n private startAvailableQueuedRequests(): void {\n if (!this.queueProcessing.has(\"recall-critical\")) {\n const nextCritical = this.dequeueQueuedRequest(\"recall-critical\");\n if (nextCritical) {\n this.queueProcessing.add(\"recall-critical\");\n void this.runQueuedRequest(nextCritical);\n }\n }\n\n if (!this.queueProcessing.has(\"background\")) {\n const nextBackground = this.dequeueQueuedRequest(\"background\");\n if (nextBackground) {\n this.queueProcessing.add(\"background\");\n void this.runQueuedRequest(nextBackground);\n }\n }\n }\n\n private async runQueuedRequest(next: LocalLlmQueuedRequest): Promise<void> {\n try {\n if (next.options.signal?.aborted) {\n next.resolve(null);\n return;\n }\n const remainingCooldownMs = this.remainingCooldownMs();\n if (remainingCooldownMs > 0) {\n const additionalDropped = this.failOpenQueuedRequestsForCooldown();\n log.warn(\n `local LLM: cooldown active (${remainingCooldownMs}ms remaining), dropping ${additionalDropped + 1} queued request(s) fail-open`,\n );\n next.resolve(null);\n return;\n }\n\n let result: LocalLlmChatCompletionResult | null = null;\n try {\n result = await this.runChatCompletionRequest(next.messages, next.options, {\n priority: next.priority,\n enqueuedAtMs: next.enqueuedAtMs,\n });\n } catch (err) {\n log.warn(`local LLM queue drain failed open: ${next.options.redactProviderErrors ? \"provider error details redacted\" : err instanceof Error ? err.message : String(err)}`);\n }\n next.resolve(result);\n } finally {\n this.queueProcessing.delete(next.priority);\n if (this.requestQueues[\"recall-critical\"].length > 0 || this.requestQueues.background.length > 0) {\n this.scheduleQueueDrain();\n }\n }\n }\n\n private async runChatCompletionRequest(\n messages: Array<{ role: string; content: string }>,\n options: LocalLlmChatCompletionOptions,\n queueMeta?: { priority: LocalLlmRequestPriority; enqueuedAtMs: number },\n ): Promise<LocalLlmChatCompletionResult | null> {\n log.debug(\n `local LLM chatCompletion: localLlmEnabled=${resolveLocalLlmCapabilities(this.config).localLlm}, model=${this.config.localLlmModel}`,\n );\n\n const operation = options.operation ?? \"unspecified\";\n const startedAtMs = Date.now();\n if (queueMeta) {\n log.debug(\n `local LLM queue start: priority=${queueMeta.priority} waitMs=${startedAtMs - queueMeta.enqueuedAtMs} op=${operation}`,\n );\n }\n\n try {\n if (options.signal?.aborted) return null;\n const isAvailable = await this.checkAvailability(options.signal);\n if (!isAvailable) {\n log.debug(\n `local LLM: checkAvailability returned false for ${this.config.localLlmUrl}`,\n );\n return null;\n }\n\n const promptChars = messages.reduce((sum, m) => sum + (m.content?.length ?? 0), 0);\n const requestBody: Record<string, unknown> = {\n model: options.model ?? this.config.localLlmModel,\n messages,\n temperature: options.temperature ?? 0.7,\n // Use max_tokens consistent with cloud models\n max_tokens: options.maxTokens ?? 4096,\n stream: false,\n };\n\n // Skip response_format for local LLMs - they don't support json_object type\n // The prompts already instruct the model to output JSON\n // Only send if it's json_schema type which some local LLMs support\n if (options.responseFormat?.type === \"json_schema\") {\n requestBody.response_format = options.responseFormat;\n }\n\n // Suppress thinking/reasoning for operations that benefit from terse,\n // structured output. Thinking-capable models (Qwen 3.5, Gemma 4,\n // DeepSeek) default to thinking-on via their chat template; sending\n // `chat_template_kwargs: { enable_thinking: false }` tells the template\n // to skip reasoning tokens. Consolidation operations on the main client\n // intentionally keep thinking enabled so entity resolution and\n // deduplication can use the model's reasoning path. Fast-tier callers can\n // still force suppression per request to preserve their low-latency\n // contract.\n //\n // Gate the injection on detected backend support (issue #548,\n // Codex P1 on PR #550): `chat_template_kwargs` is an LM Studio /\n // vLLM / llama.cpp extension, not part of standard OpenAI chat\n // completions. Strict OpenAI-compatible backends reject\n // unknown fields with 400, which trips the 400-cooldown path and\n // can effectively disable local extraction. Fail open when the\n // backend hasn't been positively identified as thinking-capable.\n const shouldSuppressThinking =\n options.forceDisableThinking === true ||\n ((options.disableThinking ?? this._disableThinking) &&\n THINKING_SUPPRESSED_OPERATIONS.has(operation));\n if (\n shouldSuppressThinking &&\n this.detectedType !== null &&\n THINKING_COMPATIBLE_BACKENDS.has(this.detectedType)\n ) {\n requestBody.chat_template_kwargs = { enable_thinking: false };\n } else if (shouldSuppressThinking && this.detectedType === \"ollama\") {\n // Ollama's /v1 endpoint silently drops `chat_template_kwargs` AND\n // `enable_thinking` — the ONLY field it maps to think-off is\n // `reasoning_effort` (\"none\" → ThinkValue{false} in openai.go;\n // verified against Ollama 0.32.0 source and live probes,\n // issue #1996). Without this, thinking-capable models (Gemma 4,\n // Qwen 3.5+) burn 1,700-2,000 reasoning tokens per extraction on\n // Ollama, exceed the client timeout, and retry forever. The effort\n // is configurable via `localLlmReasoningEffort` (default \"none\");\n // an empty value disables injection (pre-#1996 behavior).\n const effort = this.config.localLlmReasoningEffort;\n if (effort) {\n requestBody.reasoning_effort = effort;\n }\n }\n\n // Normalize URL (use 127.0.0.1 instead of localhost)\n const baseUrl = trimTrailingSlashes(\n this.config.localLlmUrl.replace(\"localhost\", \"127.0.0.1\"),\n );\n const chatUrl = baseUrl.endsWith(\"/v1\")\n ? `${baseUrl}/chat/completions`\n : `${baseUrl}/v1/chat/completions`;\n\n const requestBodyJson = JSON.stringify(requestBody);\n log.debug(\n `local LLM: sending request to ${chatUrl} with model ${this.config.localLlmModel}`,\n );\n // Avoid logging request bodies by default (can contain sensitive user content).\n log.debug(`local LLM: request body length=${requestBodyJson.length}`);\n\n const effectiveTimeoutMs =\n typeof options.timeoutMs === \"number\"\n ? Math.min(this.config.localLlmTimeoutMs, options.timeoutMs)\n : this.config.localLlmTimeoutMs;\n const maxAttempts = 1 + Math.max(0, this.config.localLlmRetry5xxCount);\n let response: Response | null = null, responseBody = \"\";\n let lastAbortError: Error | null = null;\n for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {\n response = null;\n responseBody = \"\";\n const attemptAbort = new AbortController();\n const onCallerAbort = (): void => {\n attemptAbort.abort(options.signal?.reason);\n };\n if (options.signal) {\n if (options.signal.aborted) {\n onCallerAbort();\n } else {\n options.signal.addEventListener(\"abort\", onCallerAbort, { once: true });\n }\n }\n const attemptTimeout = setTimeout(() => attemptAbort.abort(), effectiveTimeoutMs);\n try {\n response = await this.chatTransport.post({\n url: chatUrl,\n headers: this.buildRequestHeaders({\n \"Content-Type\": \"application/json\",\n }),\n body: requestBodyJson,\n signal: attemptAbort.signal,\n budgetMs: this.config.localLlmTimeoutMs,\n });\n responseBody = await response.text();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n if (options.signal?.aborted) {\n response = null;\n lastAbortError = error;\n break;\n }\n if (response && !response.ok) {\n log.debug(`local LLM failed to read ${response.status} response body: ${options.redactProviderErrors ? \"details redacted\" : error.message}`);\n } else {\n response = null;\n if (!isAbortError(err)) throw err;\n lastAbortError = error;\n if (attempt >= maxAttempts) break;\n const backoffMs = this.config.localLlmRetryBackoffMs * attempt;\n log.warn(\n `local LLM request aborted: op=${operation} attempt=${attempt}/${maxAttempts} timeoutMs=${effectiveTimeoutMs} model=${this.config.localLlmModel}; retrying after ${backoffMs}ms`,\n );\n if (!(await waitForRetryBackoff(backoffMs, options.signal))) return null;\n continue;\n }\n } finally {\n clearTimeout(attemptTimeout);\n options.signal?.removeEventListener(\"abort\", onCallerAbort);\n }\n\n if (response.ok) break;\n if (response.status >= 500 && attempt < maxAttempts) {\n const nonRecoverableReason =\n extractNonRecoverableBackendReasonFromErrorText(responseBody);\n if (nonRecoverableReason) {\n this.markBackendUnavailable(\n nonRecoverableReason,\n this.config.localLlm400CooldownMs,\n );\n this.consecutive400s = 0;\n return null;\n }\n }\n if (response.status < 500 || attempt >= maxAttempts) break;\n\n const backoffMs = this.config.localLlmRetryBackoffMs * attempt;\n log.warn(\n `local LLM request got ${response.status}; retrying (attempt ${attempt + 1}/${maxAttempts}) after ${backoffMs}ms`,\n );\n if (!(await waitForRetryBackoff(backoffMs, options.signal))) return null;\n }\n log.debug(\n `local LLM: received response, status=${response?.status}, ok=${response?.ok}`,\n );\n if (!response) {\n if (lastAbortError) {\n log.warn(\n `local LLM request aborted after ${maxAttempts} attempt(s): op=${operation} timeoutMs=${effectiveTimeoutMs} model=${this.config.localLlmModel} promptChars=${promptChars} durationMs=${Date.now() - startedAtMs}`,\n );\n } else {\n log.warn(\n `local LLM request failed: no response object (op=${operation} model=${this.config.localLlmModel} durationMs=${Date.now() - startedAtMs})`,\n );\n }\n return null;\n }\n\n if (!response.ok) {\n let reason = \"\";\n try {\n const parsed = JSON.parse(responseBody) as { error?: { message?: string } };\n reason = parsed?.error?.message ? ` — ${parsed.error.message}` : \"\";\n } catch {\n if (!options.redactProviderErrors) log.debug(`local LLM error body: ${responseBody.slice(0, 500)}`);\n }\n log.warn(\n `local LLM request failed: ${response.status}${options.redactProviderErrors ? \"\" : ` ${response.statusText}${reason}`} ` +\n `(op=${operation}, model=${this.config.localLlmModel}, url=${chatUrl}, promptChars=${promptChars}, maxTokens=${requestBody.max_tokens as number})`,\n );\n const nonRecoverableReason =\n extractNonRecoverableBackendReason(reason) ??\n extractNonRecoverableBackendReasonFromErrorText(responseBody);\n if (nonRecoverableReason) {\n this.markBackendUnavailable(\n nonRecoverableReason,\n this.config.localLlm400CooldownMs,\n );\n this.consecutive400s = 0;\n return null;\n }\n if (response.status === 400) {\n this.consecutive400s += 1;\n if (this.consecutive400s >= this.config.localLlm400TripThreshold) {\n this.cooldownUntilMs = Date.now() + this.config.localLlm400CooldownMs;\n log.warn(\n `local LLM: entering cooldown for ${this.config.localLlm400CooldownMs}ms ` +\n `after ${this.consecutive400s} consecutive 400 responses`,\n );\n this.consecutive400s = 0;\n }\n } else {\n this.consecutive400s = 0;\n }\n if (options.failureDiag)\n options.failureDiag.lastError = Object.assign(new Error(\"local LLM http failure\"), {\n status: response.status,\n });\n return null;\n }\n this.consecutive400s = 0;\n\n const data = JSON.parse(responseBody) as {\n choices?: Array<{\n message?: { content?: string; reasoning_content?: string };\n }>;\n usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n };\n };\n\n log.debug(\n `local LLM response: choices=${data.choices?.length}, usage=${JSON.stringify(data.usage)}`,\n );\n\n // Thinking models (e.g. Qwen 3.5) may put their response in\n // `reasoning_content` and leave `content` empty. Fall back to\n // reasoning_content so engram still gets a usable result.\n const msg = data.choices?.[0]?.message;\n const content = msg?.content || msg?.reasoning_content || \"\";\n if (!content) {\n log.warn(`local LLM returned empty content.${options.redactProviderErrors ? \"\" : ` choices=${JSON.stringify(data.choices)?.slice(0, 200)}`}`);\n return null;\n }\n\n // Estimate tokens if not provided by local LLM\n const usage = data.usage\n ? {\n promptTokens: data.usage.prompt_tokens ?? 0,\n completionTokens: data.usage.completion_tokens ?? 0,\n totalTokens: data.usage.total_tokens ?? 0,\n }\n : this.estimateTokens(messages, content);\n\n const durationMs = Date.now() - startedAtMs;\n if (resolvePipelineProcessingCapabilities(this.config).slowLog && durationMs >= this.config.slowLogThresholdMs) {\n const promptChars = messages.reduce((sum, m) => sum + (m.content?.length ?? 0), 0);\n const op = options.operation ? ` op=${options.operation}` : \"\";\n log.warn(\n `SLOW local LLM:${op} durationMs=${durationMs} model=${this.config.localLlmModel} url=${chatUrl} promptChars=${promptChars} outputTokens=${usage.completionTokens} totalTokens=${usage.totalTokens}`,\n );\n }\n\n log.debug(\"local LLM: request succeeded, tokens:\", usage.totalTokens);\n return { content, usage };\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err);\n const durationMs = Date.now() - startedAtMs;\n if (isAbortError(err)) {\n log.warn(\n `local LLM request aborted: op=${operation} timeoutMs=${options.timeoutMs ?? this.config.localLlmTimeoutMs} model=${this.config.localLlmModel} durationMs=${durationMs} error=${options.redactProviderErrors ? \"provider error details redacted\" : errMsg}`,\n );\n return null;\n }\n log.warn(`local LLM request error: op=${operation} error=${options.redactProviderErrors ? \"provider error details redacted\" : errMsg}`);\n this.isAvailable = false; // Mark as unavailable on non-abort errors\n const nonRecoverableReason = extractNonRecoverableBackendReason(errMsg);\n if (nonRecoverableReason) {\n this.markBackendUnavailable(\n nonRecoverableReason,\n this.config.localLlm400CooldownMs,\n );\n }\n return null;\n } finally {\n if (queueMeta) {\n const finishedAtMs = Date.now();\n const waitMs = startedAtMs - queueMeta.enqueuedAtMs;\n log.debug(\n `local LLM queue finish: priority=${queueMeta.priority} waitMs=${waitMs} runMs=${finishedAtMs - startedAtMs} totalMs=${finishedAtMs - queueMeta.enqueuedAtMs} op=${operation}`,\n );\n }\n }\n }\n\n /**\n * Query the local LLM server for loaded model information.\n * Returns null if unavailable or if the model is not found.\n */\n async getLoadedModelInfo(): Promise<LocalModelInfo | null> {\n const baseUrl = trimTrailingSlashes(\n this.config.localLlmUrl.replace(\"localhost\", \"127.0.0.1\"),\n );\n\n // Handle URL construction - localLlmUrl may already include /v1\n const modelsUrl = baseUrl.endsWith(\"/v1\")\n ? `${baseUrl}/models`\n : `${baseUrl}/v1/models`;\n log.debug(`Fetching model info from ${modelsUrl}`);\n\n try {\n const result = await this.fetchWithTimeout(modelsUrl, 3000);\n if (!result.ok) {\n if (result.status === 401 || result.status === 403) {\n log.warn(\n `Local LLM: unauthorized while fetching models from ${modelsUrl}; verify localLlmApiKey and localLlmAuthHeader settings`,\n );\n }\n log.warn(`Local LLM: Failed to fetch models from ${modelsUrl} - server returned error`);\n return null;\n }\n if (!result.data) {\n log.warn(`Local LLM: No data returned from ${modelsUrl}`);\n return null;\n }\n\n const data = result.data as {\n data?: Array<{\n id?: string;\n object?: string;\n owned_by?: string;\n // LM Studio specific fields\n max_context_length?: number;\n max_tokens?: number;\n // Ollama specific\n name?: string;\n details?: {\n parameter_size?: string;\n family?: string;\n };\n }>;\n };\n\n if (!Array.isArray(data.data) || data.data.length === 0) {\n log.warn(\"Local LLM returned no models\");\n return null;\n }\n\n // Verbose model listings are noisy on every gateway restart. Keep it debug-only.\n const modelIds = data.data.map((m) => m.id).filter(Boolean);\n log.debug(\n `Local LLM: Found ${modelIds.length} model(s). First 10: ${modelIds.slice(0, 10).join(\", \")}`,\n );\n\n // Find the model matching our configured model ID\n const configuredModel = this.config.localLlmModel;\n let model = data.data.find((m) => m.id === configuredModel);\n\n // If not found by exact match, try partial match (handle suffixes like @4bit)\n if (!model) {\n model = data.data.find((m) =>\n configuredModel.includes(m.id || \"\") ||\n (m.id || \"\").includes(configuredModel.replace(/@\\d+bit$/, \"\"))\n );\n }\n\n // If still not found, use the first loaded model and warn\n if (!model) {\n model = data.data[0];\n const availablePreview = data.data\n .map((m) => m.id)\n .filter(Boolean)\n .slice(0, 10)\n .join(\", \");\n log.warn(\n `Configured model \"${configuredModel}\" not found in local LLM. ` +\n `Using \"${model.id}\" instead. Available (first 10): ${availablePreview}`\n );\n }\n\n // Extract context window - try multiple field names\n let contextWindow = model.max_context_length || model.max_tokens;\n\n // If API doesn't report context window, try LMS CLI (LM Studio specific)\n if (!contextWindow) {\n log.info(\"Local LLM: API did not report context window, trying LMS CLI...\");\n const lmsContext = this.getCachedContextWindow(model.id || \"\");\n if (lmsContext) {\n contextWindow = lmsContext;\n }\n }\n\n this.cachedModelInfo = {\n id: model.id || \"unknown\",\n contextWindow: contextWindow,\n maxTokens: model.max_tokens,\n };\n\n log.info(\n `Local LLM model detected: ${this.cachedModelInfo.id}, ` +\n `context window: ${contextWindow?.toLocaleString() || \"unknown (may use default)\"}`\n );\n\n return this.cachedModelInfo;\n } catch (err) {\n log.warn(`Failed to fetch model info: ${err}`);\n return null;\n }\n }\n\n /**\n * Check if the configured model is available and get its actual context window.\n * Warns if there's a mismatch between expected and actual context.\n */\n async validateModelConfig(expectedContextWindow?: number): Promise<{\n available: boolean;\n actualContextWindow?: number;\n warnings: string[];\n }> {\n const warnings: string[] = [];\n\n const modelInfo = await this.getLoadedModelInfo();\n if (!modelInfo) {\n return { available: false, warnings: [\"Could not query local LLM for model info\"] };\n }\n\n // If we have expected context and the server reports one, check for mismatch\n if (expectedContextWindow && modelInfo.contextWindow) {\n if (modelInfo.contextWindow < expectedContextWindow) {\n warnings.push(\n `Context window mismatch: Model ${modelInfo.id} supports ${modelInfo.contextWindow.toLocaleString()} tokens, ` +\n `but engram is configured for ${expectedContextWindow.toLocaleString()}. ` +\n `Set localLlmMaxContext: ${modelInfo.contextWindow} in config to avoid errors.`\n );\n }\n }\n\n // Warn if server doesn't report context window (common with some local LLM setups)\n if (!modelInfo.contextWindow) {\n warnings.push(\n `Local LLM server did not report context window for ${modelInfo.id}. ` +\n `If you get \"context length exceeded\" errors, set localLlmMaxContext in config.`\n );\n }\n\n return {\n available: true,\n actualContextWindow: modelInfo.contextWindow,\n warnings,\n };\n }\n\n /**\n * Make a chat completion request to local LLM\n */\n async chatCompletion(\n messages: Array<{ role: string; content: string }>,\n options: LocalLlmChatCompletionOptions = {},\n ): Promise<LocalLlmChatCompletionResult | null> {\n if (!resolveLocalLlmCapabilities(this.config).localLlm) {\n log.debug(\"local LLM: disabled, returning null\");\n return null;\n }\n\n const remainingMs = this.remainingCooldownMs();\n if (remainingMs > 0) {\n log.debug(`local LLM: cooldown active (${remainingMs}ms remaining), skipping request`);\n return null;\n }\n if (options.priority) {\n const priority = options.priority;\n return await new Promise<LocalLlmChatCompletionResult | null>((resolve) => {\n this.requestQueues[priority].push({\n messages,\n options,\n priority,\n enqueuedAtMs: Date.now(),\n resolve,\n });\n this.scheduleQueueDrain();\n });\n }\n\n return await this.runChatCompletionRequest(messages, options);\n }\n /**\n * Estimate tokens when local LLM does not return usage stats.\n */\n private estimateTokens(\n messages: Array<{ role: string; content: string }>,\n response: string\n ): { promptTokens: number; completionTokens: number; totalTokens: number } {\n const promptTokens = messages.reduce((sum, message) => sum + estimateTokenCount(message.content), 0);\n const completionTokens = estimateTokenCount(response);\n\n return {\n promptTokens,\n completionTokens,\n totalTokens: promptTokens + completionTokens,\n };\n }\n\n /**\n * Try local LLM first, fallback to cloud provider if configured\n */\n async withFallback<T>(\n localOperation: () => Promise<T | null>,\n fallbackOperation: () => Promise<T>,\n operationName: string\n ): Promise<T> {\n // Try local LLM first if enabled\n if (resolveLocalLlmCapabilities(this.config).localLlm) {\n const localResult = await localOperation();\n if (localResult !== null) {\n log.debug(`${operationName}: used local LLM`);\n return localResult;\n }\n\n // Local failed or unavailable\n if (this.config.localLlmFallback) {\n log.info(`${operationName}: local LLM unavailable, falling back to cloud`);\n } else {\n throw new Error(`${operationName}: local LLM unavailable and fallback disabled`);\n }\n }\n\n // Use fallback (cloud provider)\n return fallbackOperation();\n }\n}\n"],"mappings":"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","names":["backoffMs","promptChars"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/secure-store/secure-fs.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\";\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): Promise<Buffer> {\n const buf = 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);\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\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 } = options;\n await mkdir(path.dirname(filePath), { recursive: true });\n\n let data: Buffer | string;\n if (key !== null) {\n const aad = 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(buf: Buffer, key: Buffer, filePath: string, memoryDir?: string): Buffer {\n const aad = 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 (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\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"],"mappings":"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","names":["position","buffer","unlink"]}