@remnic/core 10.0.91 → 10.0.93

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 (522) hide show
  1. package/dist/access-admin-ops-surface.js +334 -61
  2. package/dist/access-authorization-probe.js +2 -283
  3. package/dist/access-boundary.d.ts +1 -1
  4. package/dist/access-boundary.d.ts.map +1 -1
  5. package/dist/access-boundary.js +1 -282
  6. package/dist/access-cli.js +288 -304
  7. package/dist/access-cli.js.map +1 -1
  8. package/dist/access-coding-context-resolution.js +3 -3
  9. package/dist/access-extraction-force-flush.js +334 -61
  10. package/dist/access-http-briefing.js +334 -62
  11. package/dist/access-http-query.js +334 -63
  12. package/dist/access-http.js +332 -114
  13. package/dist/access-identity-continuity-surface.js +334 -61
  14. package/dist/access-lcm-surface.js +334 -61
  15. package/dist/access-mcp-frames.d.ts +20 -6
  16. package/dist/access-mcp-frames.d.ts.map +1 -1
  17. package/dist/access-mcp-frames.js +1 -1
  18. package/dist/access-mcp.d.ts +5 -0
  19. package/dist/access-mcp.d.ts.map +1 -1
  20. package/dist/access-mcp.js +332 -89
  21. package/dist/access-namespace-preflight.js +2 -282
  22. package/dist/access-observe-transcript.js +3 -3
  23. package/dist/access-observe-write-category.js +6 -6
  24. package/dist/access-observe-write-surface.js +334 -61
  25. package/dist/access-offline-manifest.js +12 -11
  26. package/dist/access-operations-batch.js +332 -66
  27. package/dist/access-operations.js +332 -75
  28. package/dist/access-recall-concurrency.js +334 -61
  29. package/dist/access-recall-diagnostics.js +6 -5
  30. package/dist/access-recall-response.js +334 -61
  31. package/dist/access-recall-surface.js +334 -61
  32. package/dist/access-schema.js +2 -2
  33. package/dist/access-service.d.ts +1 -0
  34. package/dist/access-service.d.ts.map +1 -1
  35. package/dist/access-service.js +337 -64
  36. package/dist/access-surface-catalog.js +6 -283
  37. package/dist/access-surface-catalog.js.map +1 -1
  38. package/dist/active-context-transform.js +1 -1
  39. package/dist/active-memory-bridge.js +3 -2
  40. package/dist/active-recall.js +36 -37
  41. package/dist/active-recall.js.map +1 -1
  42. package/dist/activity/journal-extract.d.ts +13 -1
  43. package/dist/activity/journal-extract.d.ts.map +1 -1
  44. package/dist/{auto-sync-3AZW7MXI.js → auto-sync-BO5YZKM6.js} +13 -15
  45. package/dist/{auto-sync-3AZW7MXI.js.map → auto-sync-BO5YZKM6.js.map} +1 -1
  46. package/dist/briefing.js +20 -20
  47. package/dist/buffer.js +16 -16
  48. package/dist/calibration.js +10 -10
  49. package/dist/{capsule-merge-2IIXFXN5.js → capsule-merge-6IDAYTKG.js} +6 -6
  50. package/dist/causal-consolidation.js +23 -23
  51. package/dist/{chunk-QL7RHYK6.js → chunk-2LMA22UU.js} +5 -5
  52. package/dist/{chunk-RGUUBAT5.js → chunk-2WCLTWV2.js} +3 -3
  53. package/dist/{chunk-RSK2WIEM.js → chunk-2YODOG2O.js} +2 -2
  54. package/dist/{chunk-WYG5UNDY.js → chunk-3AUDELZL.js} +2 -2
  55. package/dist/{chunk-OFVCFKDH.js → chunk-3CYOCE6U.js} +3 -3
  56. package/dist/{chunk-6HM6HARE.js → chunk-3FBAEPCU.js} +2 -2
  57. package/dist/{chunk-PIITB2YZ.js → chunk-3ZS3NUEI.js} +4 -4
  58. package/dist/{chunk-V4XE263I.js → chunk-43AA5FVD.js} +1 -1
  59. package/dist/chunk-43AA5FVD.js.map +1 -0
  60. package/dist/{chunk-ANZZLRUX.js → chunk-4IVRQMZW.js} +2 -2
  61. package/dist/{chunk-63E37JCN.js → chunk-4RAJT5L2.js} +4 -4
  62. package/dist/{chunk-45VUHDV2.js → chunk-5CO5G4MN.js} +2 -2
  63. package/dist/{chunk-PUEO37QG.js → chunk-5YT2IIFX.js} +729 -17
  64. package/dist/chunk-5YT2IIFX.js.map +1 -0
  65. package/dist/{chunk-P5VZSSYL.js → chunk-622V2AZA.js} +233 -40
  66. package/dist/chunk-622V2AZA.js.map +1 -0
  67. package/dist/{chunk-3UMDRGFS.js → chunk-65HWAQ4F.js} +3 -3
  68. package/dist/{chunk-T67LUYCR.js → chunk-77NTFICT.js} +5 -5
  69. package/dist/chunk-A2WFUMUN.js +86 -0
  70. package/dist/chunk-A2WFUMUN.js.map +1 -0
  71. package/dist/{chunk-UE6RRXMF.js → chunk-A4F7OIC5.js} +2 -2
  72. package/dist/chunk-AHA5VQGP.js +117 -0
  73. package/dist/chunk-AHA5VQGP.js.map +1 -0
  74. package/dist/{chunk-AOFEMUHG.js → chunk-ATG2PQ4V.js} +56 -5
  75. package/dist/chunk-ATG2PQ4V.js.map +1 -0
  76. package/dist/{chunk-63O7LVMN.js → chunk-B33YWUJV.js} +3 -3
  77. package/dist/{chunk-QVMOCJRV.js → chunk-B377OKUP.js} +2 -2
  78. package/dist/{chunk-IUKYFTGW.js → chunk-B3DR2JBZ.js} +140 -125
  79. package/dist/chunk-B3DR2JBZ.js.map +1 -0
  80. package/dist/{chunk-ZT6WPSII.js → chunk-BGHCSLE5.js} +2 -2
  81. package/dist/{chunk-YYOMGOHB.js → chunk-BOK6BN5M.js} +2 -2
  82. package/dist/{chunk-OH7QLI6W.js → chunk-BPPUH5JM.js} +5 -5
  83. package/dist/{chunk-EV76IZ37.js → chunk-BTI4C4MA.js} +2 -2
  84. package/dist/{chunk-O6ZSWHVR.js → chunk-C2KE5DMD.js} +4 -4
  85. package/dist/{chunk-47F6WWLQ.js → chunk-C73N66PR.js} +2 -2
  86. package/dist/{chunk-3XIWW4TB.js → chunk-CGJYL633.js} +2 -2
  87. package/dist/{chunk-WRT7GJ2D.js → chunk-CIWMTNHW.js} +4 -4
  88. package/dist/{chunk-O55EKIEC.js → chunk-D3ZKHLN2.js} +2 -2
  89. package/dist/{chunk-CD4BCCNP.js → chunk-DJ7WVEWX.js} +2 -2
  90. package/dist/{chunk-RO7RUBLD.js → chunk-EFL24KOI.js} +2 -2
  91. package/dist/{chunk-4CJTQHEG.js → chunk-ESLOLCD4.js} +3 -3
  92. package/dist/{chunk-Z4Q6USKJ.js → chunk-ETBVCRVL.js} +4 -4
  93. package/dist/{chunk-VILPVT4S.js → chunk-EZ47HOJ6.js} +4 -4
  94. package/dist/{chunk-EFYN5I7Z.js → chunk-F4QFU5IZ.js} +2 -2
  95. package/dist/{chunk-T6OZW2WR.js → chunk-FA7SLQ5X.js} +7 -1
  96. package/dist/chunk-FA7SLQ5X.js.map +1 -0
  97. package/dist/{chunk-SHUBTAY2.js → chunk-FMKILENU.js} +14 -14
  98. package/dist/{chunk-YLXETSAH.js → chunk-GCZJ7XRI.js} +2 -2
  99. package/dist/{chunk-J4MMPQ7Q.js → chunk-GQXOBKPL.js} +7 -9
  100. package/dist/{chunk-J4MMPQ7Q.js.map → chunk-GQXOBKPL.js.map} +1 -1
  101. package/dist/{chunk-UDKWFDV2.js → chunk-HLUHEMN4.js} +27 -3
  102. package/dist/chunk-HLUHEMN4.js.map +1 -0
  103. package/dist/{chunk-QFEDITXM.js → chunk-JNDMIAJ4.js} +2 -2
  104. package/dist/chunk-JTSSSR4L.js +359 -0
  105. package/dist/chunk-JTSSSR4L.js.map +1 -0
  106. package/dist/{chunk-5KBIOI4G.js → chunk-JVBAPAWZ.js} +217 -13
  107. package/dist/chunk-JVBAPAWZ.js.map +1 -0
  108. package/dist/{chunk-E5PPE4VA.js → chunk-K2VRKCPA.js} +2 -2
  109. package/dist/{chunk-MKRYQROF.js → chunk-LGEHAQHK.js} +4 -4
  110. package/dist/{chunk-7HTUYFR3.js → chunk-LLPMMMWG.js} +27 -10
  111. package/dist/chunk-LLPMMMWG.js.map +1 -0
  112. package/dist/{chunk-XYTQJD7F.js → chunk-LMJVP5CM.js} +2 -2
  113. package/dist/{chunk-JC36FPQ5.js → chunk-LPV72KRQ.js} +2 -2
  114. package/dist/{chunk-2LCRG2YV.js → chunk-M2XR6B6V.js} +2 -2
  115. package/dist/{chunk-GGRRF4PZ.js → chunk-MDPT6QFK.js} +1 -1
  116. package/dist/{chunk-GGRRF4PZ.js.map → chunk-MDPT6QFK.js.map} +1 -1
  117. package/dist/{chunk-VPQFK63H.js → chunk-MH632KPO.js} +2 -2
  118. package/dist/{chunk-EQ4U73UH.js → chunk-MLAMXQAW.js} +14 -2
  119. package/dist/chunk-MLAMXQAW.js.map +1 -0
  120. package/dist/{chunk-ZU5JDLOH.js → chunk-MMRTPH3J.js} +2 -2
  121. package/dist/{chunk-ZZ67UTQX.js → chunk-MUX7IL77.js} +2 -2
  122. package/dist/{chunk-RYWROSL5.js → chunk-NAQ4A5IX.js} +7 -7
  123. package/dist/{chunk-TVIVY7KU.js → chunk-NED7IR7B.js} +4 -4
  124. package/dist/{chunk-ADYP7HXE.js → chunk-NHVAX7MH.js} +3 -3
  125. package/dist/{chunk-TG3R2SID.js → chunk-NMGHGTVR.js} +5 -5
  126. package/dist/{chunk-YCNIGRRF.js → chunk-NS7U4ZYU.js} +13 -117
  127. package/dist/chunk-NS7U4ZYU.js.map +1 -0
  128. package/dist/{chunk-NGHDW6UU.js → chunk-NUQEREIP.js} +2 -2
  129. package/dist/{chunk-5IIQU4JZ.js → chunk-OB72KNMF.js} +4 -4
  130. package/dist/{chunk-7L3TODI7.js → chunk-OJXJWNFY.js} +2 -2
  131. package/dist/{chunk-66L5ZRJC.js → chunk-P3WRNWWM.js} +157 -4
  132. package/dist/chunk-P3WRNWWM.js.map +1 -0
  133. package/dist/{chunk-E6M225BJ.js → chunk-PC5DRBGD.js} +18 -8
  134. package/dist/chunk-PC5DRBGD.js.map +1 -0
  135. package/dist/{chunk-EURPSF4Y.js → chunk-PH5ZQM35.js} +2 -2
  136. package/dist/{chunk-BMWMTAQF.js → chunk-PRAUN6PN.js} +3 -3
  137. package/dist/{chunk-RWGRSKN5.js → chunk-PT4DZDJA.js} +2 -2
  138. package/dist/{chunk-7EPY3IQL.js → chunk-PZJQ46DH.js} +2 -2
  139. package/dist/{chunk-AH637HZ5.js → chunk-QDESY76F.js} +3 -3
  140. package/dist/{chunk-I6QGI65T.js → chunk-QRMG4HK4.js} +3 -3
  141. package/dist/{chunk-ZU3NJNO7.js → chunk-QSF5SJAM.js} +4 -4
  142. package/dist/{chunk-5IYOKDM4.js → chunk-R23JWJD3.js} +5 -5
  143. package/dist/{chunk-EWJZZDRG.js → chunk-R4BBGPI2.js} +219 -219
  144. package/dist/chunk-R4BBGPI2.js.map +1 -0
  145. package/dist/{chunk-LJQFJF4J.js → chunk-RM2YUFH6.js} +6 -6
  146. package/dist/{chunk-IPT2OY4B.js → chunk-RQFXZYTF.js} +35 -33
  147. package/dist/chunk-RQFXZYTF.js.map +1 -0
  148. package/dist/{chunk-AP6NCP3V.js → chunk-S3S72HRM.js} +8 -6
  149. package/dist/chunk-S3S72HRM.js.map +1 -0
  150. package/dist/{chunk-6QMWECO5.js → chunk-SGQPR5SX.js} +7 -7
  151. package/dist/{chunk-ZGFLIK5H.js → chunk-SPAKBUJY.js} +2 -2
  152. package/dist/{chunk-ER7XANP3.js → chunk-SQ3GEIEL.js} +4 -4
  153. package/dist/{chunk-5UCMYPVF.js → chunk-SVT5IA2H.js} +2 -2
  154. package/dist/{chunk-HVKUCT3W.js → chunk-T4KY6UYF.js} +5 -3
  155. package/dist/{chunk-HVKUCT3W.js.map → chunk-T4KY6UYF.js.map} +1 -1
  156. package/dist/{chunk-FRSHF6FU.js → chunk-TJ4F6H4H.js} +3 -3
  157. package/dist/{chunk-R6HMAIKM.js → chunk-TOSPNM4T.js} +2 -2
  158. package/dist/{chunk-NJJMVS5Y.js → chunk-TRQE3RYR.js} +5 -5
  159. package/dist/{chunk-RMXS4I66.js → chunk-TVDJEUV5.js} +2 -2
  160. package/dist/{chunk-PLCYKAWH.js → chunk-TYRWSE4B.js} +2 -2
  161. package/dist/{chunk-YS2GHSWN.js → chunk-UBEKET2V.js} +389 -39
  162. package/dist/chunk-UBEKET2V.js.map +1 -0
  163. package/dist/{chunk-IQWUACVG.js → chunk-UHMHJX5G.js} +3 -3
  164. package/dist/{chunk-YODUDA6R.js → chunk-UN4RVUG2.js} +22 -22
  165. package/dist/{chunk-KZSGBWOJ.js → chunk-V2KGDESV.js} +2 -2
  166. package/dist/{chunk-E6Z4FBX6.js → chunk-V2QSNFCT.js} +2 -2
  167. package/dist/{chunk-BCCG7VWM.js → chunk-V65H7MI6.js} +396 -13
  168. package/dist/chunk-V65H7MI6.js.map +1 -0
  169. package/dist/chunk-W54Z2MOP.js +4500 -0
  170. package/dist/chunk-W54Z2MOP.js.map +1 -0
  171. package/dist/{chunk-N74SGESS.js → chunk-WKSQTMU7.js} +2 -2
  172. package/dist/{chunk-RIZ5NUNN.js → chunk-WQ4XKIOX.js} +4 -4
  173. package/dist/{chunk-WWK6J6SE.js → chunk-WQURALGV.js} +4996 -2748
  174. package/dist/chunk-WQURALGV.js.map +1 -0
  175. package/dist/{chunk-QEYTRFR3.js → chunk-X26YOFGS.js} +2 -2
  176. package/dist/{chunk-ITHZFHST.js → chunk-X2BVB74H.js} +2 -2
  177. package/dist/{chunk-GNXGJBCA.js → chunk-XIYLWEXC.js} +2 -2
  178. package/dist/{chunk-IBJ6U753.js → chunk-XKZBZOKV.js} +4 -4
  179. package/dist/{chunk-7O2MC42R.js → chunk-XNRLRNQF.js} +2 -2
  180. package/dist/{chunk-65AUYLU3.js → chunk-XPXHQI6N.js} +2 -2
  181. package/dist/{chunk-JBCCWVU2.js → chunk-XRDYRWZI.js} +2 -2
  182. package/dist/chunk-XRWWCW4H.js +102 -0
  183. package/dist/chunk-XRWWCW4H.js.map +1 -0
  184. package/dist/chunk-YKJBSMMP.js +67137 -0
  185. package/dist/chunk-YKJBSMMP.js.map +1 -0
  186. package/dist/{chunk-2JSDPUKD.js → chunk-YMFPFXEX.js} +2 -2
  187. package/dist/{chunk-645WOU5I.js → chunk-ZDZJNXL6.js} +4 -4
  188. package/dist/{chunk-6TCXIRP5.js → chunk-ZKE4CO6B.js} +4 -4
  189. package/dist/{chunk-KEPXC4KN.js → chunk-ZSE774RV.js} +2 -2
  190. package/dist/cli.d.ts.map +1 -1
  191. package/dist/cli.js +305 -187
  192. package/dist/coding/export-okf-codegraph.js +29 -29
  193. package/dist/compounding/engine.js +23 -24
  194. package/dist/config.d.ts.map +1 -1
  195. package/dist/config.js +36 -37
  196. package/dist/connectors/codex-materialize-runner.js +20 -20
  197. package/dist/connectors/index.js +21 -21
  198. package/dist/connectors/live/framework.d.ts +7 -0
  199. package/dist/connectors/live/framework.d.ts.map +1 -1
  200. package/dist/connectors/live/google-drive.d.ts +17 -3
  201. package/dist/connectors/live/google-drive.d.ts.map +1 -1
  202. package/dist/consolidation-provenance-check.js +4 -4
  203. package/dist/consolidation-undo.js +5 -5
  204. package/dist/contradiction/index.js +7 -6
  205. package/dist/{contradiction-scan-P2MHYFVG.js → contradiction-scan-IBPYGQZF.js} +4 -3
  206. package/dist/convergence-refresh.js +22 -22
  207. package/dist/corpus-watermark.js +23 -23
  208. package/dist/deep-recall-http-glue.js +2 -2
  209. package/dist/deep-recall-policy-llm.js +4 -4
  210. package/dist/direct-answer-wiring.js +2 -2
  211. package/dist/embedding-fallback.js +1 -1
  212. package/dist/entity-retrieval.js +21 -21
  213. package/dist/eval-trajectory-store.js +2 -2
  214. package/dist/eval-trajectory.js +2 -2
  215. package/dist/event-order-recall.js +2 -2
  216. package/dist/explicit-capture.d.ts +3 -2
  217. package/dist/explicit-capture.d.ts.map +1 -1
  218. package/dist/explicit-capture.js +530 -6
  219. package/dist/explicit-cue-recall.js +2 -2
  220. package/dist/external-wiki-access.js +2 -283
  221. package/dist/extraction-faithfulness.js +4 -4
  222. package/dist/extraction-judge.js +5 -5
  223. package/dist/extraction-normalization.js +1 -1
  224. package/dist/extraction-quote-grounding.d.ts.map +1 -1
  225. package/dist/extraction-quote-grounding.js +1 -1
  226. package/dist/extraction-source-grounding-rules.js +2 -2
  227. package/dist/extraction-source-grounding.js +3 -3
  228. package/dist/extraction.js +43 -44
  229. package/dist/fallback-llm-codex-subscription.js +2 -2
  230. package/dist/fallback-llm.js +3 -3
  231. package/dist/{first-start-migration-QFI7B5SH.js → first-start-migration-FILJH3RO.js} +6 -5
  232. package/dist/{first-start-migration-QFI7B5SH.js.map → first-start-migration-FILJH3RO.js.map} +1 -1
  233. package/dist/focused-list-recall.js +2 -2
  234. package/dist/harmonic-retrieval.js +20 -20
  235. package/dist/index.js +776 -797
  236. package/dist/lcm/index.js +2 -2
  237. package/dist/live-connectors-runner.d.ts +2 -0
  238. package/dist/live-connectors-runner.d.ts.map +1 -1
  239. package/dist/live-connectors-runner.js +3 -4
  240. package/dist/local-llm-config.js +2 -2
  241. package/dist/local-llm-helpers.d.ts +4 -0
  242. package/dist/local-llm-helpers.d.ts.map +1 -1
  243. package/dist/local-llm-helpers.js +3 -1
  244. package/dist/local-llm.d.ts +4 -1
  245. package/dist/local-llm.d.ts.map +1 -1
  246. package/dist/local-llm.js +4 -4
  247. package/dist/location/index.js +27 -29
  248. package/dist/maintenance/memory-governance.js +20 -20
  249. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +20 -20
  250. package/dist/maintenance/rebuild-memory-projection-runner.js +22 -22
  251. package/dist/maintenance/rebuild-memory-projection.js +21 -21
  252. package/dist/memory-browse-operations.js +2 -283
  253. package/dist/memory-browse.js +8 -7
  254. package/dist/memory-controller.js +2 -2
  255. package/dist/memory-subject.js +2 -2
  256. package/dist/namespaces/migrate.js +21 -21
  257. package/dist/namespaces/search.js +5 -5
  258. package/dist/namespaces/storage.js +20 -20
  259. package/dist/offline-sync-changeset-generations.js +1 -1
  260. package/dist/offline-sync-embedding-generation.js +1 -1
  261. package/dist/offline-sync-impression-drain.js +6 -6
  262. package/dist/offline-sync-snapshot-stream.js +1 -1
  263. package/dist/offline-sync.js +1 -1
  264. package/dist/operator-doctor-corpus.js +24 -24
  265. package/dist/operator-toolkit.js +58 -61
  266. package/dist/orchestration/backlog-scheduler.js +279 -295
  267. package/dist/orchestration/backlog-scheduler.js.map +1 -1
  268. package/dist/orchestration/buffer-drain-runner.js +279 -295
  269. package/dist/orchestration/extraction-run.d.ts +4 -0
  270. package/dist/orchestration/extraction-run.d.ts.map +1 -1
  271. package/dist/orchestration/fact-dedup-window.d.ts +1 -1
  272. package/dist/orchestration/fact-dedup-window.d.ts.map +1 -1
  273. package/dist/orchestration/maintenance.js +39 -42
  274. package/dist/orchestrator.d.ts +11 -11
  275. package/dist/orchestrator.d.ts.map +1 -1
  276. package/dist/orchestrator.js +291 -308
  277. package/dist/page-versioning.js +3 -3
  278. package/dist/qmd-daemon-session.js +2 -2
  279. package/dist/qmd.js +3 -3
  280. package/dist/recall-explain-renderer.js +3 -3
  281. package/dist/recall-navigate-access.js +2 -2
  282. package/dist/recall-navigate-disclosure.js +2 -2
  283. package/dist/recall-navigate.js +2 -2
  284. package/dist/recall-navigation-operations.js +2 -283
  285. package/dist/recall-navigation.js +334 -61
  286. package/dist/recall-planner-llm.js +3 -3
  287. package/dist/recall-state-view-admission.js +6 -6
  288. package/dist/recall-why-service.js +10 -9
  289. package/dist/recall-why.js +4 -3
  290. package/dist/recall-xray-cli.js +4 -4
  291. package/dist/recall-xray-renderer.js +3 -3
  292. package/dist/recall-xray.js +2 -2
  293. package/dist/reconcile/manifest.js +3 -3
  294. package/dist/{resolution-N7H3F2E4.js → resolution-OVVIF7ZY.js} +8 -7
  295. package/dist/response-guidance-recall.js +2 -2
  296. package/dist/resume-bundles.js +40 -41
  297. package/dist/salvage-envelope.js +4 -4
  298. package/dist/search/factory.js +4 -4
  299. package/dist/search/index.js +4 -4
  300. package/dist/semantic-consolidation.js +23 -23
  301. package/dist/semantic-rule-promotion.js +20 -20
  302. package/dist/semantic-rule-verifier.js +20 -20
  303. package/dist/shared-context/manager.js +2 -3
  304. package/dist/source-agent-qualifier.js +32 -33
  305. package/dist/standup/build.js +20 -20
  306. package/dist/{state-store-I5HLZTZT.js → state-store-EEAX2MBS.js} +2 -2
  307. package/dist/storage/content-hash-index.d.ts +10 -0
  308. package/dist/storage/content-hash-index.d.ts.map +1 -1
  309. package/dist/storage.js +19 -19
  310. package/dist/subject-config.js +3 -3
  311. package/dist/summarizer.d.ts +4 -1
  312. package/dist/summarizer.d.ts.map +1 -1
  313. package/dist/summarizer.js +6 -6
  314. package/dist/support-passport/index.js +53 -297
  315. package/dist/targeted-fact-recall.js +2 -2
  316. package/dist/tier-routing.js +3 -2
  317. package/dist/{tier-stats-FQGQSFET.js → tier-stats-FXEUJN73.js} +6 -5
  318. package/dist/{tier-stats-FQGQSFET.js.map → tier-stats-FXEUJN73.js.map} +1 -1
  319. package/dist/transcript.js +2 -2
  320. package/dist/transfer/capsule-import.js +4 -4
  321. package/dist/transfer/export-okf.js +20 -20
  322. package/dist/types.d.ts +10 -10
  323. package/dist/types.d.ts.map +1 -1
  324. package/dist/types.js +1 -1
  325. package/dist/verified-recall.js +20 -20
  326. package/dist/wearables/memory-gen.d.ts +5 -1
  327. package/dist/wearables/memory-gen.d.ts.map +1 -1
  328. package/dist/wearables/service.d.ts.map +1 -1
  329. package/dist/who-knows-cli.js +21 -21
  330. package/dist/who-knows.js +20 -20
  331. package/dist/write-envelope.js +3 -3
  332. package/package.json +2 -2
  333. package/src/access-boundary.ts +5 -1
  334. package/src/access-category-alias.test.ts +5 -0
  335. package/src/access-mcp-frames.test.ts +60 -5
  336. package/src/access-mcp-frames.ts +72 -18
  337. package/src/access-mcp.test.ts +66 -2
  338. package/src/access-mcp.ts +19 -13
  339. package/src/access-service-coding-write.test.ts +7 -0
  340. package/src/access-service.ts +3 -0
  341. package/src/activity/journal-extract-storage.test.ts +31 -0
  342. package/src/activity/journal-extract.test.ts +1 -0
  343. package/src/activity/journal-extract.ts +24 -3
  344. package/src/cli.ts +1 -2
  345. package/src/config.test.ts +17 -0
  346. package/src/config.ts +25 -7
  347. package/src/connectors/live/framework.ts +8 -0
  348. package/src/connectors/live/google-drive.test.ts +74 -0
  349. package/src/connectors/live/google-drive.ts +138 -11
  350. package/src/explicit-capture.test.ts +109 -17
  351. package/src/explicit-capture.ts +90 -37
  352. package/src/extraction-quote-grounding.test.ts +20 -0
  353. package/src/extraction-quote-grounding.ts +21 -11
  354. package/src/fact-dedup-window.test.ts +37 -0
  355. package/src/live-connectors-runner.ts +3 -0
  356. package/src/live-connectors-secretref.test.ts +126 -0
  357. package/src/local-llm-helpers.ts +12 -0
  358. package/src/local-llm-shutdown.test.ts +194 -0
  359. package/src/local-llm.ts +32 -30
  360. package/src/orchestration/extraction-run.ts +12 -3
  361. package/src/orchestration/fact-dedup-window.ts +5 -1
  362. package/src/orchestrator-connector-dedup.test.ts +9 -5
  363. package/src/orchestrator.ts +52 -55
  364. package/src/storage/content-hash-index.ts +27 -2
  365. package/src/storage-fact-hash-publish-failure.test.ts +87 -0
  366. package/src/summarizer-shutdown.test.ts +195 -0
  367. package/src/summarizer.ts +15 -2
  368. package/src/types.ts +10 -10
  369. package/src/wearables/memory-gen.test.ts +20 -0
  370. package/src/wearables/memory-gen.ts +14 -7
  371. package/src/wearables/service.ts +14 -1
  372. package/src/wearables/storage-io.test.ts +25 -0
  373. package/dist/chunk-27XYV2XL.js +0 -43
  374. package/dist/chunk-27XYV2XL.js.map +0 -1
  375. package/dist/chunk-5KBIOI4G.js.map +0 -1
  376. package/dist/chunk-66L5ZRJC.js.map +0 -1
  377. package/dist/chunk-7HTUYFR3.js.map +0 -1
  378. package/dist/chunk-AOFEMUHG.js.map +0 -1
  379. package/dist/chunk-AP6NCP3V.js.map +0 -1
  380. package/dist/chunk-APYY5CX3.js +0 -335
  381. package/dist/chunk-APYY5CX3.js.map +0 -1
  382. package/dist/chunk-ATPWMT2E.js +0 -223
  383. package/dist/chunk-ATPWMT2E.js.map +0 -1
  384. package/dist/chunk-BCCG7VWM.js.map +0 -1
  385. package/dist/chunk-E6M225BJ.js.map +0 -1
  386. package/dist/chunk-EQ4U73UH.js.map +0 -1
  387. package/dist/chunk-ETSJD5IV.js +0 -361
  388. package/dist/chunk-ETSJD5IV.js.map +0 -1
  389. package/dist/chunk-EWJZZDRG.js.map +0 -1
  390. package/dist/chunk-FHGWKODN.js +0 -2300
  391. package/dist/chunk-FHGWKODN.js.map +0 -1
  392. package/dist/chunk-GRTKLWYX.js +0 -621
  393. package/dist/chunk-GRTKLWYX.js.map +0 -1
  394. package/dist/chunk-IGLJNHOX.js +0 -62
  395. package/dist/chunk-IGLJNHOX.js.map +0 -1
  396. package/dist/chunk-IPT2OY4B.js.map +0 -1
  397. package/dist/chunk-IUKYFTGW.js.map +0 -1
  398. package/dist/chunk-KUIRDD74.js +0 -3991
  399. package/dist/chunk-KUIRDD74.js.map +0 -1
  400. package/dist/chunk-MVMRUGBH.js +0 -1919
  401. package/dist/chunk-MVMRUGBH.js.map +0 -1
  402. package/dist/chunk-ODL4ZTNI.js +0 -3644
  403. package/dist/chunk-ODL4ZTNI.js.map +0 -1
  404. package/dist/chunk-OGXXGHW6.js +0 -223
  405. package/dist/chunk-OGXXGHW6.js.map +0 -1
  406. package/dist/chunk-P5VZSSYL.js.map +0 -1
  407. package/dist/chunk-PPCLZ4JU.js +0 -28
  408. package/dist/chunk-PPCLZ4JU.js.map +0 -1
  409. package/dist/chunk-PUEO37QG.js.map +0 -1
  410. package/dist/chunk-RRTVGD3J.js +0 -792
  411. package/dist/chunk-RRTVGD3J.js.map +0 -1
  412. package/dist/chunk-STSLB4HT.js +0 -18793
  413. package/dist/chunk-STSLB4HT.js.map +0 -1
  414. package/dist/chunk-T6OZW2WR.js.map +0 -1
  415. package/dist/chunk-TRBGGJKZ.js +0 -32218
  416. package/dist/chunk-TRBGGJKZ.js.map +0 -1
  417. package/dist/chunk-UDKWFDV2.js.map +0 -1
  418. package/dist/chunk-V4XE263I.js.map +0 -1
  419. package/dist/chunk-V7IZKJI3.js +0 -22
  420. package/dist/chunk-V7IZKJI3.js.map +0 -1
  421. package/dist/chunk-VMVVSZTD.js +0 -61
  422. package/dist/chunk-VMVVSZTD.js.map +0 -1
  423. package/dist/chunk-VNUOAXWP.js +0 -203
  424. package/dist/chunk-VNUOAXWP.js.map +0 -1
  425. package/dist/chunk-WLBEZZK5.js +0 -729
  426. package/dist/chunk-WLBEZZK5.js.map +0 -1
  427. package/dist/chunk-WUUES6CU.js +0 -25
  428. package/dist/chunk-WUUES6CU.js.map +0 -1
  429. package/dist/chunk-WWK6J6SE.js.map +0 -1
  430. package/dist/chunk-XICOH5HT.js +0 -346
  431. package/dist/chunk-XICOH5HT.js.map +0 -1
  432. package/dist/chunk-YCNIGRRF.js.map +0 -1
  433. package/dist/chunk-YS2GHSWN.js.map +0 -1
  434. package/dist/chunk-ZCLEYJPB.js +0 -10143
  435. package/dist/chunk-ZCLEYJPB.js.map +0 -1
  436. /package/dist/{capsule-merge-2IIXFXN5.js.map → capsule-merge-6IDAYTKG.js.map} +0 -0
  437. /package/dist/{chunk-QL7RHYK6.js.map → chunk-2LMA22UU.js.map} +0 -0
  438. /package/dist/{chunk-RGUUBAT5.js.map → chunk-2WCLTWV2.js.map} +0 -0
  439. /package/dist/{chunk-RSK2WIEM.js.map → chunk-2YODOG2O.js.map} +0 -0
  440. /package/dist/{chunk-WYG5UNDY.js.map → chunk-3AUDELZL.js.map} +0 -0
  441. /package/dist/{chunk-OFVCFKDH.js.map → chunk-3CYOCE6U.js.map} +0 -0
  442. /package/dist/{chunk-6HM6HARE.js.map → chunk-3FBAEPCU.js.map} +0 -0
  443. /package/dist/{chunk-PIITB2YZ.js.map → chunk-3ZS3NUEI.js.map} +0 -0
  444. /package/dist/{chunk-ANZZLRUX.js.map → chunk-4IVRQMZW.js.map} +0 -0
  445. /package/dist/{chunk-63E37JCN.js.map → chunk-4RAJT5L2.js.map} +0 -0
  446. /package/dist/{chunk-45VUHDV2.js.map → chunk-5CO5G4MN.js.map} +0 -0
  447. /package/dist/{chunk-3UMDRGFS.js.map → chunk-65HWAQ4F.js.map} +0 -0
  448. /package/dist/{chunk-T67LUYCR.js.map → chunk-77NTFICT.js.map} +0 -0
  449. /package/dist/{chunk-UE6RRXMF.js.map → chunk-A4F7OIC5.js.map} +0 -0
  450. /package/dist/{chunk-63O7LVMN.js.map → chunk-B33YWUJV.js.map} +0 -0
  451. /package/dist/{chunk-QVMOCJRV.js.map → chunk-B377OKUP.js.map} +0 -0
  452. /package/dist/{chunk-ZT6WPSII.js.map → chunk-BGHCSLE5.js.map} +0 -0
  453. /package/dist/{chunk-YYOMGOHB.js.map → chunk-BOK6BN5M.js.map} +0 -0
  454. /package/dist/{chunk-OH7QLI6W.js.map → chunk-BPPUH5JM.js.map} +0 -0
  455. /package/dist/{chunk-EV76IZ37.js.map → chunk-BTI4C4MA.js.map} +0 -0
  456. /package/dist/{chunk-O6ZSWHVR.js.map → chunk-C2KE5DMD.js.map} +0 -0
  457. /package/dist/{chunk-47F6WWLQ.js.map → chunk-C73N66PR.js.map} +0 -0
  458. /package/dist/{chunk-3XIWW4TB.js.map → chunk-CGJYL633.js.map} +0 -0
  459. /package/dist/{chunk-WRT7GJ2D.js.map → chunk-CIWMTNHW.js.map} +0 -0
  460. /package/dist/{chunk-O55EKIEC.js.map → chunk-D3ZKHLN2.js.map} +0 -0
  461. /package/dist/{chunk-CD4BCCNP.js.map → chunk-DJ7WVEWX.js.map} +0 -0
  462. /package/dist/{chunk-RO7RUBLD.js.map → chunk-EFL24KOI.js.map} +0 -0
  463. /package/dist/{chunk-4CJTQHEG.js.map → chunk-ESLOLCD4.js.map} +0 -0
  464. /package/dist/{chunk-Z4Q6USKJ.js.map → chunk-ETBVCRVL.js.map} +0 -0
  465. /package/dist/{chunk-VILPVT4S.js.map → chunk-EZ47HOJ6.js.map} +0 -0
  466. /package/dist/{chunk-EFYN5I7Z.js.map → chunk-F4QFU5IZ.js.map} +0 -0
  467. /package/dist/{chunk-SHUBTAY2.js.map → chunk-FMKILENU.js.map} +0 -0
  468. /package/dist/{chunk-YLXETSAH.js.map → chunk-GCZJ7XRI.js.map} +0 -0
  469. /package/dist/{chunk-QFEDITXM.js.map → chunk-JNDMIAJ4.js.map} +0 -0
  470. /package/dist/{chunk-E5PPE4VA.js.map → chunk-K2VRKCPA.js.map} +0 -0
  471. /package/dist/{chunk-MKRYQROF.js.map → chunk-LGEHAQHK.js.map} +0 -0
  472. /package/dist/{chunk-XYTQJD7F.js.map → chunk-LMJVP5CM.js.map} +0 -0
  473. /package/dist/{chunk-JC36FPQ5.js.map → chunk-LPV72KRQ.js.map} +0 -0
  474. /package/dist/{chunk-2LCRG2YV.js.map → chunk-M2XR6B6V.js.map} +0 -0
  475. /package/dist/{chunk-VPQFK63H.js.map → chunk-MH632KPO.js.map} +0 -0
  476. /package/dist/{chunk-ZU5JDLOH.js.map → chunk-MMRTPH3J.js.map} +0 -0
  477. /package/dist/{chunk-ZZ67UTQX.js.map → chunk-MUX7IL77.js.map} +0 -0
  478. /package/dist/{chunk-RYWROSL5.js.map → chunk-NAQ4A5IX.js.map} +0 -0
  479. /package/dist/{chunk-TVIVY7KU.js.map → chunk-NED7IR7B.js.map} +0 -0
  480. /package/dist/{chunk-ADYP7HXE.js.map → chunk-NHVAX7MH.js.map} +0 -0
  481. /package/dist/{chunk-TG3R2SID.js.map → chunk-NMGHGTVR.js.map} +0 -0
  482. /package/dist/{chunk-NGHDW6UU.js.map → chunk-NUQEREIP.js.map} +0 -0
  483. /package/dist/{chunk-5IIQU4JZ.js.map → chunk-OB72KNMF.js.map} +0 -0
  484. /package/dist/{chunk-7L3TODI7.js.map → chunk-OJXJWNFY.js.map} +0 -0
  485. /package/dist/{chunk-EURPSF4Y.js.map → chunk-PH5ZQM35.js.map} +0 -0
  486. /package/dist/{chunk-BMWMTAQF.js.map → chunk-PRAUN6PN.js.map} +0 -0
  487. /package/dist/{chunk-RWGRSKN5.js.map → chunk-PT4DZDJA.js.map} +0 -0
  488. /package/dist/{chunk-7EPY3IQL.js.map → chunk-PZJQ46DH.js.map} +0 -0
  489. /package/dist/{chunk-AH637HZ5.js.map → chunk-QDESY76F.js.map} +0 -0
  490. /package/dist/{chunk-I6QGI65T.js.map → chunk-QRMG4HK4.js.map} +0 -0
  491. /package/dist/{chunk-ZU3NJNO7.js.map → chunk-QSF5SJAM.js.map} +0 -0
  492. /package/dist/{chunk-5IYOKDM4.js.map → chunk-R23JWJD3.js.map} +0 -0
  493. /package/dist/{chunk-LJQFJF4J.js.map → chunk-RM2YUFH6.js.map} +0 -0
  494. /package/dist/{chunk-6QMWECO5.js.map → chunk-SGQPR5SX.js.map} +0 -0
  495. /package/dist/{chunk-ZGFLIK5H.js.map → chunk-SPAKBUJY.js.map} +0 -0
  496. /package/dist/{chunk-ER7XANP3.js.map → chunk-SQ3GEIEL.js.map} +0 -0
  497. /package/dist/{chunk-5UCMYPVF.js.map → chunk-SVT5IA2H.js.map} +0 -0
  498. /package/dist/{chunk-FRSHF6FU.js.map → chunk-TJ4F6H4H.js.map} +0 -0
  499. /package/dist/{chunk-R6HMAIKM.js.map → chunk-TOSPNM4T.js.map} +0 -0
  500. /package/dist/{chunk-NJJMVS5Y.js.map → chunk-TRQE3RYR.js.map} +0 -0
  501. /package/dist/{chunk-RMXS4I66.js.map → chunk-TVDJEUV5.js.map} +0 -0
  502. /package/dist/{chunk-PLCYKAWH.js.map → chunk-TYRWSE4B.js.map} +0 -0
  503. /package/dist/{chunk-IQWUACVG.js.map → chunk-UHMHJX5G.js.map} +0 -0
  504. /package/dist/{chunk-YODUDA6R.js.map → chunk-UN4RVUG2.js.map} +0 -0
  505. /package/dist/{chunk-KZSGBWOJ.js.map → chunk-V2KGDESV.js.map} +0 -0
  506. /package/dist/{chunk-E6Z4FBX6.js.map → chunk-V2QSNFCT.js.map} +0 -0
  507. /package/dist/{chunk-N74SGESS.js.map → chunk-WKSQTMU7.js.map} +0 -0
  508. /package/dist/{chunk-RIZ5NUNN.js.map → chunk-WQ4XKIOX.js.map} +0 -0
  509. /package/dist/{chunk-QEYTRFR3.js.map → chunk-X26YOFGS.js.map} +0 -0
  510. /package/dist/{chunk-ITHZFHST.js.map → chunk-X2BVB74H.js.map} +0 -0
  511. /package/dist/{chunk-GNXGJBCA.js.map → chunk-XIYLWEXC.js.map} +0 -0
  512. /package/dist/{chunk-IBJ6U753.js.map → chunk-XKZBZOKV.js.map} +0 -0
  513. /package/dist/{chunk-7O2MC42R.js.map → chunk-XNRLRNQF.js.map} +0 -0
  514. /package/dist/{chunk-65AUYLU3.js.map → chunk-XPXHQI6N.js.map} +0 -0
  515. /package/dist/{chunk-JBCCWVU2.js.map → chunk-XRDYRWZI.js.map} +0 -0
  516. /package/dist/{chunk-2JSDPUKD.js.map → chunk-YMFPFXEX.js.map} +0 -0
  517. /package/dist/{chunk-645WOU5I.js.map → chunk-ZDZJNXL6.js.map} +0 -0
  518. /package/dist/{chunk-6TCXIRP5.js.map → chunk-ZKE4CO6B.js.map} +0 -0
  519. /package/dist/{chunk-KEPXC4KN.js.map → chunk-ZSE774RV.js.map} +0 -0
  520. /package/dist/{contradiction-scan-P2MHYFVG.js.map → contradiction-scan-IBPYGQZF.js.map} +0 -0
  521. /package/dist/{resolution-N7H3F2E4.js.map → resolution-OVVIF7ZY.js.map} +0 -0
  522. /package/dist/{state-store-I5HLZTZT.js.map → state-store-EEAX2MBS.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/storage/content-hash-index.ts"],"sourcesContent":["// Content-hash dedup index for facts, extracted from storage.ts (issue #1909 /\n// PR #2016 write-hot-paths). Self-contained: cross-process locked reconcile /\n// rebuild saves plus the deferred durable-retry machinery. storage.ts imports\n// these back and re-exports the class + companion types that were previously\n// part of its public API.\nimport { mkdir, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { computeContentHash, normalizeContent } from \"../content-hash.js\";\nimport { log } from \"../logger.js\";\nimport { SecureStoreLockedError, readMaybeEncryptedFile, writeMaybeEncryptedFile } from \"../secure-store/secure-fs.js\";\nimport { isErrnoCode } from \"../utils/errno.js\";\nimport { withHeldFileLock } from \"../utils/serialize-mutations.js\";\n\n/**\n * Stale threshold (ms) for the per-file advisory lock guarding a fact-hash\n * index merge-save (issue #1909 review round 7). The critical section is a\n * single read+atomic-rewrite of one index file (≤ a few MB), so a holder older\n * than this is treated as crashed. Generous to tolerate slow/encrypted disks\n * without breaking a legitimately in-progress publish.\n */\nconst CONTENT_HASH_INDEX_LOCK_STALE_MS = 30_000;\n/** Version marker for the normalized Unicode hash index format. */\nconst CONTENT_HASH_INDEX_FORMAT_HEADER = \"# remnic-content-hash-index:v2\";\n\nfunction parseHashIndex(raw: string): { versioned: boolean; hashes: Set<string> } {\n const lines = raw\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0);\n if (lines[0] !== CONTENT_HASH_INDEX_FORMAT_HEADER) {\n return { versioned: false, hashes: new Set() };\n }\n return { versioned: true, hashes: new Set(lines.slice(1)) };\n}\n\nfunction serializeHashIndex(hashes: Set<string>): string {\n return `${CONTENT_HASH_INDEX_FORMAT_HEADER}\\n${[...hashes].join(\"\\n\")}\\n`;\n}\n\n/**\n * Durable-retry policy for a deferred content-hash reconcile save whose\n * cross-process lock acquire timed out (issue #1909 / PR #2016). When the lock\n * is contended past `withHeldFileLock`'s bounded wait, the added hashes are held\n * only in this process's memory; relying on \"some later batch save\" is not\n * durable (the run may be the last before idle/shutdown), and a long-lived peer\n * that already built its authoritative in-memory index will not see the write.\n * So we schedule a bounded, exponentially-backed-off background retry that keeps\n * re-attempting the locked publish until it lands. Bounded because the durable\n * fact `.md` is already on disk and a process restart rebuilds the index from\n * the corpus regardless — exhausting retries degrades to that rebuild safety\n * net, never to data loss or a fatal append failure.\n */\nconst CONTENT_HASH_INDEX_RETRY_MAX_ATTEMPTS = 5;\n/** Base backoff (ms) before the first deferred reconcile-save retry; doubles each attempt. */\nconst CONTENT_HASH_INDEX_RETRY_BASE_MS = 500;\n/** Ceiling (ms) for the exponential reconcile-save retry backoff. */\nexport const CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS = 5_000;\n\n/**\n * Tuning for {@link ContentHashIndex} cross-process lock waits and the\n * deferred reconcile-save durable retry (issue #1909 / PR #2016). All optional;\n * production uses the module defaults. Tests inject small values so the\n * lock-timeout + retry path is exercised deterministically without real\n * multi-second waits.\n */\nexport interface ContentHashIndexLockOptions {\n /** Bounded wait to acquire the per-file advisory lock; passed to `withHeldFileLock` (default 5000ms). */\n readonly maxWaitMs?: number;\n /** Poll interval while waiting for a busy lock (default 50ms). */\n readonly pollMs?: number;\n /** Base backoff before the first deferred-save retry (default 500ms). */\n readonly retryBaseMs?: number;\n /** Max deferred-save retries before falling back to the corpus-rebuild safety net (default 5). */\n readonly retryMaxAttempts?: number;\n /**\n * Tombstone-blocked capture write-lock backoff (COS-392). These do not\n * change the fact-hash index lock. Omitted fields use the 50ms / 2s / 60s\n * defaults. Converge copies its config here before replicating a file.\n */\n readonly captureWriteLockInitialDelayMs?: number;\n readonly captureWriteLockMaxDelayMs?: number;\n readonly captureWriteLockDeadlineMs?: number;\n /** Apply abort signal. Checked before each probe, during backoff, and before the write. */\n readonly captureWriteLockSignal?: AbortSignal;\n}\n\n/**\n * Thrown when the fact-hash index cannot be made authoritative — the\n * cross-process rebuild lock could not be acquired within the bounded retry\n * budget (PR #2016). Callers that require an authoritative dedup answer surface\n * this instead of silently trusting a stale loaded snapshot.\n */\nexport class FactHashIndexNotAuthoritativeError extends Error {\n constructor(stateDir: string) {\n super(\n `fact-hash index is not authoritative: the cross-process rebuild lock for ${stateDir} could not be acquired within the bounded retry budget`\n );\n this.name = \"FactHashIndexNotAuthoritativeError\";\n }\n}\n\n/**\n * One dedup lookup: whether the shared index holds the hash, and whether that\n * answer can be trusted as a clean MISS. Both fields describe the same instant.\n */\nexport interface ContentHashLookup {\n readonly present: boolean;\n readonly authoritative: boolean;\n}\n\ntype DiskFingerprint = {\n mtimeMs: number;\n size: number;\n dev: number;\n ino: number;\n ctimeMs: number;\n birthtimeMs: number;\n};\nexport interface ContentHashPathEntry {\n readonly path: string;\n readonly contentHash: string;\n}\n\n/**\n * Content-hash dedup index for facts.\n * Normalizes content (lowercase, strip punctuation, collapse whitespace),\n * computes SHA-256, and stores hashes in a line-delimited file.\n * Prevents writing semantically identical facts.\n */\nexport class ContentHashIndex {\n private hashes: Set<string> = new Set();\n /**\n * Fact-ONLY hash partition (issue #2474). `hashes` above is category-blind\n * (every write category registers there); this subset carries only\n * `category === \"fact\"` hashes so fact-only membership checks\n * (`hasFact*`) can never be satisfied by an unrelated decision/preference\n * sharing the same normalized body. In-memory only — never serialized to\n * fact-hashes.txt; `clear()` drops it and every authoritative rebuild\n * repopulates it via `addFactByHash`. Subset invariant: `factHashes ⊆\n * hashes` — `addFact*` registers in both, and the removal sites that drop\n * a shared hash also drop the partition entry (removal stays caller-paired\n * because shared-index removal is category-blind: archiving a non-fact\n * must not evict an active fact's fact-only membership).\n */\n private factHashes: Set<string> = new Set();\n private dirty = false;\n /** True when load() found the pre-Unicode, markerless on-disk format. */\n private formatMigrationRequired = false;\n /**\n * Hashes explicitly removed since the last successful save (issue #1909 review\n * round 8 thread 3). Tracked separately so `saveMergingWithDisk` can be\n * removal-AWARE: under the file lock it reads the latest on-disk set, DROPS\n * these, then unions our additions — so a removal batch cannot resurrect a\n * hash (union alone would) and cannot clobber a concurrent extraction's\n * appended hash (blind overwrite would). Consumed + cleared by every save.\n */\n private removed: Set<string> = new Set();\n /**\n * Hashes added by THIS instance since the last successful save (issue #1909\n * review round 9 finding 3). Tracked separately from the loaded snapshot so a\n * reconciling save publishes `(on-disk \\ removed) ∪ added` — it re-applies only\n * OUR additions, never the stale loaded entries. Without this, seeding the\n * merge from `this.hashes` (loaded ∪ added) would resurrect a hash a peer\n * removed on disk while this instance held a stale snapshot.\n */\n private added: Set<string> = new Set();\n private readonly filePath: string;\n private readonly secureStoreKeyProvider: () => Buffer | null;\n private readonly secureStoreWriteKeyProvider: () => Buffer | null;\n private readonly memoryDir: string;\n private readonly lockOptions: ContentHashIndexLockOptions;\n /**\n * Deferred reconcile-save durable-retry state (issue #1909 / PR #2016). At\n * most ONE retry is ever armed (`reconcileRetryTimer` is the reentrancy guard)\n * so successive timed-out saves cannot stack duplicate/parallel retries.\n * `reconcileRetryBarrier` resolves when the chain settles (published or gave\n * up) — awaited by tests and any caller wanting the eventual result.\n */\n private reconcileRetryTimer: NodeJS.Timeout | null = null;\n private reconcileRetryAttempts = 0;\n private reconcileRetryBarrier: Promise<void> | null = null;\n private reconcileRetryResolve: (() => void) | null = null;\n /**\n * On-disk fingerprint captured at the last point THIS instance's in-memory\n * set matched disk — after `load()`, `save()`, or a reconcile publish. A peer\n * process that advances the durable index changes its file generation even\n * when byte size and mtime collide, so the freshness check remains safe\n * without an O(file-size) read on the hot path.\n */\n private lastSyncedFingerprint: DiskFingerprint | null = null;\n constructor(\n stateDir: string,\n secureStoreKeyProvider: () => Buffer | null = () => null,\n secureStoreWriteKeyProvider: () => Buffer | null = secureStoreKeyProvider,\n memoryDir: string = path.dirname(stateDir),\n lockOptions: ContentHashIndexLockOptions = {}\n ) {\n this.filePath = path.join(stateDir, \"fact-hashes.txt\");\n this.secureStoreKeyProvider = secureStoreKeyProvider;\n this.secureStoreWriteKeyProvider = secureStoreWriteKeyProvider;\n this.memoryDir = memoryDir;\n this.lockOptions = lockOptions;\n }\n /** Whether a caller with an authoritative corpus must rebuild this legacy index. */\n get requiresFormatMigration(): boolean {\n return this.formatMigrationRequired;\n }\n\n /** Load existing hashes from disk. Safe to call multiple times. */\n async load(): Promise<void> {\n try {\n const raw = await readMaybeEncryptedFile(this.filePath, this.secureStoreKeyProvider(), this.memoryDir);\n const parsed = parseHashIndex(raw);\n this.formatMigrationRequired = !parsed.versioned;\n if (!parsed.versioned) {\n // Hashes from the pre-NFC format are unsafe after normalization changes.\n // Ignore them until an authoritative corpus rebuild publishes v2.\n this.hashes.clear();\n log.warn(`content-hash index: ignored legacy unversioned index at ${this.filePath}`);\n } else {\n for (const hash of parsed.hashes) this.hashes.add(hash);\n }\n log.debug(`content-hash index: loaded ${this.hashes.size} hashes`);\n } catch (err) {\n if (err instanceof SecureStoreLockedError) throw err;\n if (!isErrnoCode(err, \"ENOENT\")) throw err;\n log.debug(\"content-hash index: no existing index — starting fresh\");\n }\n // The loaded set now reflects the on-disk file — baseline the fingerprint so\n // a later peer write is detectable (PR #2016 review).\n await this.captureSyncedFingerprint();\n }\n\n /** Cheap `stat` of the durable index file; null when it does not exist yet. */\n private async statIndexFile(): Promise<DiskFingerprint | null> {\n try {\n const s = await stat(this.filePath);\n return {\n mtimeMs: s.mtimeMs,\n size: s.size,\n dev: s.dev,\n ino: s.ino,\n ctimeMs: s.ctimeMs,\n birthtimeMs: s.birthtimeMs,\n };\n } catch (err) {\n if (isErrnoCode(err, \"ENOENT\")) return null;\n throw err;\n }\n }\n\n /** Record the current on-disk fingerprint as this instance's synced baseline. */\n private async captureSyncedFingerprint(): Promise<void> {\n try {\n this.lastSyncedFingerprint = await this.statIndexFile();\n } catch (err) {\n // A stat failure other than ENOENT leaves no baseline; the next freshness\n // check reports non-current and the caller confirms against the corpus.\n this.lastSyncedFingerprint = null;\n log.debug(`content-hash index: could not fingerprint ${this.filePath}: ${err}`);\n }\n }\n\n /**\n * True when the durable index file is unchanged since this instance last\n * synced it (load / save / reconcile). False when a peer advanced it, when\n * the file appeared or vanished, or when freshness cannot be established\n * (stat error) — the caller then treats the in-memory snapshot as\n * non-authoritative and confirms a dedup miss against the durable corpus.\n * The stat includes file identity and generation metadata so same-size,\n * same-mtime atomic replacements are detected without reading the file body.\n */\n async isDiskFingerprintCurrent(): Promise<boolean> {\n let current: DiskFingerprint | null;\n try {\n current = await this.statIndexFile();\n } catch {\n return false;\n }\n const last = this.lastSyncedFingerprint;\n if (last === null || current === null) {\n return last === null && current === null;\n }\n return (\n current.mtimeMs === last.mtimeMs &&\n current.size === last.size &&\n current.dev === last.dev &&\n current.ino === last.ino &&\n current.ctimeMs === last.ctimeMs &&\n current.birthtimeMs === last.birthtimeMs\n );\n }\n\n /** Check if content already exists in the index. */\n has(content: string): boolean {\n return this.hashes.has(ContentHashIndex.computeHash(content));\n }\n\n /** Add content hash to the index. */\n add(content: string): void {\n const hash = ContentHashIndex.computeHash(content);\n // A re-add supersedes a pending removal of the same hash (round 8 thread 3).\n this.removed.delete(hash);\n // Record OUR durable delta even when the local snapshot already holds the\n // hash (PR #2016 thread SD-nG): local membership can be STALE — a peer\n // removed this hash on disk while this instance kept it in memory. Skipping\n // `added`/`dirty` on that path let the reconciling save compute\n // (on-disk \\ removed) ∪ added and silently drop the reintroduced hash, so\n // the peer never saw it again. Set semantics keep this idempotent (no\n // duplicate entries) and the `removed.delete` above preserves remove/add\n // ordering.\n this.hashes.add(hash);\n this.added.add(hash);\n this.dirty = true;\n }\n\n get size(): number {\n return this.hashes.size;\n }\n\n /** Clear all loaded hashes so the next save rewrites the index from scratch. */\n clear(): void {\n if (this.hashes.size > 0) {\n this.hashes.clear();\n }\n // The fact-only partition is rebuilt with the shared set on every\n // authoritative corpus rebuild, so it clears here too (issue #2474).\n this.factHashes.clear();\n // A full clear is always paired with a plain overwrite save() (rebuild);\n // pending per-hash add/remove deltas are subsumed by writing the rebuilt set.\n // Cancel any armed deferred-reconcile retry (PR #2016) so it cannot fire in\n // the clear→rebuild→save window and union the pre-clear on-disk rows back.\n this.added.clear();\n this.removed.clear();\n this.cancelReconcileRetry();\n this.dirty = true;\n }\n\n /** Persist index to disk if changed (plain whole-file overwrite). */\n async save(): Promise<void> {\n if (!this.dirty) return;\n await mkdir(path.dirname(this.filePath), { recursive: true });\n // Snapshot the deltas + overwrite body THIS save publishes BEFORE the write\n // yields the event loop. `writeMaybeEncryptedFile` materializes `serialized`\n // eagerly, then the disk await lets a concurrent same-process add()/remove()\n // land a late delta (PR #2016 thread PRRT_kwDORJXyws6SEBri). Clearing the\n // LIVE added/removed sets afterward — as the previous unconditional\n // clear()+`dirty = false` did — would drop that late hash from the index AND\n // leave no retry, so peers never see it until a corpus rebuild. Consume only\n // what we published; late deltas stay pending. Mirrors saveMergingWithDisk.\n const publishedAdded = new Set<string>(this.added);\n const publishedRemoved = new Set<string>(this.removed);\n const serialized = serializeHashIndex(this.hashes);\n await writeMaybeEncryptedFile(this.filePath, serialized, this.secureStoreWriteKeyProvider(), {}, this.memoryDir);\n this.formatMigrationRequired = false;\n // Consume ONLY the deltas this overwrite published; anything that arrived\n // during the awaits above remains pending for the next save.\n for (const h of publishedAdded) this.added.delete(h);\n for (const h of publishedRemoved) this.removed.delete(h);\n // Honest dirty state: true iff late deltas remain unpersisted.\n this.dirty = this.added.size > 0 || this.removed.size > 0;\n // This overwrite is now the on-disk state — re-baseline the freshness\n // fingerprint so our own write is not later mistaken for a peer's (PR #2016).\n await this.captureSyncedFingerprint();\n if (this.dirty) {\n // Late deltas landed mid-save: arm a bounded durable retry so they reach\n // disk even without a further batch save — never a silent in-memory-only\n // hash. The locked reconcile republishes only OUR delta onto the overwrite.\n this.scheduleReconcileRetry();\n } else {\n // Clean overwrite supersedes any pending deferred reconcile work.\n this.cancelReconcileRetry();\n }\n log.debug(`content-hash index: saved ${this.hashes.size} hashes`);\n }\n\n /**\n * Persist the index by RECONCILING with the latest on-disk state under a\n * cross-process lock (issue #1909). Used by the extraction-persist batch save\n * for BOTH append and removal batches, so appends and removals to the same\n * index serialize against each other:\n * final = (on-disk \\ this.removed) ∪ this.added\n * Note it re-applies only OUR local additions (`added`), NOT the loaded\n * snapshot (review round 9 finding 3): seeding from `this.hashes` would\n * resurrect a hash a peer removed on disk while we held a stale snapshot. This\n * preserves a concurrent writer's appended hashes (a blind overwrite would\n * drop them), never resurrects a removed hash, and never loses our additions.\n *\n * Hardening:\n * - Dirty short-circuit (round 7 finding 3): when this instance neither added\n * nor removed anything this run, skip the O(file) read+rewrite entirely.\n * - Cross-process lock (round 7 finding 2): serialize the read→reconcile→write\n * window on a per-file advisory lock. If the lock is NOT acquired (timeout),\n * do NOT publish (round 9 finding 2): keep `dirty` set AND schedule a bounded\n * durable background retry (PR #2016) that keeps re-attempting the locked\n * publish until the deferred additions land — never an unlocked write that\n * could clobber a peer, and never a silent \"batch save complete\" that leaves\n * the addition in this process's memory only.\n */\n async saveMergingWithDisk(): Promise<void> {\n if (!this.dirty) return;\n // The advisory lock file lives beside the index — ensure the dir exists\n // before acquiring it (the index dir may not exist on a first write).\n await mkdir(path.dirname(this.filePath), { recursive: true });\n const publish = async (): Promise<void> => {\n // Snapshot the deltas this publish is responsible for. The read+rewrite\n // below yields the event loop, so a concurrent add()/remove() can land\n // new deltas while we await disk (PR #2016 thread SD7Tj). Consuming the\n // LIVE sets afterward would drop those late deltas AND clear `dirty`,\n // stranding them in no save at all. We consume only what we published;\n // anything that arrived meanwhile stays pending for the next save.\n const publishedAdded = new Set<string>(this.added);\n const publishedRemoved = new Set<string>(this.removed);\n const merged = new Set<string>(publishedAdded);\n try {\n const raw = await readMaybeEncryptedFile(this.filePath, this.secureStoreKeyProvider(), this.memoryDir);\n const parsed = parseHashIndex(raw);\n if (parsed.versioned) {\n for (const hash of parsed.hashes) {\n // Keep prior/concurrent on-disk hashes EXCEPT the ones we removed.\n if (!publishedRemoved.has(hash)) merged.add(hash);\n }\n } else {\n // Do not carry hashes from the pre-NFC format into a v2 write.\n log.warn(`content-hash index: discarded legacy unversioned entries from ${this.filePath}`);\n }\n } catch (err) {\n if (err instanceof SecureStoreLockedError) throw err;\n if (!isErrnoCode(err, \"ENOENT\")) throw err;\n // ENOENT → no on-disk index yet; write our additions as-is.\n }\n await mkdir(path.dirname(this.filePath), { recursive: true });\n await writeMaybeEncryptedFile(\n this.filePath,\n serializeHashIndex(merged),\n this.secureStoreWriteKeyProvider(),\n {},\n this.memoryDir\n );\n this.formatMigrationRequired = false;\n // Consume ONLY the deltas we just published; deltas that arrived during\n // the awaits above remain pending.\n for (const h of publishedAdded) this.added.delete(h);\n for (const h of publishedRemoved) this.removed.delete(h);\n // Fold any still-pending deltas onto the reconciled set so the in-memory\n // view stays consistent with what the next save will publish.\n for (const h of this.added) merged.add(h);\n for (const h of this.removed) merged.delete(h);\n this.hashes = merged;\n // Honest dirty state: true iff late deltas remain to be persisted. A\n // subsequent batch save or the shutdown flush drains them.\n this.dirty = this.added.size > 0 || this.removed.size > 0;\n // The reconciled set is now the on-disk state — re-baseline the freshness\n // fingerprint (captured under the held lock) so our own publish is not\n // later mistaken for a peer's advance (PR #2016 review).\n await this.captureSyncedFingerprint();\n };\n await withHeldFileLock(\n `${this.filePath}.lock`,\n {\n staleMs: CONTENT_HASH_INDEX_LOCK_STALE_MS,\n ...(this.lockOptions.maxWaitMs !== undefined ? { maxWaitMs: this.lockOptions.maxWaitMs } : {}),\n ...(this.lockOptions.pollMs !== undefined ? { pollMs: this.lockOptions.pollMs } : {}),\n },\n async (acquired) => {\n if (!acquired) {\n // Never publish unlocked (round 9 finding 2): keep dirty AND schedule a\n // durable background retry (PR #2016). Relying on an unguaranteed later\n // batch save silently left the addition in this process's memory only;\n // the retry keeps re-attempting the locked publish until the addition\n // lands on disk.\n log.warn(\n `content-hash index: lock not acquired for ${this.filePath}; deferring reconcile save (dirty retained, scheduling durable retry)`\n );\n this.scheduleReconcileRetry();\n return;\n }\n await publish();\n if (this.dirty) {\n // A concurrent add()/remove() landed during publish's disk awaits, so\n // deltas remain unpersisted (PR #2016 thread SD7Tj). Arm a bounded\n // durable retry so they reach disk even if no further batch save comes\n // — never leave a settled barrier over pending work.\n this.scheduleReconcileRetry();\n } else {\n // Published cleanly: any pending deferred retry is now satisfied.\n this.cancelReconcileRetry();\n }\n }\n );\n log.debug(`content-hash index: reconcile-saved ${this.hashes.size} hashes`);\n }\n\n /**\n * Rebuild-and-publish the index from an authoritative source (the durable\n * corpus) under the SAME per-file cross-process lock the reconciling\n * append/removal saves (`saveMergingWithDisk`) use (issue #1909 / PR #2016).\n * The `populate` callback runs WHILE the lock is held and MUST repopulate this\n * index from the corpus (`clear()` then `addByHash(...)` for every corpus\n * hash); the rebuilt set is then published with a plain overwrite that is now\n * serialized against every locked writer.\n *\n * This closes the finding where the previous unlocked clear→scan→save() could\n * overwrite a peer's newer lock-merged or deferred additions: a locked writer\n * that committed BEFORE the rebuild acquired the lock has its memory `.md` on\n * disk and is re-scanned; one that commits AFTER the rebuild releases\n * reconciles its additions on top of the freshly-published set. No union with\n * the raw on-disk file is performed, so the rebuild keeps its\n * garbage-collection property (orphaned hashes for deleted memories drop).\n *\n * Lock-acquisition failure is explicit and non-destructive: `populate` is NOT\n * run and nothing is written (never an unlocked overwrite). Returns true when\n * the rebuild was published under the lock, false when the lock timed out —\n * the caller keeps the index non-authoritative and retries on next use. Uses\n * the same non-reentrant file lock as the reconcile path, so it can never\n * deadlock with the deferred reconcile-retry: a concurrent locked writer (or a\n * fired retry) simply yields `acquired=false` after the bounded wait.\n */\n async rebuildUnderLock(populate: () => Promise<void>): Promise<boolean> {\n // The advisory lock file lives beside the index — ensure the dir exists\n // before acquiring it (the index dir may not exist on a first rebuild).\n await mkdir(path.dirname(this.filePath), { recursive: true });\n let published = false;\n await withHeldFileLock(\n `${this.filePath}.lock`,\n {\n staleMs: CONTENT_HASH_INDEX_LOCK_STALE_MS,\n ...(this.lockOptions.maxWaitMs !== undefined ? { maxWaitMs: this.lockOptions.maxWaitMs } : {}),\n ...(this.lockOptions.pollMs !== undefined ? { pollMs: this.lockOptions.pollMs } : {}),\n },\n async (acquired) => {\n if (!acquired) {\n // Never publish unlocked: a concurrent locked writer holds the lock,\n // so overwriting now could clobber its addition. Leave the index as-is\n // and let the caller retry the authoritative rebuild on next use.\n log.warn(\n `content-hash index: lock not acquired for ${this.filePath}; deferring authoritative rebuild (index left non-authoritative, retried on next use)`\n );\n return;\n }\n await populate();\n // Publish under the held lock. save() takes no lock (no re-entrancy) and\n // is a full overwrite that supersedes any pending add/remove deltas.\n await this.save();\n published = true;\n }\n );\n return published;\n }\n\n /**\n * True while a background durable reconcile-save retry is armed (PR #2016).\n * Test/introspection hook.\n */\n get hasPendingReconcileRetry(): boolean {\n return this.reconcileRetryTimer !== null;\n }\n\n /** True while this index has in-memory changes not yet published to disk. */\n get hasPendingChanges(): boolean {\n return this.dirty;\n }\n\n /**\n * Resolve once any armed background reconcile-save retry chain has settled —\n * either it published the deferred additions, or it exhausted its bounded\n * attempts and fell back to the corpus-rebuild safety net. Resolves\n * immediately when nothing is armed. Lets a caller (or a test) await the\n * eventual durability outcome of a lock-timed-out save.\n */\n async whenReconcileRetrySettled(): Promise<void> {\n await (this.reconcileRetryBarrier ?? Promise.resolve());\n }\n\n /**\n * Drive any deferred lock-timeout reconcile save to completion INLINE at a\n * lifecycle boundary (PR #2016 finding 3). The background retry timer is\n * `unref`'d so it never keeps a long-lived daemon alive — but a short-lived\n * writer (a one-shot CLI) can exit before it fires, leaving a durable fact\n * `.md` whose hash never reached `fact-hashes.txt` for peers that already\n * built their in-memory index. `orchestrator.destroy()` calls this so the\n * addition publishes before the process exits; long-lived hosts only reach it\n * at their own shutdown, so their in-flight retries keep running in the\n * background until then. Bounded by the same attempt ceiling as the background\n * retry; a permanently contended lock falls back to the corpus-rebuild-on-\n * restart safety net (the fact `.md` is already durable). No deadlock: each\n * attempt is the same non-reentrant, bounded-wait file lock.\n */\n async flushReconcileRetry(): Promise<void> {\n if (!this.dirty) return;\n const maxAttempts = this.lockOptions.retryMaxAttempts ?? CONTENT_HASH_INDEX_RETRY_MAX_ATTEMPTS;\n const baseMs = this.lockOptions.retryBaseMs ?? CONTENT_HASH_INDEX_RETRY_BASE_MS;\n for (let attempt = 0; this.dirty && attempt < maxAttempts; attempt += 1) {\n // Cancel the unref'd background timer — we drive the locked publish inline.\n if (this.reconcileRetryTimer) {\n clearTimeout(this.reconcileRetryTimer);\n this.reconcileRetryTimer = null;\n }\n await this.saveMergingWithDisk();\n if (!this.dirty) break;\n if (attempt < maxAttempts - 1) {\n const wait = Math.min(baseMs * 2 ** attempt, CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS);\n await new Promise<void>((resolve) => setTimeout(resolve, wait));\n }\n }\n // Clear any timer the final saveMergingWithDisk armed and settle the barrier\n // so awaiters unblock — the bounded inline drain has done its best.\n if (this.reconcileRetryTimer) {\n clearTimeout(this.reconcileRetryTimer);\n this.reconcileRetryTimer = null;\n }\n this.reconcileRetryAttempts = 0;\n this.settleReconcileRetry();\n }\n\n /**\n * Schedule ONE bounded, exponentially-backed-off background retry of the\n * deferred reconcile save (PR #2016). Reentrant-safe: an already-armed timer\n * short-circuits, so successive timed-out saves never stack duplicate retries.\n * When attempts are exhausted the chain settles without publishing — the\n * durable fact `.md` is on disk and a process restart rebuilds the index from\n * the corpus, so this degrades to that safety net, never a fatal failure.\n */\n private scheduleReconcileRetry(): void {\n if (this.reconcileRetryTimer) return; // one retry in flight — no duplicate/reentrant retries\n const maxAttempts = this.lockOptions.retryMaxAttempts ?? CONTENT_HASH_INDEX_RETRY_MAX_ATTEMPTS;\n if (this.reconcileRetryAttempts >= maxAttempts) {\n log.warn(\n `content-hash index: exhausted ${maxAttempts} deferred reconcile-save retries for ${this.filePath}; ` +\n `${this.added.size} addition(s) remain dirty in-memory (a process restart rebuilds the index from the durable corpus)`\n );\n this.reconcileRetryAttempts = 0;\n this.settleReconcileRetry();\n return;\n }\n if (!this.reconcileRetryBarrier) {\n this.reconcileRetryBarrier = new Promise<void>((resolve) => {\n this.reconcileRetryResolve = resolve;\n });\n }\n const attempt = this.reconcileRetryAttempts + 1;\n const baseMs = this.lockOptions.retryBaseMs ?? CONTENT_HASH_INDEX_RETRY_BASE_MS;\n const delay = Math.min(baseMs * 2 ** (attempt - 1), CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS);\n this.reconcileRetryTimer = setTimeout(() => {\n this.reconcileRetryTimer = null;\n this.reconcileRetryAttempts = attempt;\n void this.saveMergingWithDisk()\n .catch((err) => {\n // A publish-time I/O/encryption error (not a lock timeout) is best-effort:\n // log and let the corpus-rebuild safety net cover it rather than loop.\n log.warn(`content-hash index: deferred reconcile-save retry failed for ${this.filePath}: ${err}`);\n })\n .finally(() => {\n // If the attempt neither published (dirty cleared → cancelReconcileRetry)\n // nor re-armed a further retry, the chain has ended — settle the barrier.\n if (this.dirty && !this.reconcileRetryTimer) this.settleReconcileRetry();\n });\n }, delay);\n this.reconcileRetryTimer.unref?.();\n }\n\n /** Cancel any armed reconcile-save retry and settle the barrier (a publish landed). */\n private cancelReconcileRetry(): void {\n if (this.reconcileRetryTimer) {\n clearTimeout(this.reconcileRetryTimer);\n this.reconcileRetryTimer = null;\n }\n this.reconcileRetryAttempts = 0;\n this.settleReconcileRetry();\n }\n\n /** Resolve and clear the retry barrier if one is outstanding. */\n private settleReconcileRetry(): void {\n const resolve = this.reconcileRetryResolve;\n this.reconcileRetryBarrier = null;\n this.reconcileRetryResolve = null;\n resolve?.();\n }\n\n /** Remove a hash from the index (used when archiving/deleting). */\n remove(content: string): void {\n const hash = ContentHashIndex.computeHash(content);\n // Record the removal (round 8 thread 3) even when the hash is not in our\n // in-memory set — a reconciling save must still drop it from the latest\n // on-disk state. A removal supersedes a pending add. Always mark dirty.\n this.added.delete(hash);\n this.removed.add(hash);\n this.hashes.delete(hash);\n this.dirty = true;\n }\n\n /**\n * Remove a pre-computed SHA-256 hash directly from the index without\n * re-hashing. Use this when the caller already holds the stored hash\n * (e.g. `memory.frontmatter.contentHash`) to avoid the double-hash bug\n * where `remove(hash)` would compute `hash(hash)` and never match the\n * entry.\n */\n removeByHash(hash: string): void {\n this.added.delete(hash);\n this.removed.add(hash);\n this.hashes.delete(hash);\n this.dirty = true;\n }\n\n /**\n * Add a pre-computed SHA-256 hash directly to the index without re-hashing.\n * Use this when the caller already holds the stored hash\n * (e.g. `memory.frontmatter.contentHash`) so that the index records the raw\n * content hash rather than re-hashing the citation-annotated body.\n *\n * @internal Only called from `StorageManager.ensureFactHashIndexAuthoritative`.\n * Not part of the public API — prefer `add(content)` for external callers.\n */\n addByHash(hash: string): void {\n // A re-add supersedes a pending removal of the same hash.\n this.removed.delete(hash);\n // Record OUR durable delta even when the local snapshot already holds the\n // hash (PR #2016 thread PRRT_kwDORJXyws6SEHve). Outside a rebuild\n // (StorageManager.addActiveFactContentHash on reactivation) local\n // membership can be STALE: a peer removed this hash on disk while this\n // instance kept it in memory. Skipping added/dirty on that path let the\n // reconciling save compute (on-disk \\ removed) ∪ added and silently drop\n // the reintroduced hash, so the peer never saw it again. Set semantics keep\n // this idempotent (no duplicate entries) and the removed.delete above\n // preserves remove/add ordering. Mirrors add().\n this.hashes.add(hash);\n this.added.add(hash);\n this.dirty = true;\n }\n\n // ── Fact-only partition (issue #2474) ─────────────────────────────────\n // Folded here from StorageManager's parallel `factOnlyHashes` set, which had\n // to be kept in lockstep by hand on every add/remove/rebuild site. The\n // partition lives on the index instance, so every path that mutates the\n // shared set and needs fact-only truth mutates it in the same breath.\n\n /** Register fact content in BOTH the shared index and the fact-only partition. */\n addFact(content: string): void {\n this.addFactByHash(ContentHashIndex.computeHash(content));\n }\n\n /** Register a pre-computed fact hash in both the shared index and the fact-only partition. */\n addFactByHash(hash: string): void {\n this.addByHash(hash);\n this.factHashes.add(hash);\n }\n\n /** Fact-ONLY membership: has this content been registered as a fact? */\n hasFact(content: string): boolean {\n return this.factHashes.has(ContentHashIndex.computeHash(content));\n }\n\n /** Fact-ONLY membership for a pre-computed SHA-256 hash. */\n hasFactByHash(hash: string): boolean {\n return this.factHashes.has(hash);\n }\n\n /**\n * Drop fact-only partition membership WITHOUT touching the shared index.\n * The shared removal stays caller-owned: a shared hash is dropped only when\n * no other active memory holds it, while fact-only membership follows the\n * fact's own lifecycle (supersede/archive/promotion).\n */\n removeFactByHash(hash: string): void {\n this.factHashes.delete(hash);\n }\n\n /**\n * Resolve a pre-computed semantic hash to one deterministic corpus path.\n *\n * This lookup is separate from durable hash membership: `this.hashes` stays\n * one hash per line and `has(content)` keeps its existing raw-content contract.\n * Reconciliation supplies short-lived manifest rows where hash and path coexist.\n */\n static resolvePathByHash(hash: string, entries: Iterable<ContentHashPathEntry>): string | undefined {\n const canonicalHash = hash.toLowerCase();\n if (!/^[a-f0-9]{64}$/.test(canonicalHash)) return undefined;\n let canonicalPath: string | undefined;\n for (const entry of entries) {\n if (entry.contentHash.toLowerCase() !== canonicalHash || entry.path.length === 0) continue;\n if (canonicalPath === undefined || entry.path < canonicalPath) canonicalPath = entry.path;\n }\n return canonicalPath;\n }\n\n /** Normalize content (delegates to content-hash.ts for a single source of truth). */\n static normalizeContent(content: string): string {\n return normalizeContent(content);\n }\n\n /** Compute SHA-256 hash (delegates to content-hash.ts for a single source of truth). */\n static computeHash(content: string): string {\n return computeContentHash(content);\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { BoundedJsonlStateConfig } from \"./bounded-jsonl-state.js\";\nimport type { ProjectionRebuildScheduleConfig } from \"./projection-rebuild-config.js\";\nimport type { MeetingsConfig } from \"./meetings/types.js\";\nimport type { LocationConfig } from \"./location/types.js\";\nimport type { OkfConfig } from \"./okf/config.js\";\nimport type { ActivityConfig } from \"./activity/types.js\";\nimport type { WearablesConfig } from \"./wearables/types.js\";\nimport type { ExtractionSpanConfig, ExtractedFactSpanRef } from \"./extraction-span-config.js\";\nimport type { ExtractionLivenessConfig } from \"./extraction-liveness.js\";\nimport type { ReplicaPeersConfig } from \"./replica-peers-config.js\";\nimport type { ExternalWikiRoot } from \"./external-wiki-config.js\";\nimport type { StateViewResult } from \"./recall-state-view.js\";\nimport type { ContradictionLocalizationConfig, ContradictionScanConfig } from \"./contradiction-config.js\";\nimport type { GraphPathScoringConfig } from \"./graph-path-scoring-config.js\"; export type { GraphPathScoringConfig };\nimport type { OutcomeBoostConfig } from \"./outcome-boost-config.js\"; export type { OutcomeBoostConfig };\nexport type { ContradictionLocalizationConfig, ContradictionScanConfig } from \"./contradiction-config.js\";\nexport type { BackgroundGenerationConfig } from \"./background-generation-config.js\";\nimport type { BackgroundGenerationConfig } from \"./background-generation-config.js\";\nimport type {\n DriftDetectionSettings,\n MemoryDriftProvenance,\n RecallDriftAnnotation,\n} from \"./preferences/drift-types.js\";\nimport type { DeepRecallSettings } from \"./deep-recall-config.js\";\nimport type { RecallNavigationSettings } from \"./recall-navigation-config.js\";\nimport type { RecognitionTierSettings } from \"./recall-recognition-tier.js\";\nimport type { SeedGraduationSettings } from \"./lifecycle/seed-graduation-config-types.js\";\nimport type { ProceduralMaintenanceConfig } from \"./procedural/maintenance-config.js\";\nimport type { SkillProjectionConfig } from \"./procedural/skill-projection.js\";\nimport type { SessionExperienceConfig } from \"./experience/session-experience-config.js\";\nimport type { ActionGateConfig } from \"./coding/action-gate.js\";\nimport type { ActiveContextConfigFields } from \"./active-context-config.js\";\nimport type { LocalLlmConfig } from \"./local-llm-config.js\";\nimport type { SecurityConfig, OriginMetadata } from \"./security/types.js\";\nexport type MemorySubject = \"user\" | \"agent\";\nexport type SubjectGuardMode = \"off\" | \"warn\" | \"enforce\";\nexport type ReasoningEffort = \"none\" | \"low\" | \"medium\" | \"high\";\nexport type TriggerMode = \"smart\" | \"every_n\" | \"time_based\";\nexport type ConvergeConflictPolicy = \"newest-wins\" | \"manual\";\nexport interface ConvergeConfig {\n conflictPolicy: ConvergeConflictPolicy;\n /** Per-request peer HTTP timeout in ms (plan/apply/watch). Default 30000; clamped to <= 3600000. */\n peerRequestTimeoutMs?: number;\n /**\n * Tombstone-blocked capture write-lock backoff for converge apply/watch.\n * Defaults: 50ms initial (jittered to 25–50), 2000ms cap per wait, 60000ms\n * deadline. Positive integers, clamped to <= 3600000. An inverted schedule\n * (initial > max, or max > deadline) is rejected.\n */\n captureWriteLockInitialDelayMs?: number;\n captureWriteLockMaxDelayMs?: number;\n captureWriteLockDeadlineMs?: number;\n}\nexport type SignalLevel = \"none\" | \"low\" | \"medium\" | \"high\";\nexport type MemoryCategory =\n | \"fact\"\n | \"preference\"\n | \"correction\"\n | \"entity\"\n | \"decision\"\n | \"relationship\"\n | \"principle\"\n | \"commitment\"\n | \"moment\"\n | \"skill\"\n | \"rule\"\n | \"procedure\"\n | \"reasoning_trace\";\nexport type ConsolidationAction = \"ADD\" | \"MERGE\" | \"UPDATE\" | \"INVALIDATE\" | \"SKIP\";\nexport type ConfidenceTier = \"explicit\" | \"implied\" | \"inferred\" | \"speculative\";\nexport type PrincipalFromSessionKeyMode = \"map\" | \"prefix\" | \"regex\";\nexport type RecallPlanMode = \"no_recall\" | \"minimal\" | \"full\" | \"graph_mode\";\nexport type CronRecallMode = \"all\" | \"none\" | \"allowlist\";\nexport type CronConversationRecallMode = \"auto\" | \"always\" | \"never\";\nexport type IdentityInjectionMode = \"recovery_only\" | \"minimal\" | \"full\";\nexport type CaptureMode = \"implicit\" | \"explicit\" | \"hybrid\";\nexport type MemoryOsPresetName = \"conservative\" | \"balanced\" | \"research-max\" | \"local-llm-heavy\";\nexport type ExtractionPassSource = \"base\" | \"proactive\";\n/**\n * Scope classification for extracted facts (issue #XXX).\n *\n * - `\"project\"` — one codebase (paths, env configs, deployment, stakeholders);\n * also tool/command or CLI-flag instructions tied to one agent.\n * - `\"global\"` — across projects: framework bugs, library/API behavior,\n * user preferences, coding patterns, infrastructure.\n *\n * Default is `\"project\"` with a coding context active, else `\"global\"`.\n */\nexport type MemoryScope = \"project\" | \"global\";\nexport type SlotMismatchMode = \"error\" | \"warn\" | \"silent\";\nexport type CodexCompactionFlushMode = \"signal\" | \"heuristic\" | \"auto\";\nexport type DreamingNarrativePromptStyle = \"reflective\" | \"diary\" | \"analytical\";\nexport type HeartbeatDetectionMode = \"runtime-signal\" | \"heuristic\" | \"auto\";\nexport type ActiveRecallQueryMode = \"message\" | \"recent\" | \"full\";\nexport type ActiveRecallPromptStyle =\n | \"balanced\"\n | \"strict\"\n | \"contextual\"\n | \"recall-heavy\"\n | \"precision-heavy\"\n | \"preference-only\";\nexport type ActiveRecallThinking = \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\" | \"adaptive\";\nexport type ActiveRecallChatType = \"direct\" | \"group\" | \"channel\";\nexport type ActiveRecallModelFallbackPolicy = \"default-remote\" | \"resolved-only\";\n\n/**\n * Retrieval tier ladder (issue #518). Identifies which tier served a recall\n * result. Ordered top-to-bottom by cost, but routing is not strictly\n * sequential — callers may jump straight to a lower tier when eligibility\n * does not hold.\n */\nexport type RetrievalTier = \"exact-cache\" | \"fuzzy-cache\" | \"direct-answer\" | \"hybrid\" | \"rerank-graph\" | \"agentic\";\n\n/**\n * Per-recall annotation describing which retrieval tier served a result,\n * why that tier was chosen, and what was filtered along the way. Added as\n * part of issue #518 (direct-answer tier + `query --explain`).\n *\n * Not to be confused with the existing `recallExplain` operation\n * (graph-path explanation) — that is a user-invoked RPC; this is a\n * per-result annotation that can be attached to any recall response.\n */\nexport interface RecallTierExplain {\n tier: RetrievalTier;\n tierReason: string;\n filteredBy: string[];\n candidatesConsidered: number;\n latencyMs: number;\n sourceAnchors?: Array<{ path: string; lineRange?: [number, number] }>;\n}\n\n/**\n * Recall disclosure depth (issue #677). Selects how much content each\n * recall result returns:\n *\n * - `\"chunk\"` — semantic chunk excerpt (cheapest; default).\n * - `\"section\"` — full markdown section / memory body (current pre-#677 behavior).\n * - `\"raw\"` — raw transcript / archive excerpts from `lcm/` when present.\n *\n * Disclosure is **orthogonal** to the retrieval-tier ladder\n * (`RetrievalTier` / `RETRIEVAL_TIERS`). The tier ladder controls *which\n * pipeline stage served a result*; disclosure controls *how deep into the\n * underlying memory the result reaches*. A request can mix any retrieval\n * tier with any disclosure depth.\n *\n * Default is `\"chunk\"` when the caller omits the field; this preserves the\n * existing recall behavior because callers that did not request a disclosure\n * level continue to receive the same chunk-shaped previews they always had.\n * Surfaces (CLI / HTTP / MCP) and downstream telemetry are wired in later\n * PRs of #677.\n */\nexport type RecallDisclosure = \"chunk\" | \"section\" | \"raw\";\n\n/**\n * Ordered list of disclosure levels, cheapest to most expensive. Used for\n * validation, escalation policy comparisons, and future telemetry rollups.\n * Treat this as the single source of truth — do not hard-code disclosure\n * strings elsewhere.\n */\nexport const RECALL_DISCLOSURE_LEVELS: readonly RecallDisclosure[] = [\"chunk\", \"section\", \"raw\"] as const;\n\n/**\n * Default disclosure level when a caller omits `disclosure`. Set to `chunk`\n * so callers that did not opt in to deeper disclosure see the same\n * preview-shaped behavior as before #677.\n */\nexport const DEFAULT_RECALL_DISCLOSURE: RecallDisclosure = \"chunk\";\n\nexport function isRecallDisclosure(value: unknown): value is RecallDisclosure {\n return typeof value === \"string\" && (RECALL_DISCLOSURE_LEVELS as readonly string[]).includes(value);\n}\n\nexport interface RecallSectionConfig {\n id: string;\n enabled?: boolean;\n maxChars?: number | null;\n maxHints?: number;\n maxSupportingFacts?: number;\n maxRelatedEntities?: number;\n consolidateTriggerLines?: number;\n consolidateTargetLines?: number;\n maxEntities?: number;\n maxResults?: number;\n recentTurns?: number;\n maxTurns?: number;\n maxTokens?: number;\n lookbackHours?: number;\n maxCount?: number;\n topK?: number;\n timeoutMs?: number;\n maxPatterns?: number;\n maxRubrics?: number;\n forceGeneric?: boolean;\n}\n\nexport interface RecallPipelineConfig {\n recallBudgetChars: number;\n pipeline: RecallSectionConfig[];\n}\n\nexport interface SessionObserverBandConfig {\n maxBytes: number;\n triggerDeltaBytes: number;\n triggerDeltaTokens: number;\n}\n\nexport interface FileHygieneConfig {\n enabled: boolean;\n // Lint (warn before truncation risk)\n lintEnabled: boolean;\n lintBudgetBytes: number;\n lintWarnRatio: number;\n lintPaths: string[];\n // Rotation/splitting\n rotateEnabled: boolean;\n rotateMaxBytes: number;\n rotateKeepTailChars: number;\n rotatePaths: string[];\n archiveDir: string;\n // Cadence\n runMinIntervalMs: number;\n // Optional warnings log (future-proofed)\n warningsLogEnabled: boolean;\n warningsLogPath: string;\n // Optional index file (future-proofed)\n indexEnabled: boolean;\n indexPath: string;\n}\n\nexport interface NativeKnowledgeConfig {\n enabled: boolean;\n includeFiles: string[];\n maxChunkChars: number;\n maxResults: number;\n maxChars: number;\n stateDir: string;\n obsidianVaults: NativeKnowledgeObsidianVaultConfig[];\n openclawWorkspace?: NativeKnowledgeOpenClawWorkspaceConfig;\n}\n\nexport interface NativeKnowledgeFolderRuleConfig {\n pathPrefix: string;\n namespace?: string;\n privacyClass?: string;\n}\n\nexport interface NativeKnowledgeObsidianVaultConfig {\n id: string;\n rootDir: string;\n includeGlobs: string[];\n excludeGlobs: string[];\n namespace?: string;\n privacyClass?: string;\n folderRules: NativeKnowledgeFolderRuleConfig[];\n dailyNotePatterns: string[];\n materializeBacklinks: boolean;\n}\n\nexport interface NativeKnowledgeOpenClawWorkspaceConfig {\n enabled: boolean;\n bootstrapFiles: string[];\n handoffGlobs: string[];\n dailySummaryGlobs: string[];\n automationNoteGlobs: string[];\n workspaceDocGlobs: string[];\n excludeGlobs: string[];\n sharedSafeGlobs: string[];\n}\n\n/**\n * OpenClaw SecretRef shape (issue #757).\n *\n * OpenClaw resolves these at runtime via its built-in secret resolver\n * (e.g. exec providers like `kc_*` for macOS Keychain). Plugins receive\n * the raw object in `pluginConfig` and must call the gateway's resolver\n * before using the value. Standalone Remnic does NOT resolve SecretRefs;\n * operators must use plain strings or `${ENV_VAR}` expansion instead.\n */\nexport interface SecretRef {\n source: string;\n provider?: string;\n id?: string;\n command?: unknown;\n [key: string]: unknown;\n}\n\nexport type AgentAccessAuthToken = string | SecretRef;\n\nexport interface AgentAccessHttpConfig {\n enabled: boolean;\n host: string;\n port: number;\n /**\n * Bearer token. Either a literal string (env-expanded) or an unresolved\n * SecretRef object preserved verbatim from openclaw.json — resolved at\n * service-start time via {@link resolveAgentAccessAuthToken}.\n */\n authToken?: AgentAccessAuthToken;\n principal?: string;\n maxBodyBytes: number;\n /** Max write requests per rolling window before 429 write_rate_limited (issue #1937). */\n writeRateLimitMaxRequests: number;\n /** Rolling window for the write rate limit, in ms (issue #1937). */\n writeRateLimitWindowMs: number;\n}\n\nexport interface DreamingConfig {\n enabled: boolean;\n journalPath: string;\n maxEntries: number;\n injectRecentCount: number;\n minIntervalMinutes: number;\n narrativeModel: string | null;\n narrativePromptStyle: DreamingNarrativePromptStyle;\n watchFile: boolean;\n}\n\n/**\n * Light-sleep phase config (issue #678 PR 2/4).\n *\n * Groups existing top-level lifecycle-policy gates under a unified namespace.\n * When `dreams.phases.lightSleep.*` keys are set they WIN over the legacy\n * top-level keys; the legacy keys remain readable for backward compatibility.\n *\n * Light sleep: recent activity scoring + clustering (tier-routing value score,\n * observation ledger, buffer state — `runLifecyclePolicyPass` in orchestrator).\n */\nexport interface DreamsLightSleepConfig {\n /** Phase master switch. Mirrors `lifecyclePolicyEnabled` when not set explicitly. */\n enabled: boolean;\n /** Minimum interval between light-sleep passes in milliseconds. */\n cadenceMs: number;\n /** Value score above which a memory is treated as hot. Mirrors `lifecyclePromoteHeatThreshold`. */\n promoteHeatThreshold: number;\n /** Value score below which a memory starts to decay. Mirrors `lifecycleStaleDecayThreshold`. */\n staleDecayThreshold: number;\n /** Value score below which a memory is eligible for archive. Mirrors `lifecycleArchiveDecayThreshold`. */\n archiveDecayThreshold: number;\n /** Whether stale memories are filtered from recall. Mirrors `lifecycleFilterStaleEnabled`. */\n filterStaleEnabled: boolean;\n}\n\n/**\n * REM phase config (issue #678 PR 2/4).\n *\n * Groups existing top-level semantic-consolidation and supersession gates.\n * When `dreams.phases.rem.*` keys are set they WIN over the legacy top-level\n * keys.\n *\n * REM: cross-session synthesis, supersession resolution, semantic consolidation\n * (`runSemanticConsolidation` in orchestrator).\n */\nexport interface DreamsRemConfig {\n /** Phase master switch. Mirrors `semanticConsolidationEnabled` when not set explicitly. */\n enabled: boolean;\n /**\n * How often the REM pass runs, in milliseconds.\n * Derived from `semanticConsolidationIntervalHours` (×3 600 000) when not set explicitly.\n */\n cadenceMs: number;\n /** Cosine-similarity threshold for cluster membership. Mirrors `semanticConsolidationThreshold`. */\n similarityThreshold: number;\n /** Minimum cluster size before consolidation runs. Mirrors `semanticConsolidationMinClusterSize`. */\n minClusterSize: number;\n /** Max cluster operations per run. Mirrors `semanticConsolidationMaxPerRun`. */\n maxPerRun: number;\n /** Minimum gap between consolidation passes (ms). Mirrors `consolidationMinIntervalMs`. */\n minIntervalMs: number;\n}\n\n/**\n * Deep-sleep phase config (issue #678 PR 2/4).\n *\n * Groups existing versioning and tier-migration gates.\n * When `dreams.phases.deepSleep.*` keys are set they WIN over the legacy\n * top-level keys.\n *\n * Deep sleep: promotion to durable memory, hot→cold tier migration,\n * page-version snapshots, archive (`engram-nightly-governance` cron,\n * `tier-migration.ts`, `page-versioning.ts`, `hygiene.ts`).\n */\nexport interface DreamsDeepSleepConfig {\n /**\n * Phase master switch. No single direct legacy mirror; defaults false unless\n * an existing deep-sleep surface such as nightly governance auto-registration,\n * tier migration, or page versioning is explicitly enabled. Set to `false`\n * to disable those surfaces without removing legacy config keys.\n */\n enabled: boolean;\n /** True only when dreams.phases.deepSleep.enabled was explicitly configured. */\n enabledExplicitlySet?: boolean;\n /**\n * Minimum interval between deep-sleep passes in milliseconds.\n * Informational only in PR 2; PR 4 will wire this into the cron scheduler.\n */\n cadenceMs: number;\n /** Enable page-version snapshots on every overwrite. Mirrors `versioningEnabled`. */\n versioningEnabled: boolean;\n /** Max snapshots per page. Mirrors `versioningMaxPerPage`. */\n versioningMaxPerPage: number;\n}\n\n/**\n * Unified dreams phases config block (issue #678 PR 2/4).\n *\n * Operators set `dreams.phases.{lightSleep,rem,deepSleep}.*` in their plugin\n * config. Values under this block WIN over the equivalent legacy top-level keys\n * when both are set. Legacy keys continue to be parsed so existing configs do\n * not need to change.\n *\n * This block is intentionally separate from `DreamingConfig` which controls the\n * diary surface (`surfaces/dreams.ts`) — a different feature. See docs/dreams.md.\n */\nexport interface DreamsPhasesConfig {\n lightSleep: DreamsLightSleepConfig;\n rem: DreamsRemConfig;\n deepSleep: DreamsDeepSleepConfig;\n}\n\nexport interface ProceduralConfig {\n enabled: boolean;\n minOccurrences: number;\n successFloor: number;\n autoPromoteOccurrences: number;\n autoPromoteEnabled: boolean;\n lookbackDays: number;\n /** When true, installer may register the nightly procedural mining cron (default off). */\n proceduralMiningCronAutoRegister: boolean;\n recallMaxProcedures: number;\n maintenance: ProceduralMaintenanceConfig;\n skillProjection: SkillProjectionConfig;\n}\n\n/**\n * Claim-level provenance spans (issue #1575). Controls whether extracted\n * facts carry verbatim source-utterance excerpts and the strength tag that\n * downstream features (faithfulness gate #1576, correction UX #1580/#1583,\n * tombstone matching #1579, TrustScore #1577) consume.\n *\n * PR 1 parses the block + schema only; the extraction prompt and validator\n * land in PR 2. When `enabled` is false, extraction prompt/output are\n * byte-identical to pre-feature behavior (rule 39).\n */\nexport interface ProvenanceConfig {\n /** Emit provenance spans on new extractions. Default true. */\n enabled: boolean;\n /** Maximum characters stored per quote; longer quotes truncate at a word boundary. Default 300. */\n maxQuoteChars: number;\n /**\n * When true, facts whose quote cannot be located are routed to\n * `pending_review` instead of `active`. Stays false until #1576 lands and\n * the bench shows the gate is safe (rule 48).\n */\n requireSpans: boolean;\n}\n\n/**\n * Coding-agent mode config (issue #569).\n *\n * When the connector provides a `CodingContext` (see below), Remnic overlays\n * a project- and/or branch-scoped namespace on top of the principal's default\n * namespace so that memories written while working on project A do not surface\n * while working on project B.\n *\n * Both flags default off-for-branch / on-for-project. Per CLAUDE.md #30 every\n * filter or transform needs an escape hatch: set `projectScope: false` to\n * exactly restore pre-#569 behaviour.\n */\nexport interface CodingModeConfig {\n /**\n * When true (default), a session with a resolved `CodingContext` uses a\n * project-scoped namespace. When false, the principal's default namespace\n * is used unchanged (pre-#569 behaviour).\n */\n projectScope: boolean;\n /**\n * When true, recall/write also overlay the current branch on top of the\n * project namespace. Default false — branch-scope is opt-in because active\n * development typically wants recall across branches. (Wired by PR 3 of\n * issue #569; declared here so the schema ships in one slice.)\n */\n branchScope: boolean;\n /**\n * When true (default), project-scoped and branch-scoped sessions include\n * the root/default namespace in their read fallbacks so globally useful\n * memories remain visible from any project. When false, project-scoped\n * sessions only see their own namespace (strict isolation).\n *\n * CLAUDE.md #30: configuration gate for the recall fan-out to the root\n * namespace. Does not affect writes — those always go to the project\n * namespace only.\n */\n globalFallback: boolean;\n}\n\n// CodingKnowledgeConfig — Track A (issue #1548) durable coding-knowledge\n// surface. Master gate plus per-feature switches. Off by default.\nexport interface CodingKnowledgeConfig {\n /** Master gate. Off = byte-for-byte pre-feature behaviour on every path. */\n enabled: boolean;\n /** Decision-record surfaces + briefing titles (effective only under the master gate). */\n decisionRecords: boolean;\n /** Architecture-card build/refresh + briefing injection. */\n architectureCard: boolean;\n /** Last-seen-head persistence + delta briefing line. */\n sessionDelta: boolean;\n /** Opt-in LLM summary pass (costs tokens — default off, per rule 48). */\n architectureCardLlmSummary: boolean;\n /**\n * Structural-context provider selection.\n * - `\"none\"` (default): no provider consulted — review-context runs\n * file-path-only boosting, identical to pre-feature behaviour.\n * - `\"subprocess\"`: shells out to `structuralProviderCommand` via\n * `execFile` (argv-array, never shell-string — rule 10).\n * - `\"native\"`: the `@remnic/coding-graph` engine, when installed.\n */\n structuralProvider: \"none\" | \"subprocess\" | \"native\";\n /**\n * Absolute path to the subprocess binary when `structuralProvider =\n * \"subprocess\"`. Empty string = no command configured (consumer must\n * reject the empty case at the wiring site). Rule 24: `statSync` file\n * check at boot, not at every call.\n */\n structuralProviderCommand: string;\n /**\n * Gate for the 14 codegraph parity tools (issue #1554). Off by default --\n * tools/list, HTTP routes, and CLI help are byte-identical to pre-feature\n * until this is true. The runtime also requires the @remnic/coding-graph\n * package to be installed (rule 39: single gate predicate\n * codegraphSurfaceVisible(config) && isCodingGraphInstalled()).\n */\n codegraphTools: boolean;\n /**\n * Root directory for per-project graph SQLite DBs. Empty string = derive\n * from memoryDir (<memoryDir>/codegraph/<principal>/<projectId>.sqlite).\n * Absolute path required when set explicitly (rule 11 -- no relative paths).\n */\n codegraphDbDir: string;\n /**\n * Phase B type resolution via LSP (issue #1917). When enabled, the\n * reindex pipeline invokes `executeLspResolution` after the heuristic\n * pass to upgrade unresolved call-site edges with real language-server\n * definitions. Requires installed language servers on the host.\n * Off by default (rule 48 — explicit opt-in for subprocess spawning).\n */\n lsp?: CodingGraphLspConfig;\n}\n\n/**\n * Per-language LSP server configuration (issue #1917). Keys are\n * `CodingGraphLanguage` values (typescript, python, go, rust, ...).\n * When a language is not listed, the default server command for that\n * language is used (typescript-language-server, pyright, gopls,\n * rust-analyzer).\n */\nexport interface CodingGraphLspConfig {\n /** Master gate for LSP resolution. Off = heuristic edges only. */\n enabled: boolean;\n /**\n * Per-language server overrides. Each value is an argv array's first\n * element (command) plus optional args. When absent for a language,\n * the default server command is probed on PATH.\n */\n servers?: Record<string, { command: string; args?: string[] }>;\n /** Per-request timeout in ms (default 3000). */\n timeoutMs?: number;\n /** Max LSP requests per reindex run (default 500). */\n maxRequestsPerRun?: number;\n}\n\n// ChatConfig — conversational memory inspection and correction (issue #1583).\n// Off by default — spawns LLM calls so explicit opt-in (rule 48).\nexport interface ChatConfig {\n /** Master gate. Off = no tools/endpoints/CLI registered (byte-identical, rule 39). */\n enabled: boolean;\n /** Routing override for the chat LLM (empty = use the default chain). */\n model: string;\n /** Max tool calls per user turn before partial-reply fallback (rule 34). */\n maxToolCallsPerTurn: number;\n /** Hours after which idle chat session state is cleaned up. */\n sessionTtlHours: number;\n}\n\nexport interface SupportPassportConfig {\n enabled: boolean;\n trustedProxyAddresses: string[];\n}\n\n/**\n * Session-scoped coding context. Produced by `resolveGitContext()` in the\n * connector layer and attached to a session so that recall + write paths can\n * compute an overlay namespace.\n *\n * All fields mirror `GitContext` from `./coding/git-context.ts`; kept as a\n * separate interface because `types.ts` must stay dependency-free (it is\n * imported by every other module).\n */\nexport interface CodingContext {\n projectId: string;\n branch: string | null;\n rootPath: string;\n defaultBranch: string | null;\n}\n\nexport type ScopeProfileLayerId = \"userProject\" | \"teamProject\" | \"userGlobal\" | \"serverShared\";\n\nexport type ScopeProfilePromotionTarget = ScopeProfileLayerId | \"serverShared\" | \"teamProject\" | \"userGlobal\";\n\nexport interface ScopeProfileTeamProjectConfig {\n /**\n * Namespace template for team-project layers. Supported placeholders:\n * `{teamId}`, `{principal}`, `{projectId}`, `{projectHash}`, and\n * `{projectNamespace}`. The resolved namespace is validated at use time.\n */\n namespaceTemplate?: string;\n /**\n * Optional trusted team id for this profile. When omitted, the resolver uses\n * the first configured team whose principal lists include the caller.\n */\n teamId?: string;\n}\n\nexport interface ScopeProfileAutoPromoteConfig {\n enabled: boolean;\n targets: ScopeProfilePromotionTarget[];\n categories: Array<\"fact\" | \"correction\" | \"decision\" | \"preference\" | \"rule\" | \"procedure\">;\n minConfidenceTier: ConfidenceTier;\n}\n\nexport interface ScopeProfileConfig {\n readOrder: ScopeProfileLayerId[];\n writeDefault: ScopeProfileLayerId;\n promotionTargets: ScopeProfilePromotionTarget[];\n teamProject?: ScopeProfileTeamProjectConfig;\n autoPromote: ScopeProfileAutoPromoteConfig;\n}\n\nexport interface ScopeTeamConfig {\n principals: string[];\n projectNamespaceTemplate?: string;\n read: string[];\n write: string[];\n promote: string[];\n}\n\nexport interface DependencyPropagationConfig {\n /** Master switch. Default false. */\n enabled: boolean;\n /** Link types that can identify one-hop dependents. */\n linkTypes: Array<\"supports\" | \"follows\" | \"references\">;\n /** Maximum active dependents to revalidate. `0` disables propagation. */\n maxDependents: number;\n /** Maximum duration of the single batched revalidation call, in milliseconds. */\n timeoutMs: number;\n /** Discover and revalidate dependents without writing supersession changes. */\n dryRun: boolean;\n}\n\nexport interface HeartbeatConfig {\n enabled: boolean;\n journalPath: string;\n maxPreviousRuns: number;\n watchFile: boolean;\n detectionMode: HeartbeatDetectionMode;\n gateExtractionDuringHeartbeat: boolean;\n}\n\nexport interface SlotBehaviorConfig {\n requireExclusiveMemorySlot: boolean;\n onSlotMismatch: SlotMismatchMode;\n}\n\nexport interface CodexCompatConfig {\n enabled: boolean;\n threadIdBufferKeying: boolean;\n compactionFlushMode: CodexCompactionFlushMode;\n fingerprintDedup: boolean;\n}\n\nexport function confidenceTier(score: number): ConfidenceTier {\n if (score >= 0.95) return \"explicit\";\n if (score >= 0.7) return \"implied\";\n if (score >= 0.4) return \"inferred\";\n return \"speculative\";\n}\n\n/** Default TTL in days for speculative memories (auto-expire if unconfirmed) */\nexport const SPECULATIVE_TTL_DAYS = 30;\n\n/**\n * Shape for semantic chunking config overrides stored in PluginConfig.\n * Mirrors SemanticChunkingConfig from semantic-chunking.ts without creating\n * a circular import (types.ts is imported by everything).\n */\nexport interface SemanticChunkingConfigShape {\n targetTokens: number;\n minTokens: number;\n maxTokens: number;\n smoothingWindowSize: number;\n boundaryThresholdStdDevs: number;\n embeddingBatchSize: number;\n fallbackToRecursive: boolean;\n}\nexport interface PluginConfig\n extends BoundedJsonlStateConfig,\n ProjectionRebuildScheduleConfig,\n SecurityConfig,\n DriftDetectionSettings,\n ActiveContextConfigFields,\n DeepRecallSettings,\n RecallNavigationSettings,\n RecognitionTierSettings,\n SeedGraduationSettings,\n LocalLlmConfig, OutcomeBoostConfig {\n openaiApiKey: string | undefined;\n openaiBaseUrl: string | undefined;\n backgroundGeneration?: BackgroundGenerationConfig;\n model: string;\n reasoningEffort: ReasoningEffort;\n triggerMode: TriggerMode;\n bufferMaxTurns: number;\n /** Distinct sessions the extraction buffer admits before backpressure (#3303). */\n bufferMaxSessions: number;\n bufferMaxMinutes: number;\n /** Queue depth cap; default 1 = serial queue, same-buffer tasks never overlap. Applies at daemon start. */\n extractionQueueConcurrency: number;\n /**\n * Debounce window (ms) for persisting the smart buffer to `state/buffer.json`\n * (issue #1909). Steady-state buffering coalesces the whole-state serialize\n * onto a trailing-edge timer instead of rewriting on every turn; extraction\n * trigger/clear and shutdown still force an immediate flush. Default 3000.\n * `0` restores the legacy save-every-turn behavior exactly.\n */\n bufferSaveDebounceMs: number;\n /**\n * Surprise-gated buffer flush (issue #563, D-MEM).\n *\n * When enabled, every turn added to the smart buffer is scored against a\n * configurable window of recent memories using an embedding-distance proxy\n * for novelty (see `buffer-surprise.ts`). A turn whose surprise score\n * exceeds `bufferSurpriseThreshold` triggers an immediate extract flush,\n * even if the existing signal/turn-count/time triggers would otherwise keep\n * buffering. Disabled by default — when `false`, buffer behavior is\n * identical to pre-#563 code. Additive only: existing triggers are never\n * suppressed by this flag.\n */\n bufferSurpriseTriggerEnabled: boolean;\n /**\n * Threshold in `[0, 1]` above which a surprise score causes an immediate\n * flush. `0.35` is a conservative default chosen to favor precision over\n * recall during the opt-in phase. Ignored unless\n * `bufferSurpriseTriggerEnabled` is `true`.\n */\n bufferSurpriseThreshold: number;\n /**\n * Number of nearest neighbors to average over when computing the surprise\n * score (see `computeSurprise`). Default `5`. Clamped to the recent-memory\n * window size at call time.\n */\n bufferSurpriseK: number;\n /**\n * Maximum number of recent memories to sample when computing the surprise\n * score. Bounds embedding cost per turn. Default `20`. Set to `0` to\n * disable the trigger even when the flag is on (no memories to compare\n * against → treat as not-applicable rather than maximally surprising).\n */\n bufferSurpriseRecentMemoryCount: number;\n /**\n * Hard timeout (ms) for the surprise probe. If the probe does not\n * resolve within this window, the buffer treats the probe as failed,\n * logs at debug, and falls through to the existing triggers. Ensures\n * a slow or hung embedder cannot stall the turn-append path. Default\n * `2000` (2s).\n */\n bufferSurpriseProbeTimeoutMs: number;\n consolidateEveryN: number;\n highSignalPatterns: string[];\n maxMemoryTokens: number;\n memoryOsPreset?: MemoryOsPresetName;\n qmdEnabled: boolean;\n qmdCollection: string;\n wikiMergeIntoRecall: boolean;\n qmdMaxResults: number;\n qmdEmbeddingBacklogThreshold: number;\n qmdColdTierEnabled?: boolean;\n qmdColdCollection?: string;\n qmdColdMaxResults?: number;\n qmdTierMigrationEnabled: boolean;\n qmdTierDemotionMinAgeDays: number;\n qmdTierDemotionValueThreshold: number;\n qmdTierPromotionValueThreshold: number;\n qmdTierParityGraphEnabled: boolean;\n qmdTierParityHiMemEnabled: boolean;\n qmdTierAutoBackfillEnabled: boolean;\n qmdSupportedVersion: string;\n qmdAutoUpgradeEnabled: boolean;\n qmdAutoUpgradeCheckIntervalMs: number;\n qmdChunkStrategy: \"auto\" | \"regex\";\n qmdCandidateLimit?: number;\n qmdQueryRerankEnabled: boolean;\n qmdSearchStrategy: \"hybrid\" | \"lex-vec\" | \"lex\";\n qmdSubprocessStrategy: \"query\" | \"search\";\n qmdDaemonTimeoutMs: number;\n qmdIndexName?: string;\n qmdForceCpu: boolean;\n qmdGpuBackend?: \"auto\" | \"metal\" | \"cuda\" | \"vulkan\" | \"false\";\n qmdEmbedParallelism?: number;\n qmdEmbedModel?: string;\n qmdRerankModel?: string;\n qmdGenerateModel?: string;\n embeddingFallbackEnabled: boolean;\n embeddingFallbackProvider: \"auto\" | \"openai\" | \"local\";\n /**\n * Host adapters may register an embedding provider scoped to this memoryDir.\n * Core remains host-agnostic: when no provider is registered, the existing\n * OpenAI/local fallback path is used.\n */\n hostEmbeddingProviderEnabled: boolean;\n hostEmbeddingProviderId?: string;\n hostEmbeddingProviderModel?: string;\n /**\n * Optional model identifier for local embedding fallback requests.\n *\n * Local chat/completion models and local embedding models are often\n * different LM Studio/Ollama model IDs. When unset, Remnic preserves the\n * legacy behavior and falls back to `localLlmModel`.\n */\n embeddingFallbackModel: string;\n /** Optional absolute path to qmd binary. If unset, PATH/fallback discovery is used. */\n qmdPath?: string;\n memoryDir: string;\n externalWikis: ExternalWikiRoot[];\n debug: boolean;\n identityEnabled: boolean;\n identityContinuityEnabled: boolean;\n identityInjectionMode: IdentityInjectionMode;\n identityMaxInjectChars: number;\n continuityIncidentLoggingEnabled: boolean;\n continuityAuditEnabled: boolean;\n sessionObserverEnabled?: boolean;\n sessionObserverDebounceMs?: number;\n sessionObserverBands?: SessionObserverBandConfig[];\n injectQuestions: boolean;\n commitmentDecayDays: number;\n workspaceDir: string;\n captureMode: CaptureMode;\n fileHygiene?: FileHygieneConfig;\n nativeKnowledge?: NativeKnowledgeConfig;\n agentAccessHttp: AgentAccessHttpConfig;\n // Access tracking (Phase 1A)\n accessTrackingEnabled: boolean;\n accessTrackingBufferMaxSize: number;\n // Retrieval options\n recencyWeight: number;\n boostAccessCount: boolean;\n /** Record empty recall impressions (memoryIds: []) when no memories are injected. Disabled by default. */\n recordEmptyRecallImpressions: boolean;\n // v2.2 Advanced Retrieval\n queryExpansionEnabled: boolean;\n queryExpansionMaxQueries: number;\n /** Minimum token length to consider for query expansion. */\n queryExpansionMinTokenLen: number;\n rerankEnabled: boolean;\n /** Rerank provider. \"local\" uses Local LLM only; \"cloud\" uses gateway fallback chain. */\n rerankProvider: \"local\" | \"cloud\";\n rerankMaxCandidates: number;\n rerankTimeoutMs: number;\n rerankCacheEnabled: boolean;\n rerankCacheTtlMs: number;\n feedbackEnabled: boolean;\n // v2.2 Negative Examples (safe defaults: off unless enabled)\n /** If true, allow recording negative examples and apply a soft penalty during ranking. */\n negativeExamplesEnabled: boolean;\n /** Score penalty per \"not useful\" hit (typical QMD scores ~0-1). Keep small. */\n negativeExamplesPenaltyPerHit: number;\n /** Maximum penalty applied from negative examples. */\n negativeExamplesPenaltyCap: number;\n // Chunking (Phase 2A)\n chunkingEnabled: boolean;\n chunkingTargetTokens: number;\n chunkingMinTokens: number;\n chunkingOverlapSentences: number;\n // Semantic Chunking (Issue #368)\n /** Enable semantic chunking with embedding-based topic boundary detection. Default: false. */\n semanticChunkingEnabled: boolean;\n /** Optional overrides for the semantic chunking algorithm. */\n semanticChunkingConfig: Partial<SemanticChunkingConfigShape>;\n // Contradiction Detection (Phase 2B)\n contradictionDetectionEnabled: boolean;\n contradictionSimilarityThreshold: number;\n contradictionMinConfidence: number;\n contradictionAutoResolve: boolean;\n /** Nightly contradiction-scan cron config (issue #520). */\n contradictionScan: ContradictionScanConfig;\n /** Anchor-first contradiction candidate localization settings (issue #2327). */\n contradictionLocalization: ContradictionLocalizationConfig;\n /** One-hop dependency invalidation propagation settings (issue #2326). */\n dependencyPropagation: DependencyPropagationConfig;\n // Temporal Supersession (issue #375)\n /**\n * When enabled, writes that carry `structuredAttributes` mark any older\n * fact with the same `entityRef + attribute_name` supersession key and a\n * conflicting value as `status: \"superseded\"`.\n */\n temporalSupersessionEnabled: boolean;\n /**\n * When enabled, superseded memories are still returned by recall (useful\n * for audit/history queries). Default: false — superseded memories are\n * filtered out.\n */\n temporalSupersessionIncludeInRecall: boolean;\n /**\n * Bi-temporal validity master gate (issue #1578). When `false` (default),\n * the recall validity-injection filter and `asOf` semantics behave\n * byte-identically to pre-#1578.\n *\n * Turn on to exclude validity-expired facts from default injection\n * candidates (they remain findable by explicit search and `as_of`\n * queries). Storage round-trips `observedAt`/`eventTimeSource`; the\n * extraction prompt emits an optional per-fact `eventTime` expression\n * and `resolveFactEventTime` resolves it against the source turn\n * timestamp at write time (#1578 PR2).\n */\n temporalBiTemporal: boolean;\n /**\n * Escape hatch for the bi-temporal injection filter (issue #1578). When\n * `true`, facts whose `[valid_at, invalid_at)` interval ended before now\n * are kept in injection candidates even with `temporalBiTemporal` on — some\n * deployments prefer the older (always-inject) behavior. Default `false`.\n */\n temporalExpiredInInjection: boolean;\n /** Correction Contract (#1580). `correction.enabled` / `correctionEnabled`. Default true. */\n correctionEnabled: boolean;\n /** Apply requires `confirm: true`. `correction.applyRequiresConfirm`. Default true. */\n correctionApplyRequiresConfirm: boolean;\n /** Max memories one plan may touch. `correction.maxAffected`. Default 10. */\n correctionMaxAffected: number;\n /** Plan TTL hours. `correction.planTtlHours`. Default 24. */\n correctionPlanTtlHours: number;\n /** Per-attempt classify timeout (ms, 1..300000). `correction.classifyTimeoutMs`. Default 45000. */\n correctionClassifyTimeoutMs: number;\n /**\n * Passive correction capture (issue #1581) — detects corrections expressed\n * passively in conversation turns during extraction and routes them to the\n * Correction Contract (#1580). `\"off\"` disables detection entirely; `\"queue\"`\n * plans corrections for human review (conservative); `\"auto\"` applies\n * immediately when all safety guards pass, otherwise falls back to queue.\n * Default `\"off\"` — operators opt in (rule 48). Parsed from\n * `correctionCapture.mode` / `correctionCaptureMode`.\n */\n correctionCaptureMode: \"off\" | \"queue\" | \"auto\";\n /**\n * Minimum planner confidence for auto-apply (issue #1581). Plans below this\n * floor are queued even in `\"auto\"` mode. Default `0.85`. Parsed from\n * `correctionCapture.confidenceFloor` / `correctionCaptureConfidenceFloor`.\n */\n correctionCaptureConfidenceFloor: number;\n /**\n * Maximum affected memories for auto-apply (issue #1581). Plans touching more\n * memories than this are queued even in `\"auto\"` mode — a blast-radius cap.\n * Default `2`. Parsed from `correctionCapture.autoApplyMaxAffected` /\n * `correctionCaptureAutoApplyMaxAffected`.\n */\n correctionCaptureAutoApplyMaxAffected: number;\n // Tombstones — non-resurrection invariant (issue #1579).\n /**\n * Master gate for the tombstone non-resurrection invariant. When `true`\n * (default), a fact that matches an active tombstone at the storage\n * chokepoint is persisted as `status: \"pending_review\"` + `blockedBy`\n * instead of becoming active — visible, never a silent drop (rule 34).\n * When `false`, pre-feature behavior: the chokepoint check is a no-op and\n * retired facts can resurrect through re-extraction / import / consolidation\n * / dreams / pattern-reinforcement. Off = rollback safety (rule 30).\n */\n tombstonesEnabled: boolean;\n /**\n * Tier-4 semantic tombstone matching (issue #1579). Off by default — ships\n * dark until MemCorrect (#1584) shows an acceptable false-block rate\n * (rule 48). When `true`, the tombstone lookup also checks embedding cosine\n * similarity against tombstoned normalized text.\n */\n tombstonesSemanticMatch: boolean;\n /**\n * Cosine threshold for tier-4 semantic tombstone matching. Default `0.9`.\n * Only consulted when `tombstonesSemanticMatch` is `true`.\n */\n tombstonesSemanticThreshold: number;\n // Direct-answer retrieval tier (issue #518)\n /**\n * When true, recall checks whether a single validated memory in a\n * high-trust taxonomy bucket can answer the query before invoking QMD.\n * Default false — enable explicitly after bench validation.\n */\n recallDirectAnswerEnabled: boolean;\n /** Recall state views (issue #1952): label current/historical/transition on change-intent queries. Default false. */\n recallStateViews: boolean;\n /** Standing memory block (issue #2971). Default false. */\n recallStandingBlock: boolean;\n standingBlockFreshDays: number;\n standingBlockMaxChars: number;\n /** Directory sidecars (issue #3088). Default false. */\n directorySidecarsEnabled: boolean;\n /**\n * Disclosure auto-escalation policy (issue #677 PR 4/4). `\"auto\"`\n * escalates chunk → section when top-K confidence falls below\n * {@link recallDisclosureEscalationThreshold}. Default `\"manual\"`.\n */\n recallDisclosureEscalation: \"manual\" | \"auto\";\n /**\n * Top-K confidence threshold (in `[0, 1]`) below which auto-escalation\n * promotes `chunk` → `section`. Only consulted when\n * {@link recallDisclosureEscalation} is `\"auto\"`. Default `0.5`.\n */\n recallDisclosureEscalationThreshold: number;\n /**\n * Graph-based retrieval tier via Personalized PageRank (issue #559 PR 4).\n * When true, recall builds a retrieval graph from memory frontmatter\n * and runs PPR, merging the result with QMD via MMR. Default false —\n * ships off pending the retrieval-graph bench in PR 5.\n */\n recallGraphEnabled: boolean;\n /** PPR damping factor used when `recallGraphEnabled` is true. */\n recallGraphDamping: number;\n /** PPR power-iteration cap used when `recallGraphEnabled` is true. */\n recallGraphIterations: number;\n /**\n * Max memories returned by the graph tier before MMR. Set to 0 to\n * disable the graph tier's contribution without flipping the flag.\n */\n recallGraphTopK: number;\n /**\n * Minimum token-overlap ratio (query tokens ∩ memory tokens / query tokens)\n * required for direct-answer eligibility. Set to 0 to disable the gate.\n */\n recallDirectAnswerTokenOverlapFloor: number;\n /**\n * Minimum calibrated importance score required for direct-answer\n * eligibility. Set to 0 to disable the gate.\n */\n recallDirectAnswerImportanceFloor: number;\n /**\n * Ambiguity margin: if the second-best candidate scores within this\n * ratio of the top candidate, direct-answer defers to the hybrid tier.\n */\n recallDirectAnswerAmbiguityMargin: number;\n /**\n * Taxonomy category IDs eligible for direct-answer routing. Memories\n * whose resolved taxonomy category is not in this list never qualify.\n */\n recallDirectAnswerEligibleTaxonomyBuckets: string[];\n /** Cross-namespace query-budget limiter (issue #565 PR 4/5): throttle a\n * principal's cross-namespace recall burst past the hard limit. Default false. */\n recallCrossNamespaceBudgetEnabled: boolean;\n /** Rolling window in milliseconds over which cross-namespace reads are counted. */\n recallCrossNamespaceBudgetWindowMs: number;\n /**\n * Soft threshold — the first point at which the limiter flags a burst.\n * Calls are still allowed; anomaly detection (issue #565 PR 5) will\n * surface the warning.\n */\n recallCrossNamespaceBudgetSoftLimit: number;\n /** Hard threshold — calls past this count are denied in the window. */\n recallCrossNamespaceBudgetHardLimit: number;\n /** Max concurrent recalls per principal; `0` = unlimited. Default 4 (#1906). */\n recallMaxConcurrentPerPrincipal: number;\n /** Coalesce identical concurrent recalls per principal; each caller gets its own clone. Default true. */\n recallSingleFlightEnabled: boolean;\n /**\n * When true, recall multiplies candidate scores by the Memory Worth factor\n * from `mw_success` / `mw_fail` (see `computeMemoryWorth`): failed memories\n * sink; neutral ones untouched (multiplier 1.0). Default false until the\n * benchmark shows precision tie-or-win.\n */\n recallMemoryWorthFilterEnabled: boolean;\n /**\n * When false, the Trust/Memory-Worth recall stage skips its corpus-level\n * fallback (a per-namespace `readAllMemories`-derived counter/signal map)\n * and relies only on frontmatter already loaded on the hot recall path plus\n * a bounded-parallel per-candidate direct read — fully O(candidates)\n * (issue #1905). Default true keeps the corpus fallback reachable.\n */\n recallTrustStageCorpusFallbackEnabled: boolean;\n /**\n * Serve `StorageManager.readAllMemories()` from a version-keyed in-process\n * cache of the full parsed corpus (issue #1902). Default true. When on, a\n * scan-once-then-serve fast path eliminates the repeated full-corpus disk\n * scans that dominate post-write recall latency on large collections\n * (~99K files); coherence is preserved by the on-disk\n * `.memory-corpus-version.log` sentinel (distinct from `.memory-status`,\n * which is kept separate so plain creates don't invalidate the entity cache)\n * and patch-on-write updates. Tradeoff: the cache holds\n * one full-corpus entry per baseDir (hundreds of MB at ~99K files, already\n * resident in the OS page cache). Set false to force disk scans on\n * memory-constrained hosts — behavior then matches the pre-#1902 pure\n * disk-scan + in-flight-dedup path exactly. Version invalidation is the\n * primary correctness mechanism; `hotMemoriesCacheTtlMs` is a bounded safety\n * net for external (user/git/editor) edits that bypass the sentinel.\n */\n hotMemoriesCacheEnabled: boolean;\n /**\n * Max age (ms) a version-keyed hot-cache entry is served before a fresh disk\n * scan (issue #1902). The version sentinel gives immediate coherence for\n * writers that go through StorageManager or bumpMemoryCorpusVersionForDir, but\n * direct filesystem edits (manual, git checkout, external tools) don't bump\n * it — this TTL bounds how long such an edit can be stale. Default 60000 (60s).\n * Set 0 to disable the TTL (version invalidation only; max perf for\n * pure-daemon deployments with no external edits).\n */\n hotMemoriesCacheTtlMs: number;\n /**\n * Scope-aware cache invalidation (issue #1904). When true (default), a memory\n * write evicts only the cache layers it can actually affect — a plain fact\n * `create` no longer nukes the global QMD result caches or the version-keyed\n * entity cache, so continuous extraction stops keeping every cache\n * permanently cold. Set false to restore the pre-#1904 behavior of clearing\n * every layer on every write (rollback lever; no featureset change).\n */\n scopedCacheInvalidationEnabled: boolean;\n // Unified TrustScore recall stage (issue #1577). Combines memory-worth,\n // provenance, faithfulness, corroboration, contradiction, feedback, recency,\n // and optional domain calibration into one [0,1] trust score applied as a\n // bounded recall multiplier. Subsumes the Memory Worth multiplier when on\n // (the orchestrator runs exactly one of the two — rule 39).\n /**\n * Master gate for the unified TrustScore recall stage. Default `false`: off\n * = the stage is absent and ranking is byte-identical to the pre-feature\n * pipeline (rule 39). Flip via #1574 ablation evidence only.\n */\n trustScoreEnabled: boolean;\n /** Append epistemic hedge suffixes to injected memory lines. Separate gate. */\n trustScoreEpistemicRendering: boolean;\n /**\n * Exclude hard-negative (quarantine-band) items from injection. Default\n * `true`; only effective under the master gate. Quarantined items stay\n * visible in X-ray with a reason (rule 34).\n */\n trustScoreQuarantine: boolean;\n /** Lower bound of the trust multiplier (default 0.5). */\n trustScoreMinMultiplier: number;\n /** Upper bound of the trust multiplier (default 1.25). */\n trustScoreMaxMultiplier: number;\n /** Per-component weights for the TrustScore blend; invalid weights rejected at parse. */\n trustScoreWeights: TrustWeights;\n /**\n * Recall-audit anomaly detector (issue #565 PR 5/5). When true,\n * access surfaces run the anomaly detector over a tail of the audit\n * trail after each recall and surface any flags via logs / metrics.\n * Ships disabled.\n */\n recallAuditAnomalyDetectionEnabled: boolean;\n /** Rolling window over which audit entries are analyzed. */\n recallAuditAnomalyWindowMs: number;\n /** Threshold for the `repeat-query` flag. */\n recallAuditAnomalyRepeatQueryLimit: number;\n /** Threshold for the `namespace-walk` flag (distinct namespaces). */\n recallAuditAnomalyNamespaceWalkLimit: number;\n /** Threshold for the `high-cardinality-return` flag. */\n recallAuditAnomalyHighCardinalityLimit: number;\n /** Threshold for the `rapid-fire` flag. */\n recallAuditAnomalyRapidFireLimit: number;\n /**\n * Optional half-life for Memory Worth decay, in milliseconds. When\n * positive, older outcome observations are exponentially decayed toward\n * the uniform prior. Set to 0 (default) to disable decay and use raw\n * counter values.\n */\n recallMemoryWorthHalfLifeMs: number;\n // Memory Linking (Phase 3A)\n memoryLinkingEnabled: boolean;\n // Conversation Threading (Phase 3B)\n threadingEnabled: boolean;\n threadingGapMinutes: number;\n // Memory Summarization (Phase 4A)\n summarizationEnabled: boolean;\n summarizationTriggerCount: number;\n summarizationRecentToKeep: number;\n summarizationImportanceThreshold: number;\n summarizationProtectedTags: string[];\n // Topic Extraction (Phase 4B)\n topicExtractionEnabled: boolean;\n topicExtractionTopN: number;\n // Transcript & Context Preservation (v2.0)\n // Transcript archive\n transcriptEnabled: boolean;\n transcriptRetentionDays: number;\n /** Channel types to skip from transcript logging (e.g., [\"cron\"]) */\n transcriptSkipChannelTypes: string[];\n // Transcript injection\n transcriptRecallHours: number;\n maxTranscriptTurns: number;\n maxTranscriptTokens: number;\n // Checkpoint\n checkpointEnabled: boolean;\n checkpointTurns: number;\n // Compaction reset: trigger session reset after compaction instead of continuing degraded.\n // Requires OC fork with PR #29985 (api.resetSession).\n compactionResetEnabled: boolean;\n beforeResetTimeoutMs: number;\n initGateTimeoutMs: number;\n flushOnResetEnabled: boolean;\n commandsListEnabled: boolean;\n openclawToolsEnabled: boolean;\n openclawToolSnippetMaxChars: number;\n openclawMessageReceivedCaptureEnabled: boolean;\n openclawReplyMetadataCaptureEnabled: boolean;\n openclawReplyMetadataExtractionHintsEnabled: boolean;\n openclawChannelEnvelopeCleaningEnabled: boolean;\n sessionTogglesEnabled: boolean;\n verboseRecallVisibility: boolean;\n recallTranscriptsEnabled: boolean;\n recallTranscriptRetentionDays: number;\n respectBundledActiveMemoryToggle: boolean;\n activeRecallEnabled: boolean;\n activeRecallAgents: string[] | null;\n activeRecallAllowedChatTypes: ActiveRecallChatType[];\n activeRecallQueryMode: ActiveRecallQueryMode;\n activeRecallPromptStyle: ActiveRecallPromptStyle;\n activeRecallCustomInstruction: string | null;\n activeRecallPromptReplacement: string | null;\n activeRecallPromptOverride: string | null;\n activeRecallPromptAppend: string | null;\n activeRecallMaxSummaryChars: number;\n activeRecallRecentUserTurns: number;\n activeRecallRecentAssistantTurns: number;\n activeRecallRecentUserChars: number;\n activeRecallRecentAssistantChars: number;\n activeRecallThinking: ActiveRecallThinking;\n activeRecallTimeoutMs: number;\n activeRecallCacheTtlMs: number;\n activeRecallModel: string | null;\n activeRecallModelFallbackPolicy: ActiveRecallModelFallbackPolicy;\n activeRecallPersistTranscripts: boolean;\n activeRecallTranscriptDir: string;\n activeRecallEntityGraphDepth: number;\n activeRecallIncludeCausalTrajectories: boolean;\n activeRecallIncludeDaySummary: boolean;\n activeRecallAttachRecallExplain: boolean;\n activeRecallAllowChainedActiveMemory: boolean;\n dreaming: DreamingConfig;\n /**\n * Unified dreams-phases config block (issue #678 PR 2/4).\n * Groups existing lifecycle, REM, and deep-sleep gates under one namespace.\n * Values here WIN over equivalent legacy top-level keys when set. See docs/dreams.md.\n */\n dreamsPhases: DreamsPhasesConfig;\n procedural: ProceduralConfig;\n /** Session-end experience extraction (issue #2979). Default off. */\n sessionExperience: SessionExperienceConfig;\n extractionLiveness: ExtractionLivenessConfig;\n /** Span-mode extraction (#2333 Phase B, bench-gated). Default off. */\n extraction: ExtractionSpanConfig;\n replicaPeers: ReplicaPeersConfig;\n converge: ConvergeConfig;\n /** Claim-level provenance spans (#1575); see `ProvenanceConfig` for defaults. */\n provenance: ProvenanceConfig;\n wearables: WearablesConfig;\n activity: ActivityConfig;\n meetings: MeetingsConfig;\n location: LocationConfig;\n okf: OkfConfig;\n /**\n * At-rest encryption configuration (issue #690 PR 3/4).\n * When `secureStoreEnabled` is true, `StorageManager` reads and\n * writes memory files through the `secure-fs` encryption layer.\n * The store must be unlocked via `remnic secure-store unlock` before\n * any recall or store operations will succeed.\n *\n * When `secureStoreEncryptOnWrite` is true (the default when enabled),\n * every new memory write is encrypted. Set to false to pause new\n * encryptions while still being able to decrypt existing files.\n */\n secureStoreEnabled: boolean;\n /** Encrypt new writes when the secure-store is unlocked. Default true. */\n secureStoreEncryptOnWrite: boolean;\n // Coding-agent project/branch scoping (issue #569)\n codingMode: CodingModeConfig;\n // Coding-knowledge durable memory surface (issue #1548 Track A). Off\n // by default — byte-for-byte pre-feature behaviour when `enabled === false`\n // (rule 39).\n codingKnowledge: CodingKnowledgeConfig;\n /** Action-site failure gate (issue #2382). Off by default; advisory only. */\n actionGate: ActionGateConfig;\n heartbeat: HeartbeatConfig;\n // Conversational memory inspection and correction (issue #1583). Off by\n // default — byte-identical pre-feature when enabled === false (rule 39).\n chat: ChatConfig;\n /** Owner-controlled support passport. Off by default. */\n supportPassport: SupportPassportConfig;\n slotBehavior: SlotBehaviorConfig;\n /** OpenClaw bridge mode (#2120): `\"embedded\"` (default) or `\"delegate\"`. */\n bridgeMode: string;\n codexCompat: CodexCompatConfig;\n /** When true (default), LLM classifies facts as `\"project\"` or `\"global\"` scope for promotion across projects. */\n extractionScopeClassificationEnabled: boolean;\n subjectClassification: { enabled: boolean };\n subjectGuard: SubjectGuardMode;\n /** Min accessCount reuse signal for promotion candidates (issue #2372). Default 3. */\n promotionCandidates: { minAccessCount: number };\n // Extraction judge (issue #376)\n /** Enable the LLM-as-judge fact-worthiness gate on extracted facts. Default false (opt-in). */\n extractionJudgeEnabled: boolean;\n /** Model override for the judge LLM. Empty string means use the local model. */\n extractionJudgeModel: string;\n /** Maximum number of candidate facts per judge LLM batch call. */\n extractionJudgeBatchSize: number;\n /**\n * Per-call timeout for the judge LLM (ms). Default 20000: measured 2.1-2.5 s\n * per compact verdict call (completion budget 24 tokens per candidate, up\n * to 2048) on a small fast judge model; the big shared core model measured\n * 13-22 s per compact call, 41 s verbose — raise it there. The earlier 150s\n * default only papered over 59-305s verbose replies; sub-second budgets\n * abort every call. On the local LLM path each attempt lasts\n * min(localLlmTimeoutMs, extractionJudgeTimeoutMs) (localLlmTimeoutMs\n * defaults to 180000), so a value above localLlmTimeoutMs does not lengthen\n * an attempt: raise both settings for a slower judge model.\n */\n extractionJudgeTimeoutMs: number;\n /** Shadow mode: log judge verdicts but do not filter facts. Default false. */\n extractionJudgeShadow: boolean;\n /**\n * Maximum number of times the same candidate text may be deferred before\n * the judge forcibly converts the verdict to `\"reject\"`. Prevents\n * pathological LLM responses from looping forever on ambiguous facts.\n * Defaults to 2 (issue #562, PR 2).\n */\n extractionJudgeMaxDeferrals: number;\n /**\n * Emit structured telemetry rows to\n * `state/observation-ledger/extraction-judge-verdicts.jsonl` on every\n * judge verdict. Off by default; enable to collect defer-rate / latency\n * metrics for operator dashboards (issue #562, PR 3).\n */\n extractionJudgeTelemetryEnabled: boolean;\n /**\n * Collect `(candidate_text, verdict_kind, reason)` tuples into\n * `~/.remnic/judge-training/<date>.jsonl` for use by a future GRPO\n * training pipeline (issue #562, PR 4). Off by default. Rows live in\n * the user's home directory rather than the shared memory directory so\n * they are not committed, sync'd, or bundled into memory exports.\n */\n collectJudgeTrainingPairs: boolean;\n /**\n * Override directory for judge training-pair collection. Empty string\n * means use the default (`~/.remnic/judge-training`). Primarily for\n * tests and for operators who want the output to land in a specific\n * location.\n */\n judgeTrainingDir: string;\n // Extraction faithfulness gate (issue #1576). Entailment-verification of\n // extracted facts against their verified source spans from #1575.\n /** Gate mode: \"off\" (default, byte-identical pre-feature), \"shadow\" (record only), \"enforce\" (route to pending_review). */\n extractionFaithfulnessGate: \"off\" | \"shadow\" | \"enforce\";\n /** Override model name; empty string = default routing chain. */\n extractionFaithfulnessModel: string;\n /**\n * Base URL of a local openai-compatible endpoint serving a fine-tuned\n * faithfulness gate (issue #1585 model-lab), e.g.\n * `http://localhost:11434/v1` (Ollama) or `http://localhost:8000/v1` (vLLM).\n * Empty string (default) preserves the existing routing chain exactly;\n * set together with `extractionFaithfulnessModel` to route the gate to the\n * local model first, falling back to the chain on failure.\n */\n extractionFaithfulnessBaseUrl: string;\n /** Context window (chars) around the quote sent to the verifier. Default 400. */\n extractionFaithfulnessContextChars: number;\n /** Per-batch timeout in ms; timeout → tagged error, never blocks writes. Default 8000. */\n extractionFaithfulnessTimeoutMs: number;\n /**\n * Resilient-fallback toggle for the local model-lab endpoint (issue #1700\n * nit #6). When the local endpoint returns 200 with non-verdict JSON, the\n * default (false) surfaces `malformed_output` so a misconfigured endpoint is\n * visible to the operator. Set true to instead fall back to the configured\n * chain on local parse failure (pre-validated via parseFaithfulnessResponse).\n */\n extractionFaithfulnessLocalParseFallback: boolean;\n /**\n * Correction-intent model-lab pointer (issue #1581 / #1585). When both this\n * and `correctionIntentBaseUrl` are set, the detection path can route to a\n * local fine-tuned correction-intent classifier. Empty default keeps the\n * rule-based passive-correction detector as the sole path (byte-identical).\n */\n correctionIntentModel: string;\n /** Base URL of a local openai-compatible endpoint serving the correction-intent classifier (issue #1585). Empty default = rule-based detection. */\n correctionIntentBaseUrl: string;\n // Hourly summaries\n hourlySummariesEnabled: boolean;\n daySummaryEnabled: boolean;\n /** Timezone override for the day summary cron (issue #1475). */\n daySummaryTimezone?: string;\n /** If true, Engram may attempt to auto-register an hourly summary cron job (default off). */\n hourlySummaryCronAutoRegister: boolean;\n /** If true, Engram may attempt to auto-register the nightly governance cron job (default off). */\n nightlyGovernanceCronAutoRegister: boolean;\n summaryRecallHours: number;\n maxSummaryCount: number;\n summaryModel: string;\n // v2.4 Extended hourly summaries\n hourlySummariesExtendedEnabled: boolean;\n hourlySummariesIncludeToolStats: boolean;\n hourlySummariesIncludeSystemMessages: boolean;\n hourlySummariesMaxTurnsPerRun: number;\n // v2.4 Conversation index (optional)\n conversationIndexEnabled: boolean;\n conversationIndexBackend: \"qmd\" | \"faiss\";\n conversationIndexQmdCollection: string;\n conversationIndexRetentionDays: number;\n conversationIndexMinUpdateIntervalMs: number;\n conversationIndexEmbedOnUpdate: boolean;\n conversationIndexFaissScriptPath?: string;\n conversationIndexFaissPythonBin?: string;\n conversationIndexFaissModelId: string;\n conversationIndexFaissIndexDir: string;\n conversationIndexFaissUpsertTimeoutMs: number;\n conversationIndexFaissSearchTimeoutMs: number;\n conversationIndexFaissHealthTimeoutMs: number;\n conversationIndexFaissMaxBatchSize: number;\n conversationIndexFaissMaxSearchK: number;\n conversationRecallTopK: number;\n conversationRecallMaxChars: number;\n conversationRecallTimeoutMs: number;\n // Evaluation harness foundation\n evalHarnessEnabled: boolean;\n evalShadowModeEnabled: boolean;\n benchmarkBaselineSnapshotsEnabled: boolean;\n benchmarkDeltaReporterEnabled: boolean;\n benchmarkStoredBaselineEnabled: boolean;\n evalStoreDir: string;\n // Objective-state memory foundation\n objectiveStateMemoryEnabled: boolean;\n objectiveStateSnapshotWritesEnabled: boolean;\n objectiveStateRecallEnabled: boolean;\n objectiveStateStoreDir: string;\n // Causal trajectory memory foundation\n causalTrajectoryMemoryEnabled: boolean;\n causalTrajectoryStoreDir: string;\n causalTrajectoryRecallEnabled: boolean;\n actionGraphRecallEnabled: boolean;\n // Trust-zone memory foundation\n trustZonesEnabled: boolean;\n quarantinePromotionEnabled: boolean;\n trustZoneStoreDir: string;\n trustZoneRecallEnabled: boolean;\n memoryPoisoningDefenseEnabled: boolean;\n memoryRedTeamBenchEnabled: boolean;\n // Harmonic retrieval foundation\n harmonicRetrievalEnabled: boolean;\n abstractionAnchorsEnabled: boolean;\n abstractionNodeStoreDir: string;\n episodicContextEnabled: boolean;\n // Episodic/semantic split foundation\n verifiedRecallEnabled: boolean;\n semanticRulePromotionEnabled: boolean;\n semanticRuleVerificationEnabled: boolean;\n semanticConsolidationEnabled: boolean;\n semanticConsolidationModel: string;\n semanticConsolidationThreshold: number;\n semanticConsolidationMinClusterSize: number;\n semanticConsolidationExcludeCategories: string[];\n semanticConsolidationIntervalHours: number;\n semanticConsolidationMaxPerRun: number;\n /**\n * When true (default), semantic-consolidation prompts the LLM with an\n * operator-aware format asking for JSON `{operator, output}` and records\n * the resulting SPLIT/MERGE/UPDATE operator on `derived_via`. When\n * false, falls back to the legacy plain-text prompt — `derived_via` is\n * still populated via the cluster-shape heuristic in\n * `chooseConsolidationOperator`. Issue #561 PR 3.\n */\n operatorAwareConsolidationEnabled: boolean;\n // Pattern reinforcement (issue #687 PR 2/4)\n /**\n * When true, the pattern-reinforcement maintenance job runs on its\n * configured cadence and clusters duplicate non-procedural memories\n * by normalized content. Clusters with `>= patternReinforcementMinCount`\n * members produce a canonical (most-recent) memory tagged with\n * `reinforcement_count` + `last_reinforced_at`; the older duplicates\n * are marked `superseded` and pointed at the canonical. Default\n * `false` — opt-in until bench validation lands.\n */\n patternReinforcementEnabled: boolean;\n /**\n * Minimum interval (ms) between pattern-reinforcement runs. Default\n * `7 * 24 * 60 * 60 * 1000` (7 days). Set to `0` to disable cadence\n * gating (useful for tests / manual invocation).\n */\n patternReinforcementCadenceMs: number;\n /**\n * Minimum cluster size before pattern reinforcement promotes a\n * canonical and supersedes duplicates. Default `3`. Clamped to\n * `>= 2` at config-parse time — a \"cluster of 1\" is just a single\n * memory and a \"cluster of 0\" is meaningless.\n */\n patternReinforcementMinCount: number;\n /**\n * Memory categories the pattern-reinforcement job considers.\n * Default `[\"preference\", \"fact\", \"decision\"]`. The job\n * intentionally skips procedural memories so it stays disjoint from\n * the procedural mining pipeline.\n */\n patternReinforcementCategories: string[];\n /** issue #687 PR 3/4: opt-in recall score boost for reinforced memories. Default false. */\n reinforcementRecallBoostEnabled: boolean;\n /** Score bonus per unit of reinforcement_count. Range [0, 1]. Default 0.05. */\n reinforcementRecallBoostWeight: number;\n /** Maximum additive reinforcement boost per result. Range [0, 1]. Default 0.3. */\n reinforcementRecallBoostMax: number;\n /**\n * Async peer profile reasoner — issue #679 PR 2/5.\n *\n * Default `false` (opt-in). When enabled, the reasoner runs after\n * `runSemanticConsolidation` (the REM phase of the dreams pipeline)\n * and updates per-peer profile.md files with provenance-tagged\n * field updates derived from the peer's interaction log.\n */\n peerProfileReasonerEnabled: boolean;\n /**\n * Model identifier used by the peer profile reasoner. Logged for\n * telemetry only — actual dispatch is via the same FallbackLlmClient\n * the orchestrator uses for semantic consolidation. Default `gpt-5.5`.\n */\n peerProfileReasonerModel: string;\n /**\n * Minimum new interaction-log entries a peer must accumulate since\n * the previous reasoner run before being processed again. Default 5.\n * Setting to 0 forces every run to consider every peer.\n */\n peerProfileReasonerMinInteractions: number;\n /**\n * Hard cap on the total number of profile fields the reasoner will\n * apply across all peers in a single run. Default 8.\n */\n peerProfileReasonerMaxFieldsPerRun: number;\n /**\n * When true, inject the active peer's profile fields into the recall\n * context as a \"## Peer Profile\" section. Default false (opt-in,\n * Gotcha #30/#48 — least-privileged default). Requires the session's\n * peer ID to be registered via `setPeerIdForSession` before recall.\n */\n peerProfileRecallEnabled: boolean;\n /**\n * Maximum number of peer profile fields to inject per recall. Only\n * the most-recently-updated N fields are included to keep the context\n * budget predictable. Default 5. Setting to 0 disables field\n * injection even when `peerProfileRecallEnabled` is true.\n */\n peerProfileRecallMaxFields: number;\n // Creation-memory foundation\n creationMemoryEnabled: boolean;\n memoryUtilityLearningEnabled: boolean;\n promotionByOutcomeEnabled: boolean;\n commitmentLedgerEnabled: boolean;\n commitmentLifecycleEnabled: boolean;\n commitmentStaleDays: number;\n commitmentLedgerDir: string;\n resumeBundlesEnabled: boolean;\n resumeBundleDir: string;\n workProductRecallEnabled: boolean;\n workProductLedgerDir: string;\n workTasksEnabled: boolean;\n workProjectsEnabled: boolean;\n workTasksDir: string;\n workProjectsDir: string;\n workIndexEnabled: boolean;\n workIndexDir: string;\n workTaskIndexEnabled: boolean;\n workProjectIndexEnabled: boolean;\n workIndexAutoRebuildEnabled: boolean;\n workIndexAutoRebuildDebounceMs: number;\n\n // Observability\n /** If true, log slow operations (local LLM + related I/O) with durations and metadata (no content). */\n slowLogEnabled: boolean;\n /**\n * If true, include the full recalled memory text in `RecallTraceEvent.recalledContent`.\n * Disabled by default — enable only when you want external trace subscribers (e.g. Langfuse)\n * to see the exact memory context injected into each conversation turn.\n * This adds payload to trace events but does not log to files or the gateway log.\n */\n traceRecallContent: boolean;\n /** Threshold for slow operation logging (ms). */\n slowLogThresholdMs: number;\n // Performance profiling (opt-in)\n /** If true, collect and persist timing traces for recall and extraction pipelines. */\n profilingEnabled: boolean;\n /** Directory for profiling trace JSONL files. Defaults to <memoryDir>/profiling. */\n profilingStorageDir: string;\n /** Maximum number of trace files to keep (rolling window). */\n profilingMaxTraces: number;\n // Extraction stability guards (P0/P1).\n extractionDedupeEnabled: boolean;\n extractionSourceGroundingEnabled: boolean;\n extractionDedupeWindowMs: number;\n extractionMinChars: number;\n extractionMinUserTurns: number;\n /**\n * When true, skip semantic memory extraction for mechanical action/state\n * telemetry transcripts that have no durable-memory cue. Raw transcript\n * storage/recall still runs; this only prevents expensive low-value fact\n * extraction over state logs. Default true.\n */\n extractionTelemetryPrefilterEnabled: boolean;\n /**\n * When true, LCM uses deterministic compression instead of semantic LLM\n * summarization for mechanical action/state telemetry transcripts with no\n * durable-memory cue. Raw transcript storage/recall still runs. Default true.\n */\n lcmTelemetryPrefilterEnabled: boolean;\n extractionMaxTurnChars: number;\n extractionMaxFactsPerRun: number;\n extractionMaxEntitiesPerRun: number;\n extractionMaxQuestionsPerRun: number;\n extractionMaxProfileUpdatesPerRun: number;\n /**\n * Minimum importance level required to persist an extracted fact. Facts\n * whose locally-scored level falls below this threshold are dropped before\n * write and counted toward the `importance_gated` metric. Defaults to\n * \"low\" so trivial content (greetings, single-word replies, filler) is\n * silently dropped while everything else still passes.\n */\n extractionMinImportanceLevel: ImportanceLevel;\n /**\n * Inline source attribution (issue #369).\n * When enabled, extracted facts carry a compact provenance tag (agent,\n * session, timestamp) inlined into the fact text — not just in YAML\n * frontmatter — so the citation survives hostile memory text, copy/paste,\n * and LLM quoting. Off by default to preserve backwards compatibility\n * with existing downstream consumers that expect raw fact text.\n */\n inlineSourceAttributionEnabled: boolean;\n /**\n * Template used when injecting inline citations. Supported placeholders:\n * `{agent}`, `{session}`, `{sessionId}`, `{ts}`, `{date}`. Defaults to\n * `[Source: agent={agent}, session={sessionId}, ts={ts}]`.\n */\n inlineSourceAttributionFormat: string;\n /**\n * Prior citation templates still recognized after a format change.\n * Empty when the operator has never changed the template.\n */\n inlineSourceAttributionFormatHistory: string[];\n consolidationRequireNonZeroExtraction: boolean;\n consolidationMinIntervalMs: number;\n // QMD maintenance (debounced singleflight)\n qmdMaintenanceEnabled: boolean;\n qmdMaintenanceDebounceMs: number;\n /**\n * Namespace-aware maintenance fanout (issue #1500). When namespaces are\n * enabled, background maintenance jobs use the rebuildable namespace catalog\n * to discover dynamic project/team namespaces rather than only processing the\n * configured default/shared/policy set.\n */\n maintenanceNamespaceFanoutEnabled: boolean;\n maintenanceMaxNamespacesPerCycle: number;\n maintenanceIncludeProjectNamespaces: boolean;\n maintenanceIncludeBranchNamespaces: boolean;\n maintenanceIncludeTeamProjectNamespaces: boolean;\n maintenanceNamespaceLockStaleMs: number;\n qmdAutoEmbedEnabled: boolean;\n qmdEmbedMinIntervalMs: number;\n qmdUpdateTimeoutMs: number;\n qmdUpdateMinIntervalMs: number;\n // Local LLM resilience\n localLlmRetry5xxCount: number;\n localLlmRetryBackoffMs: number;\n localLlm400TripThreshold: number;\n localLlm400CooldownMs: number;\n /** COS-447: retries for rate-limit responses (429/503/408, LiteLLM cooldown). Default 3. */\n localLlmRateLimitRetryCount?: number;\n /** COS-447: base delay for exponential full-jitter rate-limit backoff (ms). Default 1000. */\n localLlmRateLimitBackoffBaseMs?: number;\n /** COS-447: ceiling for rate-limit backoff; longer server hints skip the retry (ms). Default 60000. */\n localLlmRateLimitBackoffMaxMs?: number;\n /** Process-wide local LLM HTTP cap; above 1, reserves one permit for recall-critical work. 0 = unlimited. Default 4. */\n localLlmMaxConcurrency?: number;\n /** Ollama-backend thinking suppression: `reasoning_effort` value injected on\n * `/v1/chat/completions` for THINKING_SUPPRESSED_OPERATIONS (\"none\" | \"low\" |\n * \"medium\" | \"high\" | \"max\"; \"\" disables injection). Ollama ignores\n * `chat_template_kwargs`, so this is the only think-off dialect it honors.\n * Issue #1996. */\n localLlmReasoningEffort: string;\n /**\n * Maximum transcript length that re-enables thinking for local extraction.\n * Set to `0` to keep thinking suppressed for every extraction. Default:\n * `3000`. Consolidation is unaffected.\n */\n localLlmThinkingThresholdChars: number;\n // Extraction retry/backoff + circuit breaker (extraction hot-loop hardening)\n /** Master gate. When false, restores pre-change behavior (extractor called on every triggered observe; no gate). */\n extractionRetryEnabled: boolean;\n /** Per-fingerprint exponential backoff schedule (ms), indexed by attempt number. */\n extractionRetryScheduleMs: number[];\n /** Upper bound on any single backoff interval (ms). */\n extractionRetryMaxBackoffMs: number;\n /** Jitter ratio applied to each backoff interval (±ratio). */\n extractionRetryJitterRatio: number;\n /** Max attempts for a `parse_empty` fingerprint before it is long-parked. */\n extractionParseEmptyMaxAttempts: number;\n /** Consecutive provider failures before the circuit breaker opens. */\n extractionBreakerFailureThreshold: number;\n /** Breaker open cooldown (ms) for transient provider failures. */\n extractionBreakerCooldownMs: number;\n /** Breaker open cooldown (ms) for auth/config failures (longer — provider is misconfigured). */\n extractionBreakerAuthCooldownMs: number;\n /**\n * Consecutive failed extraction attempts after which the failing\n * trigger-drain chunk is dropped (with a side-file copy under\n * `state/dropped/`) so a session whose window never extracts cannot stall\n * the buffer forever. Default: `5`.\n */\n extractionChunkDropAfterFailures: number;\n // Local LLM fast tier (v9.1) — smaller model for quick ops\n localLlmFastEnabled: boolean;\n localLlmFastModel: string;\n localLlmFastUrl: string;\n localLlmFastTimeoutMs: number;\n /**\n * Suppress chain-of-thought / thinking mode on the main local LLM\n * (issue #548). When true, Remnic injects\n * `chat_template_kwargs: { enable_thinking: false }` on every\n * request so thinking-capable models (Qwen 3.5, Gemma 4, DeepSeek,\n * etc.) skip reasoning tokens that structured-output tasks like\n * extraction and consolidation cannot benefit from. Default: true\n * — the dominant localLlm use case is JSON-shaped extraction where\n * thinking is pure latency tax and a common cause of 60s timeouts.\n * Set to false to restore thinking for narrative tasks.\n *\n * The fast-tier client (`fastLlm`) always disables thinking; that\n * contract is baked into \"fast tier\" and is unaffected by this flag.\n */\n localLlmDisableThinking: boolean;\n // Gateway config for fallback AI\n gatewayConfig?: GatewayConfig;\n /**\n * Optional host-supplied resolver for provider API keys. Core never discovers\n * host runtimes directly; adapters inject their native secret resolver here.\n */\n providerApiKeyResolver?: (params: {\n provider: string;\n cfg?: unknown;\n agentDir?: string;\n }) => Promise<{ apiKey?: string; source?: string; mode?: string } | null>;\n /**\n * Optional host-supplied resolver for request-ready model auth, including\n * provider transforms such as OAuth token exchange or base URL overrides.\n */\n runtimeAuthForModelResolver?: (params: {\n model: { provider: string; id: string; api?: string; baseUrl?: string };\n cfg?: unknown;\n workspaceDir?: string;\n }) => Promise<{\n apiKey?: string;\n baseUrl?: string;\n source?: string;\n mode?: string;\n profileId?: string;\n } | null>;\n // Gateway model source (v9.2) — route LLM calls through gateway agent model chain\n modelSource: \"plugin\" | \"gateway\";\n gatewayAgentId: string;\n fastGatewayAgentId: string;\n /**\n * Optional task-specific model chain for Remnic's background LLM work —\n * extraction, the extraction judge, fact/profile/identity consolidation,\n * summarization, semantic consolidation, calibration, and causal\n * consolidation. When set, this chain is resolved through gateway providers\n * instead of using gatewayAgentId or agents.defaults.model. All task paths\n * route through the shared `gatewayTaskChainOptions` helper.\n * LCM summarization is the one fast-tier exception: if `fastGatewayAgentId`\n * is configured, LCM uses that persona before falling back to this chain.\n *\n * Only takes effect when `modelSource: \"gateway\"`; ignored (with a startup\n * warning) under `modelSource: \"plugin\"`. See issue #1365 / PR #1370.\n */\n taskModelChain?: AgentPersonaModelConfig;\n\n // v3.0 Multi-agent memory (namespaces)\n namespacesEnabled: boolean;\n /**\n * Enable the rebuildable namespace catalog (issue #1499). When enabled and\n * `namespacesEnabled` is also true, storage resolution and read/write paths\n * record cheap, failure-tolerant namespace touches in\n * `<memoryDir>/state/namespaces.jsonl`. The catalog is downstream metadata —\n * filesystem memory remains the source of truth, and the catalog is fully\n * rebuildable from disk via `remnic namespaces rebuild`. The catalog is inert\n * (a no-op that enumerates nothing) whenever `namespacesEnabled` is false, so\n * existing single-namespace behavior is unchanged. Default on.\n */\n namespaceCatalogEnabled: boolean;\n /**\n * Size (bytes) at which a namespace-catalog touch triggers an in-lock\n * auto-compaction of `state/namespaces.jsonl` (issue #1903). The append-only\n * log grows one line per touch; once it exceeds this many bytes the next\n * touch folds it (last-record-wins, every namespace row preserved) and\n * atomically rewrites it. Default `16 * 1024 * 1024` (16 MB). `0` disables\n * auto-compaction (restores the pre-#1903 unbounded-append behavior).\n */\n namespacesCatalogCompactBytes: number;\n /**\n * Coalescing window (ms) for pure-timestamp READ touches on an\n * already-known namespace record (issue #1903). Repeated `markRead` touches\n * that only refresh `lastReadAt` are buffered per (namespace, kind) and\n * flushed once per window instead of appending a line each time.\n * Semantically-observable touches (first sight, provenance/field changes)\n * always flush immediately. Default `60000`. `0` disables read coalescing\n * (each touch appends immediately, the pre-#1903 behavior).\n */\n namespacesCatalogReadTouchCoalesceMs: number;\n /**\n * Coalescing window (ms) for pure-timestamp WRITE touches on an\n * already-known namespace record (issue #1903). Mirrors\n * `namespacesCatalogReadTouchCoalesceMs` for `markWrite`/`lastWriteAt`.\n * Default `1000`. `0` disables write coalescing.\n */\n namespacesCatalogWriteTouchCoalesceMs: number;\n /**\n * Whether pure state-file writes (`state/buffer.json`, ledgers, indexes)\n * touch the namespace catalog (issue #1903). State files are not namespace\n * memory data, so by default their writes do NOT record a catalog touch.\n * Set `true` to restore the pre-#1903 behavior where every secure-file\n * write touched the catalog. Default `false`.\n */\n namespacesCatalogTouchStateWrites: boolean;\n defaultNamespace: string;\n sharedNamespace: string;\n principalFromSessionKeyMode: PrincipalFromSessionKeyMode;\n principalFromSessionKeyRules: PrincipalRule[];\n namespacePolicies: NamespacePolicy[];\n defaultRecallNamespaces: Array<\"self\" | \"shared\">;\n scopeProfiles: Record<string, ScopeProfileConfig>;\n defaultScopeProfile: string | undefined;\n teams: Record<string, ScopeTeamConfig>;\n cronRecallMode: CronRecallMode;\n cronRecallAllowlist: string[];\n cronRecallPolicyEnabled: boolean;\n cronRecallNormalizedQueryMaxChars: number;\n cronRecallInstructionHeavyTokenCap: number;\n cronConversationRecallMode: CronConversationRecallMode;\n autoPromoteToSharedEnabled: boolean;\n autoPromoteToSharedCategories: Array<\"fact\" | \"correction\" | \"decision\" | \"preference\">;\n autoPromoteMinConfidenceTier: ConfidenceTier;\n routingRulesEnabled: boolean;\n routingRulesStateFile: string;\n\n // v4.0 Shared-context (cross-agent shared intelligence)\n sharedContextEnabled: boolean;\n sharedContextDir?: string;\n sharedContextMaxInjectChars: number;\n sharedContextAllowBindingAuthority: boolean;\n crossSignalsSemanticEnabled: boolean;\n crossSignalsSemanticTimeoutMs: number;\n sharedCrossSignalSemanticEnabled?: boolean;\n sharedCrossSignalSemanticTimeoutMs?: number;\n sharedCrossSignalSemanticMaxCandidates?: number;\n\n // v5.0 Compounding engine\n compoundingEnabled: boolean;\n compoundingWeeklyCronEnabled: boolean;\n compoundingSemanticEnabled: boolean;\n compoundingSynthesisTimeoutMs: number;\n compoundingInjectEnabled: boolean;\n\n // IRC (Inductive Rule Consolidation) — preference synthesis\n ircEnabled: boolean;\n ircMaxPreferences: number;\n ircIncludeCorrections: boolean;\n ircMinConfidence: number;\n\n // CMC (Causal Memory Consolidation) — cross-session causal reasoning\n cmcEnabled: boolean;\n cmcStitchLookbackDays: number;\n cmcStitchMinScore: number;\n cmcStitchMaxEdgesPerTrajectory: number;\n cmcConsolidationEnabled: boolean;\n cmcConsolidationMinRecurrence: number;\n cmcConsolidationMinSessions: number;\n cmcConsolidationSuccessThreshold: number;\n cmcRetrievalEnabled: boolean;\n cmcRetrievalMaxDepth: number;\n cmcRetrievalMaxChars: number;\n cmcRetrievalCounterfactualBoost: number;\n cmcBehaviorLearningEnabled: boolean;\n cmcBehaviorMinFrequency: number;\n cmcBehaviorMinSessions: number;\n cmcBehaviorConfidenceThreshold: number;\n cmcLifecycleCausalImpactWeight: number;\n\n // PEDC (Prediction-Error-Driven Calibration) — model-user alignment\n calibrationEnabled: boolean;\n calibrationMaxRulesPerRecall: number;\n calibrationMaxChars: number;\n\n // Search backend abstraction\n searchBackend?: \"qmd\" | \"remote\" | \"noop\" | \"lancedb\" | \"meilisearch\" | \"orama\";\n remoteSearchBaseUrl?: string;\n remoteSearchApiKey?: string;\n remoteSearchTimeoutMs?: number;\n\n // LanceDB backend\n lancedbEnabled: boolean;\n lanceDbPath?: string;\n lanceEmbeddingDimension?: number;\n\n // Meilisearch backend\n meilisearchEnabled: boolean;\n meilisearchHost?: string;\n meilisearchApiKey?: string;\n meilisearchTimeoutMs?: number;\n meilisearchAutoIndex?: boolean;\n\n // Orama backend\n oramaEnabled: boolean;\n oramaDbPath?: string;\n oramaEmbeddingDimension?: number;\n /** Segment space-free scripts (CJK/Thai) in the Orama lexical index (issue #2187). Default `true`. */\n oramaCjkSegmentation?: boolean;\n\n // QMD daemon mode\n qmdDaemonEnabled: boolean;\n qmdDaemonUrl: string;\n qmdDaemonRecheckIntervalMs: number;\n qmdIntentHintsEnabled: boolean;\n qmdExplainEnabled: boolean;\n\n // v7.0 Knowledge Graph Enhancement\n knowledgeIndexEnabled: boolean;\n knowledgeIndexMaxEntities: number;\n knowledgeIndexMaxChars: number;\n entityRetrievalEnabled: boolean;\n entityRetrievalMaxChars: number;\n entityRetrievalMaxHints: number;\n entityRetrievalMaxSupportingFacts: number;\n entityRetrievalMaxRelatedEntities: number;\n entityRetrievalRecentTurns: number;\n entitySchemas?: Record<string, EntitySchemaDefinition>;\n // Recall assembly controls\n recallBudgetChars: number;\n /** Maximum fraction of the total recall budget that the behavioral profile may consume. */\n recallProfileMaxRatio: number;\n recallOuterTimeoutMs: number;\n recallCoreDeadlineMs: number;\n recallEnrichmentDeadlineMs: number;\n recallPipeline: RecallSectionConfig[];\n /** Apply Maximal Marginal Relevance to the final recall selection per-section. */\n recallMmrEnabled: boolean;\n /** MMR λ parameter. 1.0 = pure relevance, 0.0 = pure diversity. Default 0.7. */\n recallMmrLambda: number;\n /** MMR is applied over the top N candidates per section. Default 40. */\n recallMmrTopN: number;\n /**\n * Boost stored `reasoning_trace` memories in recall results when the\n * incoming query reads like a problem-solving ask (e.g. \"how do I…\",\n * \"step by step\", \"walk me through…\"). Default false — opt in after\n * benchmarking (issue #564 PR 3).\n */\n recallReasoningTraceBoostEnabled: boolean;\n /**\n * Issue #1582 — append stable short handles (`[m:4f2a]`) to injected memory\n * lines at recall render time. Default false: off = byte-identical injection\n * (rule 39). Token cost ~9 chars/memory; flip the default only with #1574\n * evidence that answer quality is unaffected (rule 55).\n */\n recallMemoryHandles: boolean;\n /**\n * Issue #1582 — how many recent per-session recall snapshots (newest first)\n * handle resolution searches. Older-than-N snapshots are not searched and a\n * miss is tagged rather than widening the window.\n */\n recallHandleSnapshotDepth: number;\n qmdRecallCacheTtlMs: number;\n qmdRecallCacheStaleTtlMs: number;\n qmdRecallCacheMaxEntries: number;\n entityRelationshipsEnabled: boolean;\n entityActivityLogEnabled: boolean;\n entityActivityLogMaxEntries: number;\n entityAliasesEnabled: boolean;\n entitySummaryEnabled: boolean;\n entitySynthesisMaxTokens: number;\n\n // v6.0 Fact deduplication & archival\n /** Enable content-hash deduplication to prevent storing semantically identical facts. */\n factDeduplicationEnabled: boolean;\n semanticDedupEnabled: boolean;\n semanticDedupThreshold: number;\n semanticDedupCandidates: number;\n semanticMerge: SemanticMergeConfig; // judge-mediated merge-on-write (#2330); default off\n noveltyGateEnabled: boolean;\n noveltyAddThreshold: number;\n noveltyNoopThreshold: number;\n /** Enable automatic archival of old, low-importance, rarely-accessed facts. */\n factArchivalEnabled: boolean;\n /** Minimum age in days before a fact is eligible for archival. */\n factArchivalAgeDays: number;\n /** Maximum importance score for archival eligibility (0-1). Only facts below this are archived. */\n factArchivalMaxImportance: number;\n /** Maximum access count for archival eligibility. Only rarely-accessed facts are archived. */\n factArchivalMaxAccessCount: number;\n /** Tags that protect a fact from archival regardless of other criteria. */\n factArchivalProtectedCategories: string[];\n // v8.3 Lifecycle policy engine\n lifecyclePolicyEnabled: boolean;\n lifecycleFilterStaleEnabled: boolean;\n lifecyclePromoteHeatThreshold: number;\n lifecycleStaleDecayThreshold: number;\n lifecycleArchiveDecayThreshold: number;\n lifecycleProtectedCategories: MemoryCategory[];\n lifecycleMetricsEnabled: boolean;\n // v8.3 proactive + policy learning\n proactiveExtractionEnabled: boolean;\n contextCompressionActionsEnabled: boolean;\n compressionGuidelineLearningEnabled: boolean;\n compressionGuidelineSemanticRefinementEnabled: boolean;\n compressionGuidelineSemanticTimeoutMs: number;\n maxProactiveQuestionsPerExtraction: number;\n proactiveExtractionTimeoutMs: number;\n proactiveExtractionSkipWhenLocalLlmBusy: boolean;\n proactiveExtractionMaxTokens: number;\n extractionMaxOutputTokens: number;\n proactiveExtractionCategoryAllowlist?: MemoryCategory[];\n maxCompressionTokensPerHour: number;\n behaviorLoopAutoTuneEnabled: boolean;\n behaviorLoopLearningWindowDays: number;\n behaviorLoopMinSignalCount: number;\n behaviorLoopMaxDeltaPerCycle: number;\n behaviorLoopProtectedParams: string[];\n // v8.0 Phase 1: recall planner + intent routing + verbatim artifacts\n recallPlannerEnabled: boolean;\n /**\n * Opt-in gate for LLM-based recall planning (issue #1367 / Option C). When\n * false (the default) recall mode is decided by the regex heuristic\n * `planRecallMode()`. When true, the planner consults an LLM (routed through\n * the gateway/fallback chain — provider-agnostic) and falls back to the\n * heuristic on timeout/error/unavailable. `recallPlannerShadowMode` runs the\n * LLM for telemetry only without changing the effective mode.\n */\n recallPlannerLlmEnabled: boolean;\n recallPlannerModel: string;\n recallPlannerTimeoutMs: number;\n recallPlannerUseResponsesApi: boolean;\n recallPlannerMaxPromptChars: number;\n recallPlannerMaxMemoryHints: number;\n recallPlannerShadowMode: boolean;\n recallPlannerTelemetryEnabled: boolean;\n recallPlannerMaxQmdResultsMinimal: number;\n recallPlannerMaxQmdResultsFull: number;\n intentRoutingEnabled: boolean;\n intentRoutingBoost: number;\n verbatimArtifactsEnabled: boolean;\n verbatimArtifactsMinConfidence: number;\n verbatimArtifactsMaxRecall: number;\n verbatimArtifactCategories: MemoryCategory[];\n // v8.0 Phase 2A: Memory Boxes + Trace Weaving\n memoryBoxesEnabled: boolean;\n /** Jaccard overlap threshold below which a topic shift triggers box sealing (0-1, default 0.35) */\n boxTopicShiftThreshold: number;\n /** Time gap in ms before an open box is sealed (default 30 min) */\n boxTimeGapMs: number;\n /** Max memories per box before forced seal */\n boxMaxMemories: number;\n traceWeaverEnabled: boolean;\n /** Days back to search for trace links */\n traceWeaverLookbackDays: number;\n /** Minimum Jaccard overlap to assign the same traceId (0-1, default 0.4) */\n traceWeaverOverlapThreshold: number;\n /** Number of recent days of boxes to inject during recall */\n boxRecallDays: number;\n // v8.0 Phase 2B: Episode/Note dual store (HiMem)\n /** Classify extracted memories as episode or note and tag with memoryKind */\n episodeNoteModeEnabled: boolean;\n // v8.1 Temporal + Tag Indexes (SwiftMem-inspired)\n /** Build and maintain temporal (state/index_time.json) and tag (state/index_tags.json) indexes */\n queryAwareIndexingEnabled: boolean;\n /** Max candidate paths returned from index prefilter (0 = no cap) */\n queryAwareIndexingMaxCandidates: number;\n temporalIndexWindowDays: number;\n temporalIndexMaxEntries: number;\n temporalBoostRecentDays: number;\n temporalBoostScore: number;\n temporalDecayEnabled: boolean;\n tagMemoryEnabled: boolean;\n tagMaxPerMemory: number;\n tagIndexMaxEntries: number;\n tagRecallBoost: number;\n tagRecallMaxMatches: number;\n // v8.2 multi-graph memory (PR 18)\n multiGraphMemoryEnabled: boolean;\n // Incremental GraphIndex edge cache (issue #1904). When true (default), a\n // single-writer edge append is pushed into the warm edge cache in place\n // (revalidated by file size for cross-process coherence) instead of nulling\n // the 6 MB / 30k-edge cache and re-reading + re-parsing it (2-4 s). Set false\n // to restore the pre-#1904 behavior of nulling the edge cache on every write.\n graphEdgeCacheIncrementalEnabled: boolean;\n // v8.2 PR 19A: graph recall planner gating\n graphRecallEnabled: boolean;\n graphRecallMaxExpansions: number;\n graphRecallMaxPerSeed: number;\n graphRecallMinEdgeWeight: number;\n graphRecallShadowEnabled: boolean;\n graphRecallSnapshotEnabled: boolean;\n graphRecallShadowSampleRate: number;\n graphRecallExplainToolEnabled: boolean;\n graphRecallStoreColdMirror: boolean;\n graphRecallColdMirrorCollection?: string;\n graphRecallColdMirrorMinAgeDays: number;\n graphRecallUseEntityPriors: boolean;\n graphRecallEntityPriorBoost: number;\n graphRecallPreferHubSeeds: boolean;\n graphRecallHubBias: number;\n graphRecallRecencyHalfLifeDays: number;\n graphRecallDampingFactor: number;\n graphRecallMaxSeedNodes: number;\n graphRecallMaxExpandedNodes: number;\n graphRecallMaxTrailPerNode: number;\n graphRecallMinSeedScore: number;\n graphRecallExpansionScoreThreshold: number;\n graphRecallExplainMaxPaths: number;\n graphRecallExplainMaxChars: number;\n graphRecallExplainEdgeLimit: number;\n graphRecallExplainEnabled: boolean;\n graphRecallEntityHintsEnabled: boolean;\n graphRecallEntityHintMax: number;\n graphRecallEntityHintMaxChars: number;\n graphRecallSnapshotDir: string;\n graphRecallEnableTrace: boolean;\n graphRecallEnableDebug: boolean;\n /** Allow graph_mode escalation for broader causal/timeline phrasing beyond strict keywords. */\n graphExpandedIntentEnabled?: boolean;\n /** Run bounded graph expansion in full mode when enough recall seeds exist. */\n graphAssistInFullModeEnabled?: boolean;\n /** In full mode, compute graph assist for telemetry/snapshotting but do not inject merged results. */\n graphAssistShadowEvalEnabled?: boolean;\n /** Minimum seed results required before full-mode graph assist runs. */\n graphAssistMinSeedResults?: number;\n entityGraphEnabled: boolean;\n timeGraphEnabled: boolean;\n /** When true, write fallback temporal adjacency edges for consecutive extracted memories. */\n graphWriteSessionAdjacencyEnabled?: boolean;\n causalGraphEnabled: boolean;\n maxGraphTraversalSteps: number;\n graphActivationDecay: number;\n graphPathScoring: GraphPathScoringConfig;\n /** Weight of graph activation score when blending with seed QMD score (0-1). */\n graphExpansionActivationWeight: number;\n /** Lower bound for blended graph-expanded recall scores (0-1). */\n graphExpansionBlendMin: number;\n /** Upper bound for blended graph-expanded recall scores (0-1). */\n graphExpansionBlendMax: number;\n maxEntityGraphEdgesPerMemory: number;\n /** SimpleMem-inspired de-linearization: resolve pronouns and anchor relative dates after extraction. */\n delinearizeEnabled: boolean;\n /** Synapse-inspired confidence gate — skip memory injection when top score is below threshold. */\n recallConfidenceGateEnabled: boolean;\n recallConfidenceGateThreshold: number;\n /** PlugMem-inspired causal rule extraction: mine IF→THEN rules during consolidation. */\n causalRuleExtractionEnabled: boolean;\n /** E-Mem-inspired memory reconstruction: targeted retrieval for missing entity context. */\n memoryReconstructionEnabled: boolean;\n /** Maximum number of entity expansions per recall. */\n memoryReconstructionMaxExpansions: number;\n /** Synapse-inspired lateral inhibition to suppress hub-node dominance. */\n graphLateralInhibitionEnabled: boolean;\n /** Inhibition strength (default 0.15). Higher = more suppression. */\n graphLateralInhibitionBeta: number;\n /** Number of top competing nodes considered for inhibition (default 7). */\n graphLateralInhibitionTopM: number;\n\n // Issue #681 PR 2/3 — graph-edge confidence decay maintenance.\n /** Enable the periodic graph-edge confidence decay job. Default false (opt-in). */\n graphEdgeDecayEnabled: boolean;\n /** Cadence in milliseconds at which the cron triggers the decay job. Default 7d. */\n graphEdgeDecayCadenceMs: number;\n /** Decay window passed through to `decayEdgeConfidence`. Default 90 days. */\n graphEdgeDecayWindowMs: number;\n /** Per-window confidence drop. Default 0.1. */\n graphEdgeDecayPerWindow: number;\n /** Floor confidence will not decay below. Default 0.1. */\n graphEdgeDecayFloor: number;\n /** Confidence threshold for the \"below visibility\" telemetry counter. Default 0.2. */\n graphEdgeDecayVisibilityThreshold: number;\n\n /**\n * Issue #681 PR 3/3 — minimum edge confidence to traverse during spreading\n * activation. Edges below are pruned (no activation, no downstream neighbors);\n * legacy edges without `confidence` are treated as 1.0. Range `[0, 1]`; default `0.2`.\n */\n graphTraversalConfidenceFloor: number;\n /**\n * Issue #681 PR 3/3 — number of PageRank-style refinement iterations applied\n * on top of the BFS spreading-activation scores. Each iteration redistributes\n * a node's confidence-weighted activation along its outgoing edges. Set to 0\n * to disable refinement and use raw BFS scores. Default `8`.\n */\n graphTraversalPageRankIterations: number;\n // v8.2: Temporal Memory Tree\n temporalMemoryTreeEnabled: boolean;\n tmtHourlyMinMemories: number;\n tmtSummaryMaxTokens: number;\n // Explicit cue recall\n /** Front-load exact stored evidence for query-visible cues like turns, dates, ids, files, and tools. */\n explicitCueRecallEnabled: boolean;\n /** Character budget for the explicit cue evidence section. */\n explicitCueRecallMaxChars: number;\n /** Maximum query-visible cues expanded per recall. */\n explicitCueRecallMaxReferences: number;\n /** Enable targeted fact evidence recall for direct answer questions. */\n targetedFactRecallEnabled: boolean;\n /** Character budget for the targeted fact evidence section. */\n targetedFactRecallMaxChars: number;\n /** Maximum recalled items for targeted fact evidence. */\n targetedFactRecallMaxResults: number;\n /** Recent-turn scan window for targeted fact evidence. */\n targetedFactRecallScanWindowTurns: number;\n /** Recent-token scan window for targeted fact evidence. */\n targetedFactRecallScanWindowTokens: number;\n /** Enable focused list evidence recall for count, relation, and recommendation questions. */\n focusedListRecallEnabled: boolean;\n /** Character budget for the focused list evidence section. */\n focusedListRecallMaxChars: number;\n /** Maximum recalled items for focused list evidence. */\n focusedListRecallMaxResults: number;\n /** Recent-turn scan window for focused list evidence. */\n focusedListRecallScanWindowTurns: number;\n /** Recent-token scan window for focused list evidence. */\n focusedListRecallScanWindowTokens: number;\n /** Enable response guidance recall for user answer-shape preferences. */\n responseGuidanceRecallEnabled: boolean;\n /** Character budget for the response guidance recall section. */\n responseGuidanceRecallMaxChars: number;\n /** Maximum recalled items for response guidance. */\n responseGuidanceRecallMaxResults: number;\n /** Recent-turn scan window for response guidance. */\n responseGuidanceRecallScanWindowTurns: number;\n /** Recent-token scan window for response guidance. */\n responseGuidanceRecallScanWindowTokens: number;\n /** Enable event-order evidence recall for chronology questions. */\n eventOrderRecallEnabled: boolean;\n /** Character budget for the event-order evidence section. */\n eventOrderRecallMaxChars: number;\n /** Maximum recalled items for event-order evidence. */\n eventOrderRecallMaxResults: number;\n /** Recent-turn scan window for event-order evidence. */\n eventOrderRecallScanWindowTurns: number;\n /** Recent-token scan window for event-order evidence. */\n eventOrderRecallScanWindowTokens: number;\n // Lossless Context Management (LCM)\n lcmEnabled: boolean;\n lcmLeafBatchSize: number;\n lcmRollupFanIn: number;\n lcmFreshTailTurns: number;\n lcmMaxDepth: number;\n lcmRecallBudgetShare: number;\n lcmDeterministicMaxTokens: number;\n lcmArchiveRetentionDays: number;\n /** Max independent LCM observe jobs to process concurrently. Default 1. */\n lcmObserveConcurrency: number;\n /** Opt-in structured message-part capture/recall sidecar for LCM. Default false. */\n messagePartsEnabled: boolean;\n /** Max structured file/tool matches injected into recall. */\n messagePartsRecallMaxResults: number;\n\n // v9.1 Parallel Specialized Retrieval (ASMR-inspired)\n /** Enable three-agent parallel retrieval (DirectFact + Contextual + Temporal). Default false. */\n parallelRetrievalEnabled: boolean;\n /** Per-agent source weights for score blending during merge. */\n parallelAgentWeights: { direct: number; contextual: number; temporal: number };\n /** Max results fetched per agent before merge. */\n parallelMaxResultsPerAgent: number;\n\n // Daily Context Briefing (Issue #370)\n /** Briefing configuration knobs — see BriefingConfig for field docs. */\n briefing: BriefingConfig;\n\n // Codex CLI connector settings (install-time)\n codex: CodexConnectorConfig;\n\n // Live connectors (issue #683). Concrete implementations live under\n // packages/remnic-core/src/connectors/live/. Each child block maps to one\n // connector. All defaults are off — operators opt in.\n connectors: LiveConnectorsConfig;\n\n // MECE Taxonomy (#366)\n /** Enable the MECE taxonomy knowledge directory. Default false. */\n taxonomyEnabled: boolean;\n /** Auto-regenerate RESOLVER.md when taxonomy changes. Default true. */\n taxonomyAutoGenResolver: boolean;\n\n // Codex CLI — native memory materialization (#378)\n /** Materialize Remnic memories into Codex's expected ~/.codex/memories/ layout. Default true. */\n codexMaterializeMemories: boolean;\n /** Namespace to materialize; \"auto\" derives from the connector context. Default \"auto\". */\n codexMaterializeNamespace: string;\n /** Max estimated-token size of memory_summary.md. Default 4500. */\n codexMaterializeMaxSummaryTokens: number;\n /** Max age in days for rollout_summaries/*.md before pruning. Default 30. */\n codexMaterializeRolloutRetentionDays: number;\n /** Run materialization after semantic/causal consolidation completes. Default true. */\n codexMaterializeOnConsolidation: boolean;\n /** Run materialization at Codex session-end hook. Default true. */\n codexMaterializeOnSessionEnd: boolean;\n /** Enable Codex marketplace integration. Default true. */\n codexMarketplaceEnabled: boolean;\n\n // Page-level versioning (issue #371)\n /** Enable page-level versioning with sidecar snapshots. Default false. */\n versioningEnabled: boolean;\n /** Maximum number of version snapshots to keep per page. Default 50. Set to 0 to disable pruning. */\n versioningMaxPerPage: number;\n /** Name of the sidecar directory inside memoryDir. Default \".versions\". */\n versioningSidecarDir: string;\n\n // Binary file lifecycle management (#367)\n /** Enable binary file lifecycle management (mirror, redirect, clean). Default: false. */\n binaryLifecycleEnabled: boolean;\n /** Grace period in days before a mirrored binary is eligible for local cleanup. Default: 7. */\n binaryLifecycleGracePeriodDays: number;\n /** Storage backend type: \"filesystem\" copies to a local dir, \"none\" is no-op. Default: \"none\". */\n binaryLifecycleBackendType: \"filesystem\" | \"s3\" | \"none\";\n /** Base path for the filesystem backend. Required when backendType is \"filesystem\". */\n binaryLifecycleBackendPath: string;\n\n /**\n * Advertise legacy `engram_*` / `engram.*` MCP tool aliases alongside the\n * canonical `remnic_*` names (issue #1427). Default true for backward\n * compatibility. Set false to halve the advertised MCP `tools/list` surface;\n * tools stay callable under both names regardless. Also settable via\n * `REMNIC_EMIT_LEGACY_TOOLS` (falls back to `ENGRAM_EMIT_LEGACY_TOOLS`).\n */\n emitLegacyTools: boolean;\n // Codex citation parity (issue #379)\n /** Enable oai-mem-citation blocks in recall responses. Default false. */\n citationsEnabled: boolean;\n /** Auto-enable citations when the Codex adapter is detected. Default true. */\n citationsAutoDetect: boolean;\n\n // External enrichment pipeline (issue #365)\n /** Enable the external enrichment pipeline. Default false. */\n enrichmentEnabled: boolean;\n /** Automatically enrich new entities on creation. Default false. */\n enrichmentAutoOnCreate: boolean;\n /** Max candidates accepted per entity per enrichment run. Default 20. */\n enrichmentMaxCandidatesPerEntity: number;\n\n // Memory extensions discovery (#382)\n /** Whether third-party memory extensions are discovered and injected into consolidation. Default true. */\n memoryExtensionsEnabled: boolean;\n /**\n * Root directory for memory extensions. Empty string means derive from\n * memoryDir: go up to the Remnic home dir and append memory_extensions.\n */\n memoryExtensionsRoot: string;\n\n // Offline sync excludes (#1786)\n /**\n * Operator-supplied glob patterns (relative to the memory dir, `*` and\n * `**` supported) excluded from offline-sync push-side enumeration IN\n * ADDITION to the built-in node-local state excludes. Validated at\n * config parse time; invalid entries reject the config loudly.\n */\n offlineSyncExcludes: string[];\n}\nimport type { SemanticMergeConfig } from \"./semantic-merge-config.js\"; export type { SemanticMergeConfig };\n\n/** Runtime configuration for the daily context briefing feature. */\nexport interface BriefingConfig {\n /** Whether `remnic briefing` CLI and MCP tool are enabled. */\n enabled: boolean;\n /** Default lookback window token (e.g. \"yesterday\", \"3d\", \"1w\", \"24h\"). */\n defaultWindow: string;\n /** Default output format for the CLI. */\n defaultFormat: \"markdown\" | \"json\";\n /** Maximum number of LLM-generated suggested follow-ups. */\n maxFollowups: number;\n /** Optional path to an ICS or JSON calendar file. null disables the section. */\n calendarSource: string | null;\n /** If true, CLI writes a dated briefing file by default. */\n saveByDefault: boolean;\n /** Override directory for saved briefings. null → $REMNIC_HOME/briefings/. */\n saveDir: string | null;\n /** Whether to call the Responses API for follow-up suggestions. */\n llmFollowups: boolean;\n}\n\n/** Parsed representation of a briefing lookback window. */\nexport type BriefingWindow = \"yesterday\" | \"today\" | string;\n\n/** Filter the briefing to a single entity / project / topic. */\nexport interface BriefingFocus {\n type: \"person\" | \"project\" | \"topic\";\n value: string;\n}\n\n/** Calendar event surfaced by a CalendarSource implementation. */\nexport interface CalendarEvent {\n /** Stable identifier for dedupe / linking. */\n id: string;\n /** Event title (short). */\n title: string;\n /** ISO 8601 start timestamp. */\n start: string;\n /** Optional ISO 8601 end timestamp. */\n end?: string;\n /** Optional freeform location. */\n location?: string;\n /** Optional short notes. */\n notes?: string;\n}\n\n/** Abstraction over any calendar backend. Concrete implementations: `FileCalendarSource`. */\nexport interface CalendarSource {\n /** Return events that fall on the given UTC date (YYYY-MM-DD). */\n eventsForDate(dateIso: string): Promise<CalendarEvent[]>;\n}\n\n/** A single \"active thread\" surfaced in a briefing. */\nexport interface BriefingActiveThread {\n id: string;\n title: string;\n updatedAt: string;\n reason: string;\n}\n\n/** A single \"recent entity\" entry. */\nexport interface BriefingRecentEntity {\n name: string;\n type: string;\n updatedAt: string;\n score: number;\n summary?: string;\n}\n\n/** A single unresolved commitment or open question. */\nexport interface BriefingOpenCommitment {\n id: string;\n kind: \"question\" | \"commitment\" | \"pending_memory\";\n text: string;\n source?: string;\n createdAt?: string;\n}\n\n/** An LLM-generated short follow-up suggestion. */\nexport interface BriefingFollowup {\n text: string;\n rationale?: string;\n}\n\n/** Structured sections of a briefing result. */\nexport interface BriefingSections {\n activeThreads: BriefingActiveThread[];\n recentEntities: BriefingRecentEntity[];\n openCommitments: BriefingOpenCommitment[];\n suggestedFollowups: BriefingFollowup[];\n /** Only populated when a calendar source is configured and returns events. */\n todayCalendar?: CalendarEvent[];\n}\n\n/** A calendar source failure recorded when a CalendarSource throws during briefing generation. */\nexport interface BriefingCalendarSourceError {\n /** Human-readable description of the source (e.g. file path or source name). */\n source: string;\n /** Stringified error message from the failed source. */\n error: string;\n}\n\n/** Result returned by `buildBriefing`. */\nexport interface BriefingResult {\n markdown: string;\n json: Record<string, unknown>;\n sections: BriefingSections;\n /** Reason why suggested follow-ups were omitted (e.g. missing API key, LLM error). */\n followupsUnavailableReason?: string;\n /** Effective lookback window (ISO date range) used for this briefing. */\n window: { from: string; to: string };\n /**\n * Calendar sources that failed during this briefing run.\n * Only present (non-empty) when at least one source threw.\n * Allows callers to distinguish \"no events today\" from \"source unavailable\".\n */\n calendarSourceErrors?: BriefingCalendarSourceError[];\n /**\n * Auto-applied passive corrections drained from the notification queue at\n * briefing time (issue #1581). Each carries a one-line summary and the undo\n * command. Drained exactly once — the queue file is cleared on read, so a\n * notification appears in at most one briefing. Absent when the correction\n * capture feature is off or no corrections were auto-applied.\n */\n correctionNotifications?: ReadonlyArray<{\n planId: string;\n summary: string;\n undoCommand: string;\n createdAt: string;\n }>;\n}\n\n/**\n * Settings for the Codex CLI connector. These are consumed by\n * `remnic connectors install codex-cli` to decide where the phase-2 memory\n * extension is dropped and whether to install it at all.\n */\nexport interface CodexConnectorConfig {\n /**\n * Whether to install the Remnic memory extension into\n * `<codex_home>/memories_extensions/remnic/` when the `codex-cli`\n * connector is installed. Default `true`. Set to `false` for users who\n * self-manage the Codex memory extensions folder.\n */\n installExtension: boolean;\n /**\n * Optional override for the Codex home directory. When `null`, the\n * connector reads `$CODEX_HOME` and falls back to `~/.codex`. Setting\n * this is useful for integration tests and non-default installs.\n */\n codexHome: string | null;\n}\n\n/**\n * Container for live-connector config blocks (issue #683 PR 2/N).\n *\n * Lives at `connectors.*` rather than the top level so future connectors\n * (Notion, Gmail, GitHub) can slot in without bloating `PluginConfig`.\n *\n * Every child block must default to `enabled: false` per CLAUDE.md gotcha\n * #30 (escape hatch by default) and gotcha #48 (least-privileged enum\n * defaults). Concrete connectors are also expected to short-circuit at\n * registration time when their credentials are not populated.\n */\nexport interface LiveConnectorsConfig {\n /** Google Drive live connector (issue #683 PR 2/N). */\n googleDrive: GoogleDriveLiveConnectorConfig;\n /** Notion live connector (issue #683 PR 3/N). */\n notion: NotionLiveConnectorConfig;\n /** Gmail live connector (issue #683 PR 4/6). */\n gmail: GmailLiveConnectorConfig;\n /** GitHub live connector (issue #683 PR 5/6). */\n github: GitHubLiveConnectorConfig;\n}\n\n/**\n * Operator-facing config for the Google Drive live connector. The connector\n * module itself defines a separate, *validated* `GoogleDriveConnectorConfig`\n * shape (frozen, post-validation). This interface is the pre-validation\n * shape that `parseConfig` round-trips through.\n *\n * `clientId` / `clientSecret` / `refreshToken` are stored as strings here so\n * the schema can ship in `openclaw.plugin.json` and operators can populate\n * them from a secret store (e.g. an env-substituted plist or systemd\n * EnvironmentFile). They MUST NEVER be committed to source. The repo-wide\n * privacy policy in CLAUDE.md applies.\n */\nexport interface GoogleDriveLiveConnectorConfig {\n /** Master gate. Default false — operators must opt in explicitly. */\n enabled: boolean;\n /** OAuth2 client id. Populate from a secret store; never commit. */\n clientId: string;\n /** OAuth2 client secret. Populate from a secret store; never commit. */\n clientSecret: string;\n /** OAuth2 refresh token. Populate from a secret store; never commit. */\n refreshToken: string;\n /** Poll interval in ms. Default 300000 (5 min); min 1000; max 86400000 (24h). */\n pollIntervalMs: number;\n /** Optional folder-id scope. Empty array = all accessible files. */\n folderIds: string[];\n}\n\n/**\n * Operator-facing config for the Notion live connector (issue #683 PR 3/N).\n * The connector module defines a separate validated `NotionConnectorConfig`\n * shape (frozen, post-validation). This interface is the pre-validation shape\n * that `parseConfig` round-trips through.\n *\n * `token` is stored as a string here so operators can populate it from a\n * secret store (e.g. an env-substituted plist or systemd EnvironmentFile).\n * It MUST NEVER be committed to source. The repo-wide privacy policy in\n * CLAUDE.md applies.\n */\nexport interface NotionLiveConnectorConfig {\n /** Master gate. Default false — operators must opt in explicitly. */\n enabled: boolean;\n /** Notion integration token. Starts with `secret_`. Populate from a secret store; never commit. */\n token: string;\n /** Array of Notion database ids to import pages from. Empty = connector is a no-op. */\n databaseIds: string[];\n /** Poll interval in ms. Default 300000 (5 min); min 1000; max 86400000 (24h). */\n pollIntervalMs: number;\n}\n\n/**\n * Operator-facing config for the Gmail live connector (issue #683 PR 4/6).\n * The connector module defines a separate validated `GmailConnectorConfig`\n * shape (frozen, post-validation). This interface is the pre-validation shape\n * that `parseConfig` round-trips through.\n *\n * OAuth2 credentials are stored as strings here so operators can populate\n * them from a secret store (e.g. env-substituted plist or systemd\n * EnvironmentFile). They MUST NEVER be committed to source. The repo-wide\n * privacy policy in CLAUDE.md applies.\n */\nexport interface GmailLiveConnectorConfig {\n /** Master gate. Default false — operators must opt in explicitly. */\n enabled: boolean;\n /** OAuth2 client id. Populate from a secret store; never commit. */\n clientId: string;\n /** OAuth2 client secret. Populate from a secret store; never commit. */\n clientSecret: string;\n /** OAuth2 refresh token issued for the Gmail scope. Populate from a secret store; never commit. */\n refreshToken: string;\n /** Gmail userId. Defaults to \"me\" (the authenticated user). */\n userId: string;\n /** Gmail search query applied in addition to the watermark filter. Default \"in:inbox\". */\n query: string;\n /** Poll interval in ms. Default 300000 (5 min); min 1000; max 86400000 (24h). */\n pollIntervalMs: number;\n}\n\n/**\n * Operator-facing config for the GitHub live connector (issue #683 PR 5/6).\n * The connector module defines a separate validated `GitHubConnectorConfig`\n * shape (frozen, post-validation). This interface is the pre-validation shape\n * that `parseConfig` round-trips through.\n *\n * `token` is stored as a string here so operators can populate it from a\n * secret store (e.g. an env-substituted plist or systemd EnvironmentFile).\n * It MUST NEVER be committed to source. The repo-wide privacy policy in\n * CLAUDE.md applies.\n */\nexport interface GitHubLiveConnectorConfig {\n /** Master gate. Default false — operators must opt in explicitly. */\n enabled: boolean;\n /** GitHub personal access token. Populate from a secret store; never commit. */\n token: string;\n /** GitHub login of the user whose comments will be imported. Required. */\n userLogin: string;\n /** Repos to poll in \"owner/repo\" format. Empty = connector is a no-op. */\n repos: string[];\n /** Poll interval in ms. Default 300000 (5 min); min 1000; max 86400000 (24h). */\n pollIntervalMs: number;\n /** Whether to fetch Discussion comments in addition to issue/PR comments. Default false. */\n includeDiscussions: boolean;\n}\n\nexport interface BootstrapOptions {\n dryRun?: boolean;\n sessionsDir?: string;\n limit?: number;\n since?: Date;\n}\n\nexport interface BootstrapResult {\n sessionsScanned: number;\n turnsProcessed: number;\n highSignalTurns: number;\n memoriesCreated: number;\n skipped: number;\n}\n\nexport interface PrincipalRule {\n match: string;\n principal: string;\n}\n\nexport interface NamespacePolicy {\n name: string;\n readPrincipals: string[];\n writePrincipals: string[];\n includeInRecallByDefault?: boolean;\n}\n\nexport interface RelevanceFeedback {\n up: number;\n down: number;\n lastUpdatedAt: string;\n notes?: string[];\n}\n\n/**\n * Trusted source identity and role, resolved from the auth boundary.\n * Never user-editable — set by the server from authenticated host metadata,\n * not from tool arguments or model-authored content (anti-spoofing).\n */\nexport interface SourceConnectorProvenance {\n sourceConnector?: string;\n originRole?: \"user\" | \"assistant\" | \"tool\";\n}\nexport type { BufferTurn } from \"./buffer-turn.js\";\nimport type { BufferTurn } from \"./buffer-turn.js\";\n\nexport interface BufferEntryState {\n turns: BufferTurn[];\n lastExtractionAt: string | null;\n extractionCount: number;\n /**\n * Turns retained across `clearAfterExtraction` so a later extraction pass\n * sees the context that caused a defer verdict (issue #562, PR 2). Bounded\n * to the configured retention cap by `retainDeferredTurns`. Empty / absent\n * means no retention in effect.\n */\n retainedTurns?: BufferTurn[];\n}\n\nexport interface BufferState {\n turns: BufferTurn[];\n lastExtractionAt: string | null;\n extractionCount: number;\n entries?: Record<string, BufferEntryState>;\n}\n\nexport interface BehaviorLoopAdjustment {\n parameter: string;\n previousValue: number;\n nextValue: number;\n delta: number;\n evidenceCount: number;\n confidence: number;\n reason: string;\n appliedAt: string;\n}\n\nexport interface BehaviorLoopPolicyState {\n version: number;\n windowDays: number;\n minSignalCount: number;\n maxDeltaPerCycle: number;\n protectedParams: string[];\n adjustments: BehaviorLoopAdjustment[];\n updatedAt: string;\n}\n\nexport type BehaviorSignalType =\n | \"correction_override\"\n | \"preference_affinity\"\n | \"topic_revisitation\"\n | \"action_pattern\"\n | \"outcome_preference\"\n | \"phrasing_style\";\nexport type BehaviorSignalDirection = \"positive\" | \"negative\";\n\nexport interface BehaviorSignalEvent {\n timestamp: string;\n namespace: string;\n memoryId: string;\n category: Extract<MemoryCategory, \"correction\" | \"preference\">;\n signalType: BehaviorSignalType;\n direction: BehaviorSignalDirection;\n confidence: number;\n signalHash: string;\n source: \"extraction\" | \"correction\";\n}\n\n/**\n * One row of the buffer-surprise telemetry ledger (issue #563 PR 3).\n *\n * Emitted by `SmartBuffer` each time the surprise probe produces a score\n * for an incoming turn (i.e. the feature flag is on and the existing\n * trigger-logic path called through to the probe). Not written when the\n * probe is skipped — the absence of a row is meaningful and matches the\n * \"probe was not consulted\" state.\n *\n * The ledger is intentionally lean: we record the score, the threshold in\n * force, whether the turn caused a flush, and the turn count so operators\n * can re-derive precision/recall without replaying traffic. Turn content\n * is never persisted — this ledger is safe to commit to shared storage.\n */\nexport interface BufferSurpriseEvent {\n /** Literal tag to simplify multiplexed log consumers. */\n event: \"BUFFER_SURPRISE\";\n /** ISO timestamp when the decision was made. Server-side, not turn ts. */\n timestamp: string;\n /** Buffer identifier (session / thread). Opaque string. */\n bufferKey: string;\n /** Session key if available; null when the turn has no session binding. */\n sessionKey: string | null;\n /** Role of the scored turn. */\n turnRole: \"user\" | \"assistant\";\n /** Surprise score in `[0, 1]`, already clamped. */\n surpriseScore: number;\n /** Threshold in force when the decision was made. */\n threshold: number;\n /** Whether this turn upgraded `keep_buffering` → `extract_now`. */\n triggeredFlush: boolean;\n /** Number of turns in the buffer (including the current turn). */\n turnCountInWindow: number;\n}\n\n/** Memory status for lifecycle management */\nexport type MemoryStatus =\n | \"active\"\n | \"pending_review\"\n | \"rejected\"\n | \"quarantined\"\n | \"superseded\"\n | \"archived\"\n /**\n * Operator explicitly forgot the memory (issue #686 PR 4/6). Soft\n * delete: the file stays on disk and a page-version snapshot is kept\n * so the act is reversible during a configurable retention window\n * (default 90 days), but the memory is excluded from recall, browse,\n * and entity attribution. After the retention window passes, a\n * future maintenance cron will hard-delete forgotten memories.\n */\n | \"forgotten\";\nexport type LifecycleState = \"candidate\" | \"validated\" | \"active\" | \"stale\" | \"archived\";\nexport type VerificationState = \"unverified\" | \"user_confirmed\" | \"system_inferred\" | \"disputed\";\nexport type PolicyClass = \"ephemeral\" | \"durable\" | \"protected\";\n\n/** Importance level tiers */\nexport type ImportanceLevel = \"critical\" | \"high\" | \"normal\" | \"low\" | \"trivial\";\n\n/** Importance scoring result */\nexport interface ImportanceScore {\n /** Numeric score 0-1 */\n score: number;\n /** Tier level */\n level: ImportanceLevel;\n /** Reasons for this score */\n reasons: string[];\n /** Salient keywords extracted */\n keywords: string[];\n}\n\nexport interface MemoryFrontmatter extends SourceConnectorProvenance, OriginMetadata, MemoryDriftProvenance {\n /** True when write-time classification ties this memory to its source connector's tools or commands. */\n toolScoped?: true;\n id: string;\n /** Whom this memory models (issue #2372). The promotion guard treats absent as \"user\" (fail closed). */\n subject?: MemorySubject;\n category: MemoryCategory;\n /** OKF v0.1 interop metadata (issue #1946). Inert: presentation only, never overrides `category`. */\n type?: string;\n /** Dominant-script hint (ISO 15924, e.g. `latn`/`jpan`) for cross-lingual recall (#2197). Absent on legacy files. */\n language?: string;\n created: string;\n updated: string;\n source: string;\n confidence: number;\n confidenceTier: ConfidenceTier;\n tags: string[];\n entityRef?: string;\n supersedes?: string;\n /** ISO 8601 date — memory expires and gets cleaned up after this date */\n expiresAt?: string;\n /** IDs of parent memories this was derived from (lineage tracking) */\n lineage?: string[];\n /** Memory status: active (default), pending_review, rejected, quarantined, superseded, archived, or forgotten */\n status?: MemoryStatus;\n /**\n * Tombstone block marker (issue #1579): persisted `pending_review` with this\n * set to the tombstone id — a VISIBLE block (rule 34); approval revokes it.\n */\n blockedBy?: string;\n /** Which tombstone tier matched (`exact`/`normalized`/`keyed`/`semantic`). */\n tombstoneBlockTier?: \"exact\" | \"normalized\" | \"keyed\" | \"semantic\";\n /** ID of memory that superseded this one */\n supersededBy?: string;\n /** Timestamp when superseded */\n supersededAt?: string;\n /** Why this memory was superseded. Absent means legacy/direct. */\n supersessionCause?: \"direct\" | \"dependency\";\n /** ID of the superseded memory that caused dependency invalidation. */\n invalidatedBy?: string;\n /** Timestamp when archived */\n archivedAt?: string;\n /**\n * Explicit fact validity start (issue #680). ISO 8601 timestamp.\n *\n * When present, marks the moment at which the fact begins being\n * \"true\" / authoritative. When absent at read time, callers fall\n * back to `created` so legacy memories written before #680 still\n * participate in `as_of` recall filtering without a migration.\n */\n valid_at?: string;\n /**\n * Explicit fact validity end (issue #680). ISO 8601 timestamp.\n *\n * Set automatically by the temporal-supersession pipeline when a\n * newer fact supersedes this one — the value is the superseder's\n * `valid_at` (or `created` if no `valid_at` was set). May also be\n * set manually for facts that are known to expire at a specific\n * point in time.\n */\n invalid_at?: string;\n /**\n * Ingestion time (issue #1578) — when Remnic *learned* this fact.\n * Distinct from `created` (file creation) and `valid_at` (event-time\n * start). Aligns with the source turn timestamp (#1575\n * `sources[].observedAt`). When `eventTimeSource === \"assumed\"`,\n * `valid_at` is copied from this value.\n */\n observedAt?: string;\n /**\n * Provenance of the event-time interval (issue #1578).\n * - `\"extracted\"` — a per-fact `eventTime` expression was resolved\n * against the source turn's timestamp and produced `valid_at` /\n * `invalid_at`.\n * - `\"assumed\"` — no resolvable expression; `valid_at` was copied from\n * `observedAt` (the ingestion anchor).\n * Absent on memories written before #1578; readers treat absent as\n * `\"assumed\"` for display purposes.\n */\n eventTimeSource?: \"extracted\" | \"assumed\";\n /**\n * Timestamp when the operator explicitly forgot this memory\n * (issue #686 PR 4/6). Set by `remnic forget <id>`. Memories with\n * `status === \"forgotten\"` are excluded from recall, browse, and\n * entity attribution; the file remains on disk until the retention\n * window passes.\n */\n forgottenAt?: string;\n /** Optional human-readable reason captured by `remnic forget --reason`. */\n forgottenReason?: string;\n /** Policy-driven lifecycle state used for retrieval eligibility/ranking. */\n lifecycleState?: LifecycleState;\n /** Verification provenance used by lifecycle policy. */\n verificationState?: VerificationState;\n /** Policy class used by lifecycle guardrails. */\n policyClass?: PolicyClass;\n /** Last lifecycle validation timestamp (ISO 8601). */\n lastValidatedAt?: string;\n /** Lifecycle decay score in [0,1]. */\n decayScore?: number;\n /** Lifecycle heat score in [0,1]. */\n heatScore?: number;\n // Access tracking (Phase 1A)\n /** Number of times this memory has been retrieved */\n accessCount?: number;\n /** Last time this memory was accessed (ISO 8601) */\n lastAccessed?: string;\n // Memory Worth counters (issue #560)\n // Per-fact outcome counters used to derive a dynamic utility score —\n // `p(success | retrieved)` — as a complement to the static `importance`\n // field. Absent on legacy memories written before #560; readers must treat\n // `undefined` as zero observations (uniform Beta(1,1) prior).\n // Both values must be non-negative integers on write. PR 1 wires only the\n // schema + storage round-trip — no increments, scoring, or filtering yet.\n /** Number of sessions where this memory was retrieved and the outcome was judged a success. */\n mw_success?: number;\n /** Number of sessions where this memory was retrieved and the outcome was judged a failure. */\n mw_fail?: number;\n // Importance scoring (Phase 1B)\n /** Importance score with level, reasons, and keywords */\n importance?: ImportanceScore;\n // Chunking (Phase 2A)\n /** Parent memory ID if this is a chunk */\n parentId?: string;\n /** Chunk index within parent (0-based) */\n chunkIndex?: number;\n /** Total number of chunks for this parent */\n chunkTotal?: number;\n // Memory Linking (Phase 3A)\n /** Links to other memories */\n links?: MemoryLink[];\n // Intent-grounded memory routing (v8.0 phase 1)\n intentGoal?: string;\n intentActionType?: string;\n intentEntityTypes?: string[];\n // Verbatim artifact lineage (v8.0 phase 1)\n artifactType?: \"decision\" | \"constraint\" | \"todo\" | \"definition\" | \"commitment\" | \"correction\" | \"fact\";\n sourceMemoryId?: string;\n sourceTurnId?: string;\n // v8.0 Phase 2B: HiMem episode/note classification\n /** episode = time-specific event; note = stable belief/preference/decision */\n memoryKind?: \"episode\" | \"note\" | \"box\" | \"dream\" | \"procedural\";\n /** Structured key-value attributes extracted from the content (e.g., product attributes, dates, quantities). */\n structuredAttributes?: Record<string, string>;\n /**\n * SHA-256 (via ContentHashIndex.computeHash) of the raw content that was\n * used as the dedup key at write time. Persists through archive and\n * consolidation so the hash can be removed from the index even if the stored\n * content has been transformed (e.g. an inline citation was appended).\n *\n * When present, archive/consolidation paths use this directly instead of\n * calling stripCitation(memory.content), which only handles the default\n * [Source: ...] format and silently fails for custom citation templates.\n */\n contentHash?: string;\n /**\n * Consolidation provenance — pointers to the page-versioning snapshots\n * that this memory was derived from (issue #561). Each entry is a\n * `\"<memory-path>:<version-number>\"` string (e.g.\n * `\"facts/preferences.md:3\"`) referencing a snapshot recorded by\n * `page-versioning.ts`.\n *\n * PR 1 introduces this field as read-through only — storage preserves\n * it verbatim but no code produces it yet. PR 2 populates it on\n * consolidation writes; PR 4 adds a `remnic doctor` integrity check\n * that validates each referent actually exists.\n */\n derived_from?: string[];\n /**\n * Which consolidation operator produced this memory (issue #561,\n * extended in #687). See `ConsolidationOperator` in\n * `semantic-consolidation.ts` for the operator algebra. Absent on\n * memories that were not produced by a consolidation pass.\n *\n * `\"pattern-reinforcement\"` (issue #687 PR 2/4) tags memories that\n * were promoted to canonical by the pattern-reinforcement\n * maintenance job after observing the same content across\n * multiple sessions.\n */\n derived_via?: \"split\" | \"merge\" | \"update\" | \"pattern-reinforcement\";\n /**\n * Number of source memories that reinforced this canonical memory\n * (issue #687 PR 2/4). Set by the pattern-reinforcement\n * maintenance job when it clusters duplicate memories and promotes\n * the most recent member to canonical. Counts the cluster size at\n * the time of the run; subsequent runs update this monotonically.\n *\n * Always a positive integer when present. Absent on memories that\n * have not been touched by pattern reinforcement.\n */\n reinforcement_count?: number;\n /**\n * ISO 8601 timestamp recording the most recent pattern-reinforcement\n * run that touched this memory (issue #687 PR 2/4). Updated each\n * time the cluster size grows. Absent when `reinforcement_count`\n * is absent.\n */\n last_reinforced_at?: string;\n // Claim-level provenance spans (issue #1575).\n //\n // Verbatim source-utterance excerpts that ground each extracted fact, plus\n // a coarse `provenance` tag recording whether the span was located in the\n // buffered turn text. Absent on legacy memories written before #1575;\n // readers MUST treat both `undefined` (legacy) and `provenance: \"none\"`\n // uniformly — never crash, never drop the fact (rule 34 spirit).\n //\n // `charStart` / `charEnd` are best-effort debugging aids — consumers MUST\n // tolerate their absence (turn text is not persisted forever). The span\n // interval is half-open [charStart, charEnd) per rule 35.\n //\n // PR 1 wires only the schema + storage round-trip — no extraction prompt,\n // validator, or read surfaces yet.\n /** Literal source-utterance excerpts backing this fact (issue #1575). */\n sources?: ProvenanceSource[];\n /**\n * Coarse provenance strength: `\"verified\"` (span located in source text),\n * `\"unverified\"` (span present but not locatable), or `\"none\"` (no span\n * recorded). Readers treat absent as `\"none\"` for legacy memories.\n */\n provenance?: \"verified\" | \"unverified\" | \"none\";\n\n // Faithfulness gate (issue #1576). When the extraction faithfulness gate\n // runs (mode \"shadow\" or \"enforce\"), this field records the verdict. Absent\n // when the gate is off or the fact predates #1576 — readers MUST treat\n // absent as \"not checked\" (rule 34 spirit).\n //\n // This field is its own frontmatter island — it never feeds mw_*/trust\n // fields. Consumed by #1577 (TrustScore) as one input among many.\n /** Entailment verdict from the faithfulness gate (issue #1576). */\n faithfulness?: FaithfulnessFrontmatter;\n}\n\n/**\n * Faithfulness gate frontmatter (issue #1576).\n *\n * Serialized as a single JSON line so it round-trips through the existing\n * YAML parser. `verdict: \"unchecked\"` marks a fact that entered the gate but\n * could not be evaluated (backend failure, timeout). `verdict:\n * \"skipped_no_span\"` marks a legacy fact without a verified source span.\n * Both are distinct from the field being absent (gate was off entirely).\n */\nexport interface FaithfulnessFrontmatter {\n verdict: \"entailed\" | \"contradicted\" | \"unsupported\" | \"unchecked\" | \"skipped_no_span\";\n /** Model/endpoint that produced the verdict, when known. */\n model?: string;\n /** One-sentence rationale from the verifier. */\n rationale?: string;\n /** ISO-8601 timestamp the check ran. */\n at?: string;\n}\n\n/**\n * Per-component weights for the unified TrustScore blend (issue #1577). Every\n * field is optional and defaults to the documented value in `trust-score.ts`;\n * weights are sum-normalized at score time so only relative magnitudes matter.\n * Invalid weights (non-finite, outside `[0,1]`) are rejected at config parse.\n */\nexport interface TrustWeights {\n memoryWorth?: number;\n provenance?: number;\n faithfulness?: number;\n corroboration?: number;\n contradiction?: number;\n domainCalibration?: number;\n feedback?: number;\n recency?: number;\n}\n\n/** Memory link relationship types */\nexport type MemoryLinkType = \"follows\" | \"references\" | \"contradicts\" | \"supports\" | \"related\";\n\n/** A link between memories */\nexport interface MemoryLink {\n targetId: string;\n linkType: MemoryLinkType;\n strength: number;\n reason?: string;\n}\n\n/**\n * A verbatim source-utterance excerpt that grounds an extracted fact\n * (issue #1575). Emitted by the PR 2 extraction validator; round-tripped\n * through storage in PR 1.\n *\n * `quote` is the only strictly-required field — without it the entry is\n * meaningless and is dropped on read. `charStart` / `charEnd` are\n * best-effort offsets within the buffered turn text at write time and\n * form a half-open interval [charStart, charEnd) (rule 35).\n */\nexport interface ProvenanceSource {\n /** Session key of the source turn (e.g. `project/<name>/<ts>`). */\n sessionKey: string;\n /** Host-supplied turn identifier, when one was provided. */\n turnId?: string;\n /** ISO 8601 timestamp of the source turn. */\n observedAt: string;\n /** Verbatim utterance excerpt, capped at `provenance.maxQuoteChars`. */\n quote: string;\n /** Offset within the buffered turn text where the quote begins, when located. */\n charStart?: number;\n /** Half-open end offset within the buffered turn text (rule 35). */\n charEnd?: number;\n}\n\n// Conversation Threading (Phase 3B)\nexport interface ConversationThread {\n id: string;\n title: string;\n createdAt: string;\n updatedAt: string;\n sessionKey?: string;\n episodeIds: string[];\n linkedThreadIds: string[];\n}\n\n// Memory Summarization (Phase 4A)\nexport interface MemorySummary {\n id: string;\n createdAt: string;\n timeRangeStart: string;\n timeRangeEnd: string;\n summaryText: string;\n keyFacts: string[];\n keyEntities: string[];\n sourceEpisodeIds: string[];\n}\n\nexport interface DaySummaryResult {\n summary: string;\n bullets: string[];\n next_actions: string[];\n risks_or_open_loops: string[];\n}\n\n// Topic Extraction (Phase 4B)\nexport interface TopicScore {\n term: string;\n score: number;\n count: number;\n}\n\nexport interface MemoryFile {\n path: string;\n frontmatter: MemoryFrontmatter;\n content: string;\n}\n\n/**\n * Public type representing the **Observation** stage in the\n * Trace → Observation → Primitive pipeline (issue #685).\n *\n * - **Trace**: raw conversation turns captured in `buffer.ts`. Noisy,\n * verbose, ephemeral.\n * - **Observation** (this type): post-extraction, importance-scored\n * fact candidate emitted by `extraction.ts` / `extraction-judge.ts`.\n * Already distilled — but not yet consolidated against the corpus.\n * - **Primitive**: the durable `MemoryFile` written by `storage.ts`,\n * reinforced over time by `compounding/engine.ts`.\n *\n * `MemoryObservation` is the named handle on the intermediate stage\n * the codebase has always produced but never publicly typed. It lets\n * callers (telemetry, doctor surfaces, tests, downstream tooling)\n * inspect the post-extraction shape without reaching into extraction\n * internals.\n *\n * Naming note: this is intentionally NOT the same as the existing\n * `state/observation-ledger/` directory, which is telemetry storage\n * for the extraction pipeline (turn-count aggregates rebuilt by\n * `maintenance/rebuild-observations.ts` and judge verdict events\n * appended by `extraction-judge-telemetry.ts`). Lifecycle events on\n * primitives — status flips, supersessions, archival, forget — live\n * in `state/memory-lifecycle-ledger.jsonl`, written by\n * `StorageManager`. A `MemoryObservation` describes the in-flight\n * candidate that became (or didn't become) a primitive; the ledger\n * directory is how the pipeline reports on itself. See\n * `docs/trace-to-primitive.md` for the full pipeline walkthrough.\n */\nexport interface MemoryObservation {\n /** Stable id for this observation, distinct from any primitive id. */\n id: string;\n /** Source session id the trace came from. */\n sessionId?: string;\n /** ISO timestamp the observation was emitted. */\n observedAt: string;\n /** The extracted fact candidate (category, content, confidence, tags, etc.). */\n fact: ExtractedFact;\n /** Importance score in [0,1], from `importance.ts`. */\n importance?: number;\n /**\n * Whether the observation passed the extraction judge\n * (`extraction-judge.ts`). When `false`, the observation was\n * captured for telemetry but not persisted as a primitive.\n */\n judgeAccepted?: boolean;\n /** Optional reason the judge gave when rejecting. */\n judgeRejectionReason?: string;\n /**\n * Id of the resulting `MemoryFile` primitive once consolidation runs.\n * Absent until consolidation decides to ADD/MERGE/UPDATE the\n * observation into the corpus.\n */\n resultingPrimitiveId?: string;\n}\n\n/** Ordered step for extracted procedure memories (issue #519). */\nexport interface ExtractedProcedureStep {\n order: number;\n intent: string;\n toolCall?: { kind: string; signature: string };\n expectedOutcome?: string;\n optional?: boolean;\n}\n\nexport interface ExtractedFact {\n category: MemoryCategory;\n content: string;\n confidence: number;\n tags: string[];\n entityRef?: string;\n cueAnchors?: Array<{ type: \"entity\" | \"file\" | \"tool\" | \"outcome\" | \"constraint\" | \"date\"; value: string }>;\n source?: ExtractionPassSource;\n /** Claim-level provenance spans. Missing spans fail closed when verification requires them. */\n sources?: ProvenanceSource[];\n /**\n * Coarse provenance strength tag (issue #1575 PR 2). Set by the extraction\n * validator: \"verified\" when the quote was located in source turns,\n * \"unverified\" when the quote survived but no offsets were recoverable.\n * Absent when `provenance.enabled` is false or no quote was provided —\n * readers treat absent as \"none\" (rule 34 spirit).\n */\n provenance?: \"verified\" | \"unverified\" | \"none\";\n /**\n * Transient LLM quote (issue #1575 PR 2): consumed by the post-parse\n * validator to build `sources`, stripped before persistence, never in\n * content-hash dedup (rule 23 / checklist §13).\n */\n quote?: string;\n /** Transient requireSpans signal (#1575 PR 2): quote unlocatable while\n * `provenance.requireSpans` is on → routed to `pending_review`; stripped pre-persist. */\n requireSpansPending?: boolean;\n promptedByQuestion?: string;\n /** Project- vs global-scoped; emitted when `extractionScopeClassificationEnabled`; defaults \"project\" with a coding context. */\n scope?: MemoryScope;\n /** Extractor-assigned subject, present only when subjectClassification.enabled (issue #2372). */\n subject?: MemorySubject;\n /** Structured key-value attributes extracted from the content (e.g., product attributes, dates, quantities). */\n structuredAttributes?: Record<string, string>;\n /** When category is `procedure`, ordered steps with intents (persisted under procedures/). */\n procedureSteps?: ExtractedProcedureStep[];\n /**\n * When category is `reasoning_trace`, the stored solution chain the user\n * walked through. Persisted under reasoning-traces/.\n */\n reasoningTrace?: ExtractedReasoningTrace;\n /**\n * Optional event-time expression for bi-temporal validity (#1578 PR2).\n * Verbatim temporal anchor extracted from the conversation (\"2025-03-01\",\n * \"last March\", \"since 2024\", \"until 2025-06-01\"). Resolved against the\n * source turn timestamp at write time by `resolveFactEventTime` — never\n * re-resolved at read. Absent when the fact carries no temporal anchor or\n * when `temporal.biTemporal` is off.\n */\n eventTime?: string;\n /**\n * Per-fact source-turn timestamp for bi-temporal event-time resolution\n * (#1670). When set, `resolveFactEventTime` anchors this fact's\n * `eventTime` expression against THIS turn's timestamp instead of the\n * batch-wide latest turn timestamp — so a buffered conversation spanning\n * a date boundary resolves \"yesterday\" on an early-turn fact against\n * that early turn's date, not the last turn's.\n *\n * Programmatic-only: extractors that know the exact source turn set this\n * directly. The LLM extraction prompt never emits it (models cannot know\n * turn timestamps). Falls back to the earliest provenance span's\n * `observedAt`, then to the batch anchor, when absent.\n */\n sourceTurnTimestamp?: string;\n /** Transient span-mode reference (issue #2333): offsets into the exact\n * per-turn text the extraction model saw; stripped at materialization. */\n span?: ExtractedFactSpanRef | null;\n}\n\nexport interface ExtractedReasoningTraceStep {\n order: number;\n description: string;\n}\n\nexport interface ExtractedReasoningTrace {\n steps: ExtractedReasoningTraceStep[];\n finalAnswer: string;\n observedOutcome?: string;\n}\n\nexport interface MemoryIntent {\n goal: string;\n actionType: string;\n entityTypes: string[];\n /** True when the prompt reads like starting a concrete task (ship/deploy/tests/PR, etc.). */\n taskInitiation?: boolean;\n}\n\nexport interface ExtractedQuestion {\n question: string;\n context: string;\n priority: number;\n}\n\nexport interface QuestionEntry {\n id: string;\n question: string;\n context: string;\n priority: number; // 0-1, higher = more important\n created: string;\n resolved: boolean;\n resolvedAt?: string;\n}\n\n/**\n * Coarse failure class consumed by the extraction retry/backoff + circuit\n * breaker layer (extraction hot-loop hardening). Kept deliberately small:\n * - `provider_retryable`: transient provider failure (429/5xx/network) — back off and retry.\n * - `parse_empty`: provider responded but produced no parseable output — retry with a low attempt cap.\n * - `auth_config`: auth/config failure (401/403/\"no models configured\") — open the breaker immediately.\n */\nexport type ExtractionFailureClass = \"provider_retryable\" | \"parse_empty\" | \"auth_config\";\n\nexport interface ExtractionResult {\n facts: ExtractedFact[];\n profileUpdates: string[];\n entities: EntityMention[];\n questions: ExtractedQuestion[];\n identityReflection?: string;\n episodeTitle?: string;\n relationships?: ExtractedRelationship[];\n extractionFailure?: string;\n /** Coarse class used by the retry/backoff + circuit-breaker layer (extraction hot loop). */\n extractionFailureClass?: ExtractionFailureClass;\n /** Local prefilter reason; no provider extraction was attempted. */\n extractionSkippedReason?: \"conversation_only_non_memory\" | \"mechanical_telemetry\";\n /** Trusted source connector resolved over boundedTurns; persisted verbatim by the orchestrator so it is not recomputed (#2183). */\n sourceConnector?: string;\n}\n\nexport interface EntityMention {\n name: string;\n type: \"person\" | \"project\" | \"tool\" | \"company\" | \"place\" | \"other\";\n facts: string[];\n structuredSections?: EntityStructuredSection[];\n source?: ExtractionPassSource;\n promptedByQuestion?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Knowledge Graph Enhancement (Entity Relationships, Activity, Scoring)\n// ---------------------------------------------------------------------------\n\nexport interface EntityRelationship {\n target: string;\n label: string;\n}\n\nexport interface EntityActivityEntry {\n date: string;\n note: string;\n}\nexport interface EntityTimelineEntry {\n timestamp: string;\n text: string;\n source?: string;\n sessionKey?: string;\n principal?: string;\n origin?: string;\n}\nexport interface EntityStructuredSection {\n key: string;\n title: string;\n facts: string[];\n factOrigins?: Array<string | undefined>;\n}\n\nexport interface EntitySchemaSectionDefinition {\n key: string;\n title: string;\n description: string;\n aliases?: string[];\n}\n\nexport interface EntitySchemaDefinition {\n sections: EntitySchemaSectionDefinition[];\n}\n\nexport interface EntityFile {\n name: string;\n type: string;\n created?: string;\n updated: string;\n extraFrontmatterLines?: string[];\n preSectionLines?: string[];\n facts: string[];\n summary?: string;\n synthesis?: string;\n synthesisUpdatedAt?: string;\n synthesisTimelineCount?: number;\n synthesisStructuredFactCount?: number;\n synthesisStructuredFactDigest?: string;\n synthesisVersion?: number;\n timeline: EntityTimelineEntry[];\n structuredSections?: EntityStructuredSection[];\n relationships: EntityRelationship[];\n activity: EntityActivityEntry[];\n aliases: string[];\n extraSections?: Array<{\n title: string;\n lines: string[];\n }>;\n}\n\nexport interface ScoredEntity {\n name: string;\n type: string;\n score: number;\n factCount: number;\n summary?: string;\n topRelationships: string[];\n}\n\nexport interface ExtractedRelationship {\n source: string;\n target: string;\n label: string;\n extractionSource?: ExtractionPassSource;\n promptedByQuestion?: string;\n}\n\nexport interface ConsolidationItem {\n existingId: string;\n action: ConsolidationAction;\n mergeWith?: string;\n updatedContent?: string;\n reason: string;\n}\n\nexport interface ConsolidationResult {\n items: ConsolidationItem[];\n profileUpdates: string[];\n entityUpdates: EntityMention[];\n}\n\nexport interface ConsolidationObservation {\n runAt: string;\n recentMemories: MemoryFile[];\n existingMemories: MemoryFile[];\n profile: string;\n result: ConsolidationResult;\n merged: number;\n invalidated: number;\n}\nexport interface QmdSearchResult extends OriginMetadata, RecallDriftAnnotation, StateViewResult {\n docid: string;\n path: string;\n snippet: string;\n score: number;\n /**\n * Pool-normalized rank × multiplier from the last adjusted rerank stage.\n * MMR prefers it over `score`; additive boosts compose on this scale.\n */\n effectiveScore?: number;\n line?: number;\n explain?: QmdSearchExplain;\n transport?: \"daemon\" | \"subprocess\" | \"hybrid\" | \"scoped_prefilter\";\n /** Namespace that owned this result when returned by a namespace fanout search. */\n namespace?: string;\n /** Graph activation path evidence from opt-in path scoring. */\n pathNodeIds?: string[];\n /** True when a graph path penalty was applied. */\n pathPenaltyApplied?: boolean;\n sourceConnector?: string;\n}\n\nexport interface QmdSearchExplain {\n ftsScores?: number[];\n vectorScores?: number[];\n /** QMD 2.5 nested RRF `totalScore`, or legacy numeric RRF score. */\n rrf?: number;\n rrfRank?: number;\n rrfPositionScore?: number;\n rrfBaseScore?: number;\n rrfTopRankBonus?: number;\n rerankScore?: number;\n blendedScore?: number;\n /** Additive boost applied from `reinforcement_count` frontmatter (issue #687 PR 3/4). */\n reinforcementBoost?: number;\n}\n\nexport interface MetaState {\n extractionCount: number;\n lastExtractionAt: string | null;\n lastConsolidationAt: string | null;\n totalMemories: number;\n totalEntities: number;\n processedExtractionFingerprints?: Array<{\n fingerprint: string;\n observedAt: string;\n }>;\n /**\n * Per-fingerprint extraction retry/backoff state (extraction hot-loop\n * hardening). Optional and default-safe: older meta files without it load\n * unchanged. Bounded to the newest entries at write time.\n */\n extractionRetryState?: Array<{\n fingerprint: string;\n attempts: number;\n nextEligibleAt: string; // ISO\n firstFailedAt: string; // ISO\n lastFailureClass: ExtractionFailureClass;\n }>;\n}\n\nexport type MemoryActionType =\n | \"store_episode\"\n | \"store_note\"\n | \"update_note\"\n | \"create_artifact\"\n | \"summarize_node\"\n | \"discard\"\n | \"link_graph\";\n\nexport type MemoryActionOutcome = \"applied\" | \"skipped\" | \"failed\";\n\nexport type MemoryActionPolicyDecision = \"allow\" | \"defer\" | \"deny\";\n\nexport type MemoryActionStatus = \"validated\" | \"applied\" | \"rejected\";\n\nexport type MemoryActionEligibilitySource = \"extraction\" | \"consolidation\" | \"replay\" | \"manual\" | \"unknown\";\n\nexport interface MemoryActionEligibilityContext {\n confidence: number;\n lifecycleState: LifecycleState;\n importance: number;\n source: MemoryActionEligibilitySource;\n}\n\nexport interface MemoryActionPolicyResult {\n action: MemoryActionType;\n decision: MemoryActionPolicyDecision;\n rationale: string;\n eligibility: MemoryActionEligibilityContext;\n}\n\nexport interface MemoryActionEvent {\n schemaVersion?: number;\n actionId?: string;\n timestamp: string;\n action: MemoryActionType;\n outcome: MemoryActionOutcome;\n status?: MemoryActionStatus;\n actor?: string;\n subsystem?: string;\n reason?: string;\n memoryId?: string;\n namespace?: string;\n sessionKey?: string;\n sourceSessionKey?: string;\n checkpointCapturedAt?: string;\n checkpointTtl?: string;\n checkpointTurnCount?: number;\n inputSummary?: string;\n outputMemoryIds?: string[];\n dryRun?: boolean;\n policyVersion?: string;\n promptHash?: string;\n policyDecision?: MemoryActionPolicyDecision;\n policyRationale?: string;\n policyEligibility?: MemoryActionEligibilityContext;\n}\n\nexport type MemoryLifecycleEventType =\n | \"created\"\n | \"updated\"\n | \"superseded\"\n | \"archived\"\n | \"rejected\"\n | \"restored\"\n | \"merged\"\n | \"imported\"\n | \"promoted\"\n | \"explicit_capture_accepted\"\n | \"explicit_capture_queued\";\n\nexport interface MemoryLifecycleStateSummary {\n category?: MemoryCategory;\n path?: string;\n status?: MemoryStatus;\n lifecycleState?: LifecycleState;\n}\n\nexport interface MemoryLifecycleEvent {\n eventId: string;\n memoryId: string;\n eventType: MemoryLifecycleEventType;\n timestamp: string;\n actor: string;\n reasonCode?: string;\n ruleVersion: string;\n relatedMemoryIds?: string[];\n before?: MemoryLifecycleStateSummary;\n after?: MemoryLifecycleStateSummary;\n correlationId?: string;\n}\n\nexport interface MemoryProjectionCurrentState {\n memoryId: string;\n category: MemoryCategory;\n status: MemoryStatus;\n lifecycleState?: LifecycleState;\n path: string;\n pathRel: string;\n created: string;\n updated: string;\n archivedAt?: string;\n supersededAt?: string;\n entityRef?: string;\n source: string;\n confidence: number;\n confidenceTier: ConfidenceTier;\n memoryKind?: MemoryFrontmatter[\"memoryKind\"];\n accessCount?: number;\n lastAccessed?: string;\n tags?: string[];\n preview?: string;\n privateRecord?: boolean;\n}\n\nexport interface CompressionGuidelineOptimizerSourceWindow {\n from: string;\n to: string;\n}\n\nexport interface CompressionGuidelineOptimizerEventCounts {\n total: number;\n applied: number;\n skipped: number;\n failed: number;\n}\n\nexport type CompressionGuidelineActivationState = \"draft\" | \"active\";\n\nexport interface CompressionGuidelineOptimizerActionSummary {\n action: MemoryActionType;\n total: number;\n outcomes: Record<MemoryActionOutcome, number>;\n quality: {\n good: number;\n poor: number;\n unknown: number;\n };\n}\n\nexport interface CompressionGuidelineOptimizerRuleUpdate {\n action: MemoryActionType;\n delta: number;\n direction: \"increase\" | \"decrease\" | \"hold\";\n confidence: \"low\" | \"medium\" | \"high\";\n notes: string[];\n}\n\nexport interface CompressionGuidelineOptimizerState {\n version: number;\n updatedAt: string;\n sourceWindow: CompressionGuidelineOptimizerSourceWindow;\n eventCounts: CompressionGuidelineOptimizerEventCounts;\n guidelineVersion: number;\n contentHash?: string;\n activationState?: CompressionGuidelineActivationState;\n actionSummaries?: CompressionGuidelineOptimizerActionSummary[];\n ruleUpdates?: CompressionGuidelineOptimizerRuleUpdate[];\n}\n\n// Continuity incident + improvement-loop types live in a sibling module to keep\n// types.ts under its size ceiling; re-exported so `./types.js` imports resolve.\nexport type {\n ContinuityIncidentState,\n ContinuityIncidentRecord,\n ContinuityIncidentOpenInput,\n ContinuityIncidentCloseInput,\n ContinuityLoopCadence,\n ContinuityLoopStatus,\n ContinuityImprovementLoop,\n ContinuityLoopUpsertInput,\n ContinuityLoopReviewInput,\n} from \"./types-continuity.js\";\n\n/** Entry in the access tracking buffer (batched updates) */\nexport interface AccessTrackingEntry {\n memoryId: string;\n memoryPath?: string;\n newCount: number;\n lastAccessed: string;\n}\n\nexport interface SignalScanResult {\n level: SignalLevel;\n patterns: string[];\n}\n\n// ============================================================================\n// LLM Trace Callback (for external observability plugins)\n// ============================================================================\n\nexport interface LlmTraceEvent {\n kind: \"llm_start\" | \"llm_end\" | \"llm_error\";\n traceId: string;\n model: string;\n operation: \"extraction\" | \"consolidation\" | \"profile_consolidation\" | \"identity_consolidation\" | \"day_summary\";\n input?: string;\n output?: string;\n durationMs?: number;\n error?: string;\n tokenUsage?: { input?: number; output?: number; total?: number };\n}\n\nexport interface RecallTraceEvent {\n kind: \"recall_summary\";\n traceId: string;\n operation: \"recall\";\n sessionKey?: string;\n promptHash: string;\n promptLength: number;\n retrievalQueryHash: string;\n retrievalQueryLength: number;\n recallMode: RecallPlanMode;\n recallResultLimit: number;\n qmdEnabled: boolean;\n qmdAvailable: boolean;\n recallNamespaces: string[];\n source: \"none\" | \"hot_qmd\" | \"hot_embedding\" | \"cold_fallback\" | \"recent_scan\";\n recalledMemoryCount: number;\n injected: boolean;\n contextChars: number;\n policyVersion?: string;\n identityInjectionMode?: IdentityInjectionMode | \"none\";\n identityInjectedChars?: number;\n identityInjectionTruncated?: boolean;\n durationMs: number;\n timings?: Record<string, string>;\n /**\n * The full recalled memory context added to the runtime context.\n * Only populated when `traceRecallContent` config option is `true`.\n * Omitted by default to avoid sending potentially sensitive memory content\n * to external trace collectors unless explicitly opted in.\n */\n recalledContent?: string;\n}\n\nexport type EngramTraceEvent = LlmTraceEvent | RecallTraceEvent;\nexport type LlmTraceCallback = (event: EngramTraceEvent) => void;\n\n// ============================================================================\n// Gateway Configuration Types (for fallback AI)\n// ============================================================================\n\nexport type ModelApi = \"openai-completions\" | \"anthropic-messages\" | \"google-generative\" | \"codex-cli\" | string;\nexport type CodexCliReasoningEffort = \"low\" | \"medium\" | \"high\" | \"xhigh\";\n\nexport type ModelProviderAuthMode = \"bearer\" | \"header\" | \"query\";\n\nexport interface ModelDefinitionConfig {\n id: string;\n name?: string;\n contextWindow?: number;\n maxOutputTokens?: number;\n costPer1MInput?: number;\n costPer1MOutput?: number;\n aliases?: string[];\n}\n\nexport interface ModelProviderConfig {\n baseUrl: string;\n apiKey?: string | Record<string, unknown>;\n auth?: ModelProviderAuthMode;\n api?: ModelApi;\n headers?: Record<string, string>;\n authHeader?: boolean;\n disableThinking?: boolean;\n executable?: string;\n reasoningEffort?: CodexCliReasoningEffort;\n codexCliExecutable?: string;\n codexCliReasoningEffort?: CodexCliReasoningEffort;\n retryOptions?: {\n timeoutMs?: number;\n };\n models: ModelDefinitionConfig[];\n}\n\nexport interface AgentDefaultsConfig {\n model?: {\n primary?: string;\n backup?: string;\n fallbacks?: string[];\n };\n thinking?: {\n mode?: \"off\" | \"on\" | \"adaptive\";\n budget?: number;\n };\n}\n\nexport interface AgentPersonaModelConfig {\n primary?: string;\n fallbacks?: string[];\n}\n\nexport interface AgentPersona {\n id: string;\n name?: string;\n model?: AgentPersonaModelConfig;\n}\n\nexport interface GatewayConfig {\n agents?: {\n defaults?: AgentDefaultsConfig;\n list?: AgentPersona[];\n };\n models?: {\n providers?: Record<string, ModelProviderConfig>;\n };\n}\n\n// ============================================================================\n// Transcript & Context Preservation (v2.0)\n// ============================================================================\n\nexport interface TranscriptEntry {\n timestamp: string;\n role: \"user\" | \"assistant\";\n content: string;\n sessionKey: string;\n turnId: string;\n metadata?: {\n compactAfter?: boolean;\n compactionId?: string | null;\n messageId?: string;\n threadId?: string;\n replyToId?: string;\n replyToBody?: string;\n replyToSender?: string;\n };\n}\n\nexport interface Checkpoint {\n sessionKey: string;\n capturedAt: string;\n turns: TranscriptEntry[];\n ttl: string; // ISO timestamp when checkpoint expires\n}\n\nexport interface HourlySummary {\n hour: string; // \"2026-02-08T14:00:00Z\"\n sessionKey: string;\n bullets: string[];\n turnCount: number;\n generatedAt: string;\n}\n\n// Dreams Pipeline Telemetry (issue #678 PR 3/4). Re-export from\n// dreams-ledger.ts so callers importing types.js stay unchanged.\nexport type {\n DreamsPhase,\n DreamsPhaseStatus,\n DreamsStatusResult,\n DreamsRunResult,\n} from \"./maintenance/dreams-ledger.js\";\nexport type { RecallWhyExpectation, RecallWhyReport, RecallWhyStageRecord } from \"./recall-why.js\";\n"],"mappings":";AA+JO,IAAM,2BAAwD,CAAC,SAAS,WAAW,KAAK;AAOxF,IAAM,4BAA8C;AAEpD,SAAS,mBAAmB,OAA2C;AAC5E,SAAO,OAAO,UAAU,YAAa,yBAA+C,SAAS,KAAK;AACpG;AA6fO,SAAS,eAAe,OAA+B;AAC5D,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,SAAS,IAAK,QAAO;AACzB,MAAI,SAAS,IAAK,QAAO;AACzB,SAAO;AACT;AAGO,IAAM,uBAAuB;","names":[]}
@@ -1,22 +0,0 @@
1
- import {
2
- displayErrorDetail
3
- } from "./chunk-RL6NEVXT.js";
4
-
5
- // src/wearables/errors.ts
6
- var WearablesInputError = class extends Error {
7
- constructor(message) {
8
- super(message);
9
- this.name = "WearablesInputError";
10
- }
11
- };
12
- function describeErrorForOperator(err) {
13
- if (err instanceof WearablesInputError) return err.message;
14
- const detail = displayErrorDetail(err);
15
- return detail.length > 0 ? detail : "unexpected non-Error failure";
16
- }
17
-
18
- export {
19
- WearablesInputError,
20
- describeErrorForOperator
21
- };
22
- //# sourceMappingURL=chunk-V7IZKJI3.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/wearables/errors.ts"],"sourcesContent":["/**\n * Error thrown by wearables surfaces for caller-correctable problems:\n * invalid parameters, unknown sources, disabled subsystem, missing\n * connector packages. Transport layers map this to 400-class responses;\n * anything else is a backend fault and bubbles to the 500 handler.\n */\n\nimport { displayErrorDetail } from \"../runtime/better-sqlite.js\";\n\nexport class WearablesInputError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"WearablesInputError\";\n }\n}\n\n/**\n * Render a caught error for operator-facing sync warnings and auth\n * details. Warnings travel back through CLI/MCP/HTTP responses, so\n * foreign error text never passes through — delegation to the\n * project-standard `displayErrorDetail` exposes only the error class\n * and Node errno code. Wearables' own input errors (authored messages,\n * no foreign text) pass through verbatim.\n */\nexport function describeErrorForOperator(err: unknown): string {\n if (err instanceof WearablesInputError) return err.message;\n const detail = displayErrorDetail(err);\n return detail.length > 0 ? detail : \"unexpected non-Error failure\";\n}\n"],"mappings":";;;;;AASO,IAAM,sBAAN,cAAkC,MAAM;AAAA,EAC7C,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAUO,SAAS,yBAAyB,KAAsB;AAC7D,MAAI,eAAe,oBAAqB,QAAO,IAAI;AACnD,QAAM,SAAS,mBAAmB,GAAG;AACrC,SAAO,OAAO,SAAS,IAAI,SAAS;AACtC;","names":[]}