@remnic/core 1.0.2 → 1.0.3

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 (347) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -0
  3. package/dist/access-cli.d.ts +13 -3
  4. package/dist/access-cli.js +90 -75
  5. package/dist/access-cli.js.map +1 -1
  6. package/dist/access-http.d.ts +10 -3
  7. package/dist/access-http.js +25 -18
  8. package/dist/access-mcp.d.ts +30 -3
  9. package/dist/access-mcp.js +16 -1
  10. package/dist/access-schema.d.ts +12 -12
  11. package/dist/access-schema.js +1 -1
  12. package/dist/access-service.d.ts +65 -4
  13. package/dist/access-service.js +21 -15
  14. package/dist/active-memory-bridge.d.ts +66 -0
  15. package/dist/active-memory-bridge.js +11 -0
  16. package/dist/active-recall.d.ts +96 -0
  17. package/dist/active-recall.js +308 -0
  18. package/dist/active-recall.js.map +1 -0
  19. package/dist/behavior-learner.js +1 -1
  20. package/dist/bootstrap.d.ts +6 -3
  21. package/dist/bootstrap.js +2 -2
  22. package/dist/boxes.js +2 -2
  23. package/dist/briefing.d.ts +169 -0
  24. package/dist/briefing.js +52 -0
  25. package/dist/briefing.js.map +1 -0
  26. package/dist/buffer.d.ts +19 -5
  27. package/dist/buffer.js +2 -2
  28. package/dist/calibration.js +6 -6
  29. package/dist/causal-behavior.js +5 -5
  30. package/dist/causal-chain.js +3 -3
  31. package/dist/causal-consolidation.d.ts +22 -2
  32. package/dist/causal-consolidation.js +36 -9
  33. package/dist/causal-consolidation.js.map +1 -1
  34. package/dist/causal-retrieval.js +6 -6
  35. package/dist/causal-trajectory-graph.js +1 -1
  36. package/dist/causal-trajectory.d.ts +14 -1
  37. package/dist/causal-trajectory.js +5 -1
  38. package/dist/{chunk-KWBU5S5U.js → chunk-2ODBA7MQ.js} +9 -3
  39. package/dist/chunk-2ODBA7MQ.js.map +1 -0
  40. package/dist/{chunk-6UJQNRIO.js → chunk-2VFW5K5U.js} +93 -36
  41. package/dist/chunk-2VFW5K5U.js.map +1 -0
  42. package/dist/chunk-3PG3H5TD.js +7 -0
  43. package/dist/chunk-3PG3H5TD.js.map +1 -0
  44. package/dist/{chunk-NTTLPF7F.js → chunk-3QFQGRHO.js} +5 -5
  45. package/dist/chunk-4DJQYKMN.js +187 -0
  46. package/dist/chunk-4DJQYKMN.js.map +1 -0
  47. package/dist/chunk-4KAN3GZ3.js +225 -0
  48. package/dist/chunk-4KAN3GZ3.js.map +1 -0
  49. package/dist/{chunk-ORZMT74A.js → chunk-4NRAJUDS.js} +11 -1
  50. package/dist/chunk-4NRAJUDS.js.map +1 -0
  51. package/dist/{chunk-B7LOFDVE.js → chunk-4WMCPJWX.js} +8 -3
  52. package/dist/chunk-4WMCPJWX.js.map +1 -0
  53. package/dist/{chunk-G3AG3KZN.js → chunk-5IZL4DCV.js} +2 -2
  54. package/dist/{chunk-BRK4ODMI.js → chunk-5NPGSAVB.js} +2 -2
  55. package/dist/chunk-6MKAMLQL.js +16 -0
  56. package/dist/chunk-6MKAMLQL.js.map +1 -0
  57. package/dist/{chunk-ESSMF2FR.js → chunk-6PFRXT4K.js} +15 -6
  58. package/dist/chunk-6PFRXT4K.js.map +1 -0
  59. package/dist/chunk-6ZH4TU6I.js +245 -0
  60. package/dist/chunk-6ZH4TU6I.js.map +1 -0
  61. package/dist/{chunk-V4YC4LUK.js → chunk-74JR4N5J.js} +175 -63
  62. package/dist/chunk-74JR4N5J.js.map +1 -0
  63. package/dist/{chunk-L5RPWGFK.js → chunk-7DHTMOND.js} +2 -2
  64. package/dist/{chunk-TVVVQQAK.js → chunk-7PA4OZEU.js} +53 -11
  65. package/dist/chunk-7PA4OZEU.js.map +1 -0
  66. package/dist/{chunk-Q6FETXJA.js → chunk-7SEAZFFB.js} +2 -2
  67. package/dist/chunk-ALXMCZEU.js +332 -0
  68. package/dist/chunk-ALXMCZEU.js.map +1 -0
  69. package/dist/{chunk-QANCTXQF.js → chunk-AYPYCLR7.js} +3 -3
  70. package/dist/{chunk-WWIQTB2Y.js → chunk-BKQJBXXX.js} +9 -2
  71. package/dist/chunk-BKQJBXXX.js.map +1 -0
  72. package/dist/{chunk-LP47L3ZX.js → chunk-BTY5RRRF.js} +7 -7
  73. package/dist/{chunk-SCHEKPYH.js → chunk-C2EFFULQ.js} +1 -1
  74. package/dist/{chunk-GJR6D6KC.js → chunk-D654IBA6.js} +2 -2
  75. package/dist/{chunk-UV2FO7J4.js → chunk-E6K4NIEU.js} +2 -2
  76. package/dist/{chunk-T4WRIV2C.js → chunk-EABGC2TL.js} +2 -2
  77. package/dist/chunk-ECKDIK5F.js +813 -0
  78. package/dist/chunk-ECKDIK5F.js.map +1 -0
  79. package/dist/chunk-EJI5XIBB.js +232 -0
  80. package/dist/chunk-EJI5XIBB.js.map +1 -0
  81. package/dist/{chunk-ONRU4L2N.js → chunk-FEMOX5AD.js} +2 -2
  82. package/dist/{chunk-IFFFR3MR.js → chunk-FSFEQI74.js} +3 -3
  83. package/dist/chunk-G4SK7DSQ.js +121 -0
  84. package/dist/chunk-G4SK7DSQ.js.map +1 -0
  85. package/dist/{chunk-UIYZ5T3I.js → chunk-GJQPH5G3.js} +8 -8
  86. package/dist/{chunk-2PO5ZRKV.js → chunk-GZCUW5IC.js} +16 -3
  87. package/dist/chunk-GZCUW5IC.js.map +1 -0
  88. package/dist/{chunk-IZME7KW2.js → chunk-HITJFT7E.js} +24 -10
  89. package/dist/{chunk-IZME7KW2.js.map → chunk-HITJFT7E.js.map} +1 -1
  90. package/dist/chunk-IQT3XTKW.js +121 -0
  91. package/dist/chunk-IQT3XTKW.js.map +1 -0
  92. package/dist/{chunk-BDFZXRSO.js → chunk-J4IYOZZ5.js} +15 -2
  93. package/dist/chunk-J4IYOZZ5.js.map +1 -0
  94. package/dist/{chunk-ZKYI7UVO.js → chunk-JR4ZC3G4.js} +2 -2
  95. package/dist/{chunk-UCYSTFZR.js → chunk-JRNQ3RNA.js} +2 -2
  96. package/dist/{chunk-UYSKNO6E.js → chunk-JROGC36Y.js} +15 -4
  97. package/dist/chunk-JROGC36Y.js.map +1 -0
  98. package/dist/{chunk-GPGBSNKM.js → chunk-K4FLSOR5.js} +2 -2
  99. package/dist/{chunk-M5ZBBBJI.js → chunk-KEG4GNGI.js} +2 -2
  100. package/dist/chunk-KVE7R4CG.js +320 -0
  101. package/dist/chunk-KVE7R4CG.js.map +1 -0
  102. package/dist/{chunk-L7WO3MZ4.js → chunk-KWP7T3DP.js} +2 -2
  103. package/dist/chunk-LAYN4LDC.js +267 -0
  104. package/dist/chunk-LAYN4LDC.js.map +1 -0
  105. package/dist/{chunk-PGK3VUHN.js → chunk-MTLYEMJB.js} +3 -2
  106. package/dist/chunk-MTLYEMJB.js.map +1 -0
  107. package/dist/{chunk-J47FNDR7.js → chunk-MYQWXITD.js} +7 -7
  108. package/dist/{chunk-YNI4S5WT.js → chunk-N53K2EXC.js} +2 -2
  109. package/dist/{chunk-763GUIOU.js → chunk-NBNN5GOB.js} +2 -2
  110. package/dist/{chunk-CXWFUJR2.js → chunk-NSB3WSYS.js} +125 -6
  111. package/dist/chunk-NSB3WSYS.js.map +1 -0
  112. package/dist/{chunk-KL4CP4SB.js → chunk-O5ETUNBT.js} +17 -5
  113. package/dist/chunk-O5ETUNBT.js.map +1 -0
  114. package/dist/{chunk-OOSWAUYB.js → chunk-ODWDQNRE.js} +2 -2
  115. package/dist/{chunk-ISY75RLM.js → chunk-OJFGVJS6.js} +288 -7
  116. package/dist/chunk-OJFGVJS6.js.map +1 -0
  117. package/dist/{chunk-HLBYLYRD.js → chunk-PAORGQRI.js} +70 -13
  118. package/dist/chunk-PAORGQRI.js.map +1 -0
  119. package/dist/{chunk-ZJLY4QSU.js → chunk-PMB3WGDL.js} +69 -6
  120. package/dist/chunk-PMB3WGDL.js.map +1 -0
  121. package/dist/{chunk-J3BT33K7.js → chunk-POBPGDWI.js} +5 -5
  122. package/dist/{chunk-QWUUMMIK.js → chunk-POMSFKTB.js} +1351 -76
  123. package/dist/chunk-POMSFKTB.js.map +1 -0
  124. package/dist/{chunk-OTAVQCSF.js → chunk-PYXS46O7.js} +2 -2
  125. package/dist/chunk-QDW3E4RD.js +108 -0
  126. package/dist/chunk-QDW3E4RD.js.map +1 -0
  127. package/dist/{chunk-YNCQ7E4M.js → chunk-QDYXG4CS.js} +4 -3
  128. package/dist/chunk-QDYXG4CS.js.map +1 -0
  129. package/dist/{chunk-XUHI52HK.js → chunk-QKAH5B6E.js} +4 -4
  130. package/dist/{chunk-HLXVTBF3.js → chunk-QNJMBKFK.js} +3 -2
  131. package/dist/chunk-QNJMBKFK.js.map +1 -0
  132. package/dist/chunk-RCICHSHL.js +789 -0
  133. package/dist/chunk-RCICHSHL.js.map +1 -0
  134. package/dist/{chunk-HG2NKWR2.js → chunk-S4LX5EBI.js} +2 -2
  135. package/dist/{chunk-4A24LIM2.js → chunk-S75M5ZRK.js} +2 -2
  136. package/dist/{chunk-QCCCQT3O.js → chunk-TBBDFYXW.js} +2 -2
  137. package/dist/chunk-TBBDFYXW.js.map +1 -0
  138. package/dist/{chunk-U4PV25RD.js → chunk-U2IQTSBY.js} +1 -1
  139. package/dist/chunk-U2IQTSBY.js.map +1 -0
  140. package/dist/chunk-U66YHYC7.js +31 -0
  141. package/dist/chunk-U66YHYC7.js.map +1 -0
  142. package/dist/{chunk-MWGVGUIS.js → chunk-UEYA6UC7.js} +36 -4
  143. package/dist/chunk-UEYA6UC7.js.map +1 -0
  144. package/dist/{chunk-MDDAA2AO.js → chunk-UPMD5XND.js} +2 -2
  145. package/dist/{chunk-M5KEYE5E.js → chunk-URB2WSKZ.js} +2 -2
  146. package/dist/chunk-UVJFDP7P.js +202 -0
  147. package/dist/chunk-UVJFDP7P.js.map +1 -0
  148. package/dist/{chunk-QY2BHY5O.js → chunk-V7XCAHIB.js} +265 -25
  149. package/dist/chunk-V7XCAHIB.js.map +1 -0
  150. package/dist/chunk-W6SL7OFG.js +180 -0
  151. package/dist/chunk-W6SL7OFG.js.map +1 -0
  152. package/dist/{chunk-QDOSNLB4.js → chunk-X4WESCKA.js} +17 -15
  153. package/dist/chunk-X4WESCKA.js.map +1 -0
  154. package/dist/{chunk-OTFNI3OO.js → chunk-XMGSSBFX.js} +1738 -383
  155. package/dist/chunk-XMGSSBFX.js.map +1 -0
  156. package/dist/chunk-YDBIWGNI.js +298 -0
  157. package/dist/chunk-YDBIWGNI.js.map +1 -0
  158. package/dist/chunk-YFYL2SIJ.js +7857 -0
  159. package/dist/chunk-YFYL2SIJ.js.map +1 -0
  160. package/dist/chunking.js +1 -1
  161. package/dist/citations.d.ts +67 -0
  162. package/dist/citations.js +13 -0
  163. package/dist/citations.js.map +1 -0
  164. package/dist/cli-DwIBnp2g.d.ts +1240 -0
  165. package/dist/cli.d.ts +31 -1147
  166. package/dist/cli.js +149 -7092
  167. package/dist/cli.js.map +1 -1
  168. package/dist/codex-materialize-CQlLTzke.d.ts +139 -0
  169. package/dist/codex-thread-key.d.ts +3 -0
  170. package/dist/codex-thread-key.js +7 -0
  171. package/dist/codex-thread-key.js.map +1 -0
  172. package/dist/config.js +3 -2
  173. package/dist/connectors/codex/instructions.md +160 -0
  174. package/dist/connectors/codex/resources/namespace-cheatsheet.md +48 -0
  175. package/dist/day-summary.d.ts +7 -2
  176. package/dist/day-summary.js +5 -2
  177. package/dist/embedding-fallback.d.ts +96 -2
  178. package/dist/embedding-fallback.js +6 -4
  179. package/dist/{engine-2A6J4XEX.js → engine-X7X3AAG3.js} +10 -7
  180. package/dist/engine-X7X3AAG3.js.map +1 -0
  181. package/dist/entity-retrieval.d.ts +3 -2
  182. package/dist/entity-retrieval.js +10 -7
  183. package/dist/entity-schema.d.ts +11 -0
  184. package/dist/entity-schema.js +19 -0
  185. package/dist/entity-schema.js.map +1 -0
  186. package/dist/explicit-capture.d.ts +6 -3
  187. package/dist/explicit-capture.js +2 -2
  188. package/dist/extraction-judge.d.ts +66 -0
  189. package/dist/extraction-judge.js +18 -0
  190. package/dist/extraction-judge.js.map +1 -0
  191. package/dist/extraction.d.ts +1 -0
  192. package/dist/extraction.js +12 -10
  193. package/dist/fallback-llm.js +4 -4
  194. package/dist/graph.js +1 -1
  195. package/dist/importance.d.ts +11 -1
  196. package/dist/importance.js +3 -1
  197. package/dist/index.d.ts +1140 -8
  198. package/dist/index.js +3350 -333
  199. package/dist/index.js.map +1 -1
  200. package/dist/intent.d.ts +2 -1
  201. package/dist/intent.js +3 -1
  202. package/dist/lifecycle.js +1 -1
  203. package/dist/local-llm.js +2 -2
  204. package/dist/logger.d.ts +1 -1
  205. package/dist/logger.js +1 -1
  206. package/dist/memory-cache.d.ts +2 -2
  207. package/dist/memory-cache.js +1 -1
  208. package/dist/{memory-projection-store-NxMkbocT.d.ts → memory-projection-store-DeSXPh1j.d.ts} +1 -1
  209. package/dist/memory-projection-store.d.ts +1 -1
  210. package/dist/model-registry.js +2 -2
  211. package/dist/models-json.js +2 -2
  212. package/dist/native-knowledge.js +2 -2
  213. package/dist/negative.js +2 -2
  214. package/dist/operator-toolkit.js +20 -16
  215. package/dist/{orchestrator-zTa-Qo-1.d.ts → orchestrator-B9kwlCep.d.ts} +252 -7
  216. package/dist/orchestrator.d.ts +6 -3
  217. package/dist/orchestrator.js +70 -58
  218. package/dist/page-versioning.d.ts +77 -0
  219. package/dist/page-versioning.js +15 -0
  220. package/dist/page-versioning.js.map +1 -0
  221. package/dist/plugin-id.d.ts +37 -0
  222. package/dist/plugin-id.js +11 -0
  223. package/dist/plugin-id.js.map +1 -0
  224. package/dist/policy-runtime.js +2 -2
  225. package/dist/profiling.js +2 -2
  226. package/dist/qmd.d.ts +5 -2
  227. package/dist/qmd.js +3 -3
  228. package/dist/recall-audit.d.ts +20 -0
  229. package/dist/recall-audit.js +50 -0
  230. package/dist/recall-audit.js.map +1 -0
  231. package/dist/recall-mmr.d.ts +152 -0
  232. package/dist/recall-mmr.js +17 -0
  233. package/dist/recall-mmr.js.map +1 -0
  234. package/dist/recall-qos.js +2 -2
  235. package/dist/recall-state.js +2 -2
  236. package/dist/relevance.js +2 -2
  237. package/dist/resolve-provider-secret.js +2 -2
  238. package/dist/resume-bundles.js +5 -4
  239. package/dist/retrieval-agents.js +2 -2
  240. package/dist/retrieval.js +2 -2
  241. package/dist/schemas.d.ts +398 -40
  242. package/dist/schemas.js +3 -1
  243. package/dist/sdk-compat.d.ts +2 -0
  244. package/dist/sdk-compat.js +6 -3
  245. package/dist/sdk-compat.js.map +1 -1
  246. package/dist/semantic-chunking.d.ts +87 -0
  247. package/dist/semantic-chunking.js +20 -0
  248. package/dist/semantic-chunking.js.map +1 -0
  249. package/dist/semantic-consolidation-DrvSYRdB.d.ts +119 -0
  250. package/dist/semantic-consolidation.d.ts +4 -42
  251. package/dist/semantic-consolidation.js +23 -2
  252. package/dist/semantic-rule-promotion.js +9 -6
  253. package/dist/semantic-rule-verifier.js +10 -7
  254. package/dist/session-observer-state.js +2 -2
  255. package/dist/session-toggles.d.ts +22 -0
  256. package/dist/session-toggles.js +116 -0
  257. package/dist/session-toggles.js.map +1 -0
  258. package/dist/skills-registry.d.ts +47 -0
  259. package/dist/skills-registry.js +48 -0
  260. package/dist/skills-registry.js.map +1 -0
  261. package/dist/source-attribution.d.ts +169 -0
  262. package/dist/source-attribution.js +27 -0
  263. package/dist/source-attribution.js.map +1 -0
  264. package/dist/storage.d.ts +171 -10
  265. package/dist/storage.js +16 -5
  266. package/dist/summarizer.js +7 -7
  267. package/dist/temporal-supersession.d.ts +127 -0
  268. package/dist/temporal-supersession.js +20 -0
  269. package/dist/temporal-supersession.js.map +1 -0
  270. package/dist/threading.js +2 -2
  271. package/dist/tier-migration.d.ts +2 -1
  272. package/dist/tier-routing.js +2 -2
  273. package/dist/tokens.d.ts +21 -1
  274. package/dist/tokens.js +5 -1
  275. package/dist/transcript.js +2 -2
  276. package/dist/types.d.ts +497 -3
  277. package/dist/types.js +1 -1
  278. package/dist/utility-learner.js +2 -2
  279. package/dist/utility-runtime.js +3 -3
  280. package/dist/verified-recall.js +11 -8
  281. package/dist/whitespace.d.ts +4 -0
  282. package/dist/whitespace.js +9 -0
  283. package/dist/whitespace.js.map +1 -0
  284. package/package.json +14 -8
  285. package/dist/chunk-2CJCWDMR.js +0 -87
  286. package/dist/chunk-2CJCWDMR.js.map +0 -1
  287. package/dist/chunk-2PO5ZRKV.js.map +0 -1
  288. package/dist/chunk-6UJQNRIO.js.map +0 -1
  289. package/dist/chunk-B7LOFDVE.js.map +0 -1
  290. package/dist/chunk-BDFZXRSO.js.map +0 -1
  291. package/dist/chunk-CXWFUJR2.js.map +0 -1
  292. package/dist/chunk-DORBM6OB.js +0 -81
  293. package/dist/chunk-DORBM6OB.js.map +0 -1
  294. package/dist/chunk-ESSMF2FR.js.map +0 -1
  295. package/dist/chunk-HLBYLYRD.js.map +0 -1
  296. package/dist/chunk-HLXVTBF3.js.map +0 -1
  297. package/dist/chunk-ISY75RLM.js.map +0 -1
  298. package/dist/chunk-KL4CP4SB.js.map +0 -1
  299. package/dist/chunk-KWBU5S5U.js.map +0 -1
  300. package/dist/chunk-MWGVGUIS.js.map +0 -1
  301. package/dist/chunk-ORZMT74A.js.map +0 -1
  302. package/dist/chunk-OTFNI3OO.js.map +0 -1
  303. package/dist/chunk-PGK3VUHN.js.map +0 -1
  304. package/dist/chunk-QCCCQT3O.js.map +0 -1
  305. package/dist/chunk-QDOSNLB4.js.map +0 -1
  306. package/dist/chunk-QPKFPHOO.js +0 -178
  307. package/dist/chunk-QPKFPHOO.js.map +0 -1
  308. package/dist/chunk-QWUUMMIK.js.map +0 -1
  309. package/dist/chunk-QY2BHY5O.js.map +0 -1
  310. package/dist/chunk-TVVVQQAK.js.map +0 -1
  311. package/dist/chunk-U4PV25RD.js.map +0 -1
  312. package/dist/chunk-UYSKNO6E.js.map +0 -1
  313. package/dist/chunk-V4YC4LUK.js.map +0 -1
  314. package/dist/chunk-WWIQTB2Y.js.map +0 -1
  315. package/dist/chunk-YNCQ7E4M.js.map +0 -1
  316. package/dist/chunk-ZJLY4QSU.js.map +0 -1
  317. /package/dist/{engine-2A6J4XEX.js.map → active-memory-bridge.js.map} +0 -0
  318. /package/dist/{chunk-NTTLPF7F.js.map → chunk-3QFQGRHO.js.map} +0 -0
  319. /package/dist/{chunk-G3AG3KZN.js.map → chunk-5IZL4DCV.js.map} +0 -0
  320. /package/dist/{chunk-BRK4ODMI.js.map → chunk-5NPGSAVB.js.map} +0 -0
  321. /package/dist/{chunk-L5RPWGFK.js.map → chunk-7DHTMOND.js.map} +0 -0
  322. /package/dist/{chunk-Q6FETXJA.js.map → chunk-7SEAZFFB.js.map} +0 -0
  323. /package/dist/{chunk-QANCTXQF.js.map → chunk-AYPYCLR7.js.map} +0 -0
  324. /package/dist/{chunk-LP47L3ZX.js.map → chunk-BTY5RRRF.js.map} +0 -0
  325. /package/dist/{chunk-SCHEKPYH.js.map → chunk-C2EFFULQ.js.map} +0 -0
  326. /package/dist/{chunk-GJR6D6KC.js.map → chunk-D654IBA6.js.map} +0 -0
  327. /package/dist/{chunk-UV2FO7J4.js.map → chunk-E6K4NIEU.js.map} +0 -0
  328. /package/dist/{chunk-T4WRIV2C.js.map → chunk-EABGC2TL.js.map} +0 -0
  329. /package/dist/{chunk-ONRU4L2N.js.map → chunk-FEMOX5AD.js.map} +0 -0
  330. /package/dist/{chunk-IFFFR3MR.js.map → chunk-FSFEQI74.js.map} +0 -0
  331. /package/dist/{chunk-UIYZ5T3I.js.map → chunk-GJQPH5G3.js.map} +0 -0
  332. /package/dist/{chunk-ZKYI7UVO.js.map → chunk-JR4ZC3G4.js.map} +0 -0
  333. /package/dist/{chunk-UCYSTFZR.js.map → chunk-JRNQ3RNA.js.map} +0 -0
  334. /package/dist/{chunk-GPGBSNKM.js.map → chunk-K4FLSOR5.js.map} +0 -0
  335. /package/dist/{chunk-M5ZBBBJI.js.map → chunk-KEG4GNGI.js.map} +0 -0
  336. /package/dist/{chunk-L7WO3MZ4.js.map → chunk-KWP7T3DP.js.map} +0 -0
  337. /package/dist/{chunk-J47FNDR7.js.map → chunk-MYQWXITD.js.map} +0 -0
  338. /package/dist/{chunk-YNI4S5WT.js.map → chunk-N53K2EXC.js.map} +0 -0
  339. /package/dist/{chunk-763GUIOU.js.map → chunk-NBNN5GOB.js.map} +0 -0
  340. /package/dist/{chunk-OOSWAUYB.js.map → chunk-ODWDQNRE.js.map} +0 -0
  341. /package/dist/{chunk-J3BT33K7.js.map → chunk-POBPGDWI.js.map} +0 -0
  342. /package/dist/{chunk-OTAVQCSF.js.map → chunk-PYXS46O7.js.map} +0 -0
  343. /package/dist/{chunk-XUHI52HK.js.map → chunk-QKAH5B6E.js.map} +0 -0
  344. /package/dist/{chunk-HG2NKWR2.js.map → chunk-S4LX5EBI.js.map} +0 -0
  345. /package/dist/{chunk-4A24LIM2.js.map → chunk-S75M5ZRK.js.map} +0 -0
  346. /package/dist/{chunk-MDDAA2AO.js.map → chunk-UPMD5XND.js.map} +0 -0
  347. /package/dist/{chunk-M5KEYE5E.js.map → chunk-URB2WSKZ.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/connectors/codex-materialize-runner.ts","../src/connectors/codex-materialize.ts","../src/semantic-consolidation.ts"],"sourcesContent":["/**\n * codex-materialize-runner.ts — Thin I/O bridge for the Codex materializer.\n *\n * The pure rendering logic lives in {@link ./codex-materialize.js}. This file\n * is the place callers (consolidation hooks, CLI, session-end hook) go when\n * they want the whole \"load memories from storage → render → write\" flow.\n *\n * Kept deliberately small so #378 never has to reach into orchestrator.ts /\n * importance.ts — the two files Wave 1 agents are editing concurrently.\n */\n\nimport path from \"node:path\";\nimport { existsSync } from \"node:fs\";\n\nimport { log } from \"../logger.js\";\nimport { StorageManager } from \"../storage.js\";\nimport type { PluginConfig, MemoryFile } from \"../types.js\";\nimport {\n materializeForNamespace,\n type MaterializeResult,\n type RolloutSummaryInput,\n} from \"./codex-materialize.js\";\n\n/** Options accepted by the shared post-consolidation materialize helper. */\nexport interface PostConsolidationMaterializeOptions {\n config: PluginConfig;\n namespace?: string;\n memories?: MemoryFile[];\n memoryDir?: string;\n codexHome?: string;\n rolloutSummaries?: RolloutSummaryInput[];\n now?: Date;\n}\n\n/** Options accepted by the runner. */\nexport interface RunMaterializeOptions {\n /** Remnic config — we only read the `codexMaterialize*` fields. */\n config: PluginConfig;\n /** Namespace to materialize. Overrides the config's `codexMaterializeNamespace`. */\n namespace?: string;\n /** Override the memory directory (defaults to `config.memoryDir`). */\n memoryDir?: string;\n /** Override `<codex_home>` (useful for tests). */\n codexHome?: string;\n /** Optional pre-loaded memories (bypasses disk read — used in tests). */\n memories?: MemoryFile[];\n /** Optional rollout summaries supplied by the caller. */\n rolloutSummaries?: RolloutSummaryInput[];\n /** Current time injection for deterministic runs. */\n now?: Date;\n /** Reason string — logged for observability. */\n reason?: \"consolidation\" | \"session_end\" | \"manual\" | \"cli\";\n}\n\n/**\n * Run the Codex materialization end-to-end. Returns `null` when the feature\n * is disabled in config or when the user hasn't opted in via the sentinel.\n * Never throws for \"expected\" skips; only throws on schema validation or I/O\n * errors that callers actually need to surface.\n */\nexport async function runCodexMaterialize(\n options: RunMaterializeOptions,\n): Promise<MaterializeResult | null> {\n const cfg = options.config;\n if (!cfg.codexMaterializeMemories) {\n log.debug(`[codex-materialize] skipped — codexMaterializeMemories=false`);\n return null;\n }\n\n // Per-trigger gate: session-end runs must honor codexMaterializeOnSessionEnd.\n // session-end.sh passes reason=\"session_end\"; when the user has turned off the\n // session-end trigger we short-circuit here without touching disk.\n if (options.reason === \"session_end\" && cfg.codexMaterializeOnSessionEnd === false) {\n log.debug(\n `[codex-materialize] skipped — session-end disabled via codexMaterializeOnSessionEnd=false`,\n );\n return null;\n }\n\n const namespace = resolveNamespace(options.namespace, cfg);\n const memoryDir = options.memoryDir ?? cfg.memoryDir;\n if (!memoryDir) {\n log.warn(`[codex-materialize] skipped — no memoryDir available`);\n return null;\n }\n\n let memories: MemoryFile[];\n if (options.memories) {\n memories = options.memories;\n } else {\n const nsDir = resolveNamespaceDir(memoryDir, namespace, cfg);\n const storage = new StorageManager(nsDir);\n try {\n memories = await storage.readAllMemories();\n } catch (error) {\n log.warn(\n `[codex-materialize] skipped — failed to read memories from ${nsDir}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n return null;\n }\n }\n\n // Intentionally NOT catching here: per the JSDoc contract above,\n // schema-validation and I/O errors from `materializeForNamespace` must\n // surface to callers so they can exit non-zero (CLI) or log + recover\n // (consolidation post-hook, which has its own narrower try/catch).\n // Catching everything would make a broken MEMORY.md render look like a\n // successful skip, and invisible failures are strictly worse than loud\n // ones for a feature that writes to `~/.codex/memories`.\n const result = materializeForNamespace(namespace, {\n memories,\n codexHome: options.codexHome,\n maxSummaryTokens: cfg.codexMaterializeMaxSummaryTokens,\n rolloutRetentionDays: cfg.codexMaterializeRolloutRetentionDays,\n rolloutSummaries: options.rolloutSummaries,\n now: options.now,\n });\n if (options.reason) {\n log.debug(\n `[codex-materialize] ran reason=${options.reason} wrote=${result.wrote} files=${result.filesWritten.length}`,\n );\n }\n return result;\n}\n\n/**\n * Shared helper for post-consolidation materialize hooks.\n *\n * `materializeAfterSemanticConsolidation` and `materializeAfterCausalConsolidation`\n * used to be two nearly-identical copies of this logic; keeping the actual\n * body here means any future guard/logging change happens in one place.\n *\n * The only per-caller knob is `logPrefix`, which is used to tag the\n * non-fatal warning emitted when the materializer throws.\n */\nexport async function runPostConsolidationMaterialize(\n logPrefix: string,\n options: PostConsolidationMaterializeOptions,\n): Promise<MaterializeResult | null> {\n if (!options.config.codexMaterializeMemories) return null;\n if (!options.config.codexMaterializeOnConsolidation) return null;\n try {\n return await runCodexMaterialize({\n config: options.config,\n namespace: options.namespace,\n memories: options.memories,\n memoryDir: options.memoryDir,\n codexHome: options.codexHome,\n rolloutSummaries: options.rolloutSummaries,\n now: options.now,\n reason: \"consolidation\",\n });\n } catch (error) {\n log.warn(\n `${logPrefix} Codex materialize post-hook failed (non-fatal): ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n return null;\n }\n}\n\nfunction resolveNamespace(override: string | undefined, cfg: PluginConfig): string {\n const requested = (override ?? cfg.codexMaterializeNamespace ?? \"auto\").trim();\n if (requested.length === 0 || requested === \"auto\") {\n // When the caller asks for \"auto\", fall back to the configured default\n // namespace (if any) so storage-root resolution lines up with what\n // NamespaceStorageRouter would do for the same install.\n return cfg.defaultNamespace && cfg.defaultNamespace.length > 0\n ? cfg.defaultNamespace\n : \"default\";\n }\n return requested;\n}\n\n/**\n * Resolve the on-disk storage root for a namespace, matching\n * `NamespaceStorageRouter` in `packages/remnic-core/src/namespaces/storage.ts`.\n *\n * Contract:\n * - When namespaces are disabled, every namespace maps to `memoryDir` itself.\n * - When namespaces are enabled, non-default namespaces always live under\n * `memoryDir/namespaces/<namespace>`.\n * - The default namespace prefers `memoryDir/namespaces/<defaultNamespace>`\n * when that directory already exists (migrated install); otherwise it\n * falls back to the legacy `memoryDir` root so materialization does not\n * silently switch directories out from under an existing install.\n */\nfunction resolveNamespaceDir(\n memoryDir: string,\n namespace: string,\n cfg: PluginConfig,\n): string {\n if (!cfg.namespacesEnabled) return memoryDir;\n\n const ns = namespace || cfg.defaultNamespace || \"default\";\n const namespacedRoot = path.join(memoryDir, \"namespaces\", ns);\n\n if (ns === cfg.defaultNamespace) {\n return existsSync(namespacedRoot) ? namespacedRoot : memoryDir;\n }\n return namespacedRoot;\n}\n","/**\n * codex-materialize.ts — Codex CLI native memory artifact materialization (#378)\n *\n * Periodically writes Remnic memories into the file layout that Codex CLI's\n * phase-2 consolidation reads directly under `<codex_home>/memories/`:\n *\n * memory_summary.md — always-loaded at session start (tight budget)\n * MEMORY.md — searchable handbook (task-group schema)\n * raw_memories.md — mechanical merge of raw memories, latest first\n * rollout_summaries/<slug>.md — per-session recaps\n *\n * Codex's own read path is agnostic to which producer wrote these files — it\n * tags reads by `memory_md` / `memory_summary` / `raw_memories` /\n * `rollout_summaries` / `skills`. By materializing Remnic content into this\n * exact layout we let Codex pick up Remnic memories without a single MCP call.\n *\n * Safety invariants\n * ─────────────────\n * - **Atomic writes.** Every file is rendered under `.remnic-tmp/` and then\n * `rename()`d into place so Codex never observes a half-written file.\n * - **Sentinel-based opt-in.** If `<codex_home>/memories/.remnic-managed` is\n * missing, we SKIP materialization entirely and log a warning. This honors\n * user hand-edits to the directory — a user who manually curated their\n * Codex memory layout will never have those edits overwritten.\n * - **Schema validation.** `MEMORY.md` content is validated against the\n * task-group schema before write. Invalid content throws and nothing is\n * written.\n * - **Idempotent no-ops.** A content hash is written into the sentinel. If\n * the re-rendered hash matches the previous run, we skip writes entirely.\n * - **Token budget.** `memory_summary.md` is truncated to fit under the\n * configured token budget (whitespace-tokenized approximation), leaving\n * headroom under Codex's 5000-token summary cap.\n *\n * Privacy\n * ───────\n * This module does not persist any user content outside `<codex_home>/memories`\n * — it only mirrors the memories that Remnic already wrote. It does not log\n * memory content to stdout; it logs file names, counts, and hashes.\n */\n\nimport {\n createHash,\n} from \"node:crypto\";\nimport {\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n renameSync,\n rmSync,\n statSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport path from \"node:path\";\n\nimport { log } from \"../logger.js\";\nimport type { MemoryFile } from \"../types.js\";\n\n// ─── Types ─────────────────────────────────────────────────────────────────\n\n/**\n * Input for {@link materializeForNamespace}. Prefer passing pre-loaded\n * `memories` so this module stays I/O-agnostic and trivially testable.\n */\nexport interface MaterializeOptions {\n /** Pre-loaded Remnic memories for this namespace (required). */\n memories: MemoryFile[];\n /** Override `<codex_home>`. Defaults to `$CODEX_HOME` or `~/.codex`. */\n codexHome?: string;\n /** Maximum whitespace-tokenized size of memory_summary.md. Default 4500. */\n maxSummaryTokens?: number;\n /** Maximum age of rollout_summaries/*.md in days. Default 30. */\n rolloutRetentionDays?: number;\n /** Per-session rollout summaries to render. */\n rolloutSummaries?: RolloutSummaryInput[];\n /** Current time, injected for deterministic tests. */\n now?: Date;\n /** Optional logger override for tests. */\n logger?: { info: (msg: string) => void; warn: (msg: string) => void; debug?: (msg: string) => void };\n}\n\n/** Input describing one Codex rollout summary file. */\nexport interface RolloutSummaryInput {\n /** Stable slug for the file (becomes `<slug>.md`). */\n slug: string;\n /** Working directory used during the rollout. */\n cwd?: string;\n /** Path to the raw Codex rollout log, if known. */\n rolloutPath?: string;\n /** ISO-8601 timestamp of the last update. */\n updatedAt?: string;\n /** Opaque thread / session id. */\n threadId?: string;\n /** Markdown body for the recap. */\n body: string;\n /** Freeform keywords / search hints. */\n keywords?: string[];\n}\n\n/** Result of a materialization run. */\nexport interface MaterializeResult {\n /** Namespace that was materialized. */\n namespace: string;\n /** `<codex_home>/memories` path this run targeted. */\n memoriesDir: string;\n /** Was anything actually written (vs. skipped / idempotent no-op)? */\n wrote: boolean;\n /** True if the sentinel was missing and we skipped with a warning. */\n skippedNoSentinel: boolean;\n /** True if the hash matched the previous run and we short-circuited. */\n skippedIdempotent: boolean;\n /** Files that were written this run (relative to `memoriesDir`). */\n filesWritten: string[];\n /** Content hash computed for this run. */\n contentHash: string;\n}\n\n/** On-disk shape of the `.remnic-managed` sentinel. */\ninterface SentinelFile {\n version: number;\n namespace: string;\n updated_at: string;\n content_hash: string;\n}\n\n// ─── Constants ─────────────────────────────────────────────────────────────\n\n/** Bump when the on-disk layout or semantics change. */\nexport const MATERIALIZE_VERSION = 1;\n\n/** Sentinel file name at the root of the materialized memories dir. */\nexport const SENTINEL_FILE = \".remnic-managed\";\n\n/** Scratch directory used for atomic renames. */\nexport const TMP_DIR = \".remnic-tmp\";\n\n/** File names we own. Anything else in the directory is considered user-managed. */\nconst OWNED_FILES = new Set<string>([\n \"memory_summary.md\",\n \"MEMORY.md\",\n \"raw_memories.md\",\n]);\n\n/** Sub-directory for per-session rollout recaps. */\nconst ROLLOUT_SUBDIR = \"rollout_summaries\";\n\n// ─── Public entry points ───────────────────────────────────────────────────\n\n/**\n * Materialize a Remnic namespace into Codex's native memory layout.\n *\n * Returns a {@link MaterializeResult} describing what happened. Callers\n * should treat \"skipped\" as success — the sentinel / idempotent cases are\n * expected and intentional.\n *\n * @throws if `MEMORY.md` fails schema validation (we do not write garbage).\n */\nexport function materializeForNamespace(\n namespace: string,\n options: MaterializeOptions,\n): MaterializeResult {\n const logger = options.logger ?? {\n info: (msg) => log.info(`[codex-materialize] ${msg}`),\n warn: (msg) => log.warn(`[codex-materialize] ${msg}`),\n debug: (msg) => log.debug(`[codex-materialize] ${msg}`),\n };\n const codexHome = resolveCodexHome(options.codexHome);\n const memoriesDir = path.join(codexHome, \"memories\");\n const now = options.now ?? new Date();\n // Honor `0` as \"no summary budget\" — parseConfig already clamps to non-\n // negative integers, so any provided number is meaningful. Only fall back\n // to the default when the caller did not provide the option at all.\n const maxSummaryTokens =\n typeof options.maxSummaryTokens === \"number\" && options.maxSummaryTokens >= 0\n ? options.maxSummaryTokens\n : 4500;\n const rolloutRetentionDays =\n typeof options.rolloutRetentionDays === \"number\" && options.rolloutRetentionDays >= 0\n ? options.rolloutRetentionDays\n : 30;\n\n // ── Sentinel check ─────────────────────────────────────────────────────\n // We deliberately do NOT `mkdirSync(memoriesDir)` before reading the\n // sentinel: creating `~/.codex/memories/` for every user (including ones\n // who never use Codex) would make Remnic's post-consolidation hook leave\n // empty opt-in directories behind on disk. Instead we only check whether\n // the sentinel already exists — if the parent dir doesn't exist, the\n // sentinel can't exist either and we fall straight through to the skip\n // path without touching the filesystem. The `mkdirSync` for `memoriesDir`\n // happens later, only once we know we're actually going to write.\n const sentinelPath = path.join(memoriesDir, SENTINEL_FILE);\n const existingSentinel = readSentinel(sentinelPath);\n if (!existingSentinel) {\n // Log at `debug` when the entire memories dir doesn't exist — that's\n // the common \"user never opted in\" case and should not be noisy.\n // Keep the `warn` level only when the dir exists but lacks a sentinel,\n // which is the \"user hand-curated layout, don't overwrite\" case that\n // genuinely warrants attention.\n if (existsSync(memoriesDir)) {\n logger.warn(\n `sentinel ${SENTINEL_FILE} missing in ${memoriesDir}; skipping materialization to preserve hand-edits`,\n );\n } else {\n logger.debug?.(\n `skipping materialization — ${memoriesDir} does not exist (user not opted in)`,\n );\n }\n return {\n namespace,\n memoriesDir,\n wrote: false,\n skippedNoSentinel: true,\n skippedIdempotent: false,\n filesWritten: [],\n contentHash: \"\",\n };\n }\n\n // Now that we know the user has opted in (sentinel exists), it's safe to\n // ensure the memories dir is present. In practice this is almost always a\n // no-op because the sentinel read above already succeeded, but a defensive\n // mkdirSync protects against a race where the dir was removed between the\n // sentinel read and the first write.\n mkdirSync(memoriesDir, { recursive: true });\n\n // ── Render ─────────────────────────────────────────────────────────────\n const memories = [...options.memories];\n // Track whether the caller actually supplied a rollout set. `undefined`\n // means \"don't touch rollout_summaries/\"; an empty array is still\n // authoritative and means \"we own this dir and it should be empty\".\n const rolloutsSupplied = options.rolloutSummaries !== undefined;\n const rolloutSummaries = options.rolloutSummaries ?? [];\n\n // Prune-before-render: MEMORY.md and memory_summary.md both embed rollout\n // filenames in their body, so they must only ever see the *retained* set.\n // Running pruneRollouts after the renderers (as an earlier revision did)\n // caused MEMORY.md to list `rollout_summaries/<slug>.md` paths for rollouts\n // that were then pruned and never written — a broken link pointing at a\n // ghost file. See review feedback on PR #392.\n const retainedRollouts = pruneRollouts(rolloutSummaries, rolloutRetentionDays, now);\n\n // Deduplicate on sanitized filename. Two different slugs (\"Session 1\" and\n // \"session!!!1\") can sanitize to the same output (\"session-1\"), which would\n // otherwise make the first entry's tmp file get overwritten and cause the\n // later rename step to crash with ENOENT. For each collision slot we keep\n // the entry with the newest `updatedAt` so an unsorted input (or a caller\n // that accidentally appends older recaps after newer ones) can't have an\n // older recap clobber a newer one.\n // We do this at the retained-input level (not just at the written-file\n // level) so MEMORY.md's \"rollout_summary_files\" section lists each slot\n // exactly once and matches what actually gets written to disk.\n const dedupedRollouts: RolloutSummaryInput[] = [];\n const seenNames = new Map<string, number>();\n const parseTs = (value: string | undefined): number => {\n if (!value) return Number.NEGATIVE_INFINITY;\n const parsed = Date.parse(value);\n return Number.isFinite(parsed) ? parsed : Number.NEGATIVE_INFINITY;\n };\n for (const r of retainedRollouts) {\n const name = `${sanitizeSlug(r.slug)}.md`;\n const existingIdx = seenNames.get(name);\n if (existingIdx === undefined) {\n seenNames.set(name, dedupedRollouts.length);\n dedupedRollouts.push(r);\n continue;\n }\n // Newest-wins: only replace the existing entry if the incoming one has a\n // strictly newer timestamp. Ties keep the earlier entry (stable for\n // unsorted inputs) because overwriting on ties would flip rendering output\n // for benign call-order changes.\n const existing = dedupedRollouts[existingIdx];\n if (parseTs(r.updatedAt) > parseTs(existing.updatedAt)) {\n dedupedRollouts[existingIdx] = r;\n }\n }\n\n const memorySummary = renderMemorySummary({\n namespace,\n memories,\n rolloutSummaries: dedupedRollouts,\n maxTokens: maxSummaryTokens,\n });\n\n const memoryMd = renderMemoryMd({\n namespace,\n memories,\n rolloutSummaries: dedupedRollouts,\n });\n\n // Fail fast on schema issues — do not write garbage.\n const validation = validateMemoryMd(memoryMd);\n if (!validation.valid) {\n const reason = validation.errors.join(\"; \");\n logger.warn(`MEMORY.md failed schema validation: ${reason}`);\n throw new Error(`codex-materialize: MEMORY.md schema validation failed: ${reason}`);\n }\n\n const rawMemories = renderRawMemories({ memories });\n\n const rolloutFiles = dedupedRollouts.map((r) => ({\n name: `${sanitizeSlug(r.slug)}.md`,\n body: renderRolloutSummary(r),\n }));\n\n // ── Idempotence check ──────────────────────────────────────────────────\n const hash = computeContentHash({\n namespace,\n memorySummary,\n memoryMd,\n rawMemories,\n rolloutFiles,\n });\n\n if (existingSentinel.content_hash === hash) {\n // Idempotence early-return is only safe when the managed files we would\n // have written are still on disk. If a user or external process deleted\n // `MEMORY.md` / `memory_summary.md` / `raw_memories.md` (or any retained\n // rollout file) while the sentinel's hash stayed the same, we must fall\n // through and rewrite — otherwise Codex would be stuck with missing\n // artifacts until a memory-content change happens to flip the hash.\n const requiredFiles = [\n path.join(memoriesDir, \"memory_summary.md\"),\n path.join(memoriesDir, \"MEMORY.md\"),\n path.join(memoriesDir, \"raw_memories.md\"),\n ...rolloutFiles.map((r) => path.join(memoriesDir, ROLLOUT_SUBDIR, r.name)),\n ];\n const allPresent = requiredFiles.every((f) => existsSync(f));\n if (allPresent) {\n logger.debug?.(`no-op materialization for namespace=${namespace} (hash unchanged)`);\n return {\n namespace,\n memoriesDir,\n wrote: false,\n skippedNoSentinel: false,\n skippedIdempotent: true,\n filesWritten: [],\n contentHash: hash,\n };\n }\n logger.debug?.(\n `hash unchanged for namespace=${namespace} but managed file missing — forcing rewrite`,\n );\n }\n\n // ── Atomic writes ──────────────────────────────────────────────────────\n // Use a unique, per-run staging sub-directory so two overlapping runs\n // (e.g. a session-end trigger overlapping with a consolidation post-hook)\n // can't stomp each other's tmp files mid-rename. The old \"fixed TMP_DIR,\n // wipe-on-entry\" layout meant run B would delete run A's staging area out\n // from under it, causing ENOENT on A's rename loop. Per-run uniqueness\n // turns the shared dir into an insulated workspace. See review feedback on\n // PR #392.\n const runTag = `${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n const tmpDir = path.join(memoriesDir, `${TMP_DIR}-${runTag}`);\n // Opportunistic GC for stale scratch dirs left behind by a previous\n // crashed run. We only remove entries whose mtime is older than the\n // stale-threshold below — that way we never delete another in-flight\n // run's staging area out from under it. The threshold is deliberately\n // generous (1h) because a healthy materialize completes in milliseconds\n // and there's no legitimate reason for a live staging dir to be older.\n //\n // NB: we compare against `Date.now()` (wall-clock), not against the\n // injected `options.now`. Tests and deterministic replays commonly\n // inject a non-current timestamp, but file mtimes on disk are always\n // wall-clock, so mixing the two would either false-positive delete\n // fresh dirs (test-time in the past) or false-negative skip stale ones\n // (test-time in the future).\n const TMP_STALE_MS = 60 * 60 * 1000;\n const wallClockMs = Date.now();\n try {\n for (const entry of readdirSync(memoriesDir, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue;\n if (!entry.name.startsWith(TMP_DIR)) continue;\n const stalePath = path.join(memoriesDir, entry.name);\n try {\n const stat = statSync(stalePath);\n if (wallClockMs - stat.mtimeMs < TMP_STALE_MS) continue;\n rmSync(stalePath, { recursive: true, force: true });\n } catch {\n // ignore — another concurrent run may own it, or we lack perms\n }\n }\n } catch {\n // ignore — dir may not exist yet\n }\n mkdirSync(tmpDir, { recursive: true });\n mkdirSync(path.join(tmpDir, ROLLOUT_SUBDIR), { recursive: true });\n\n const filesWritten: string[] = [];\n\n writeFileSync(path.join(tmpDir, \"memory_summary.md\"), memorySummary);\n filesWritten.push(\"memory_summary.md\");\n\n writeFileSync(path.join(tmpDir, \"MEMORY.md\"), memoryMd);\n filesWritten.push(\"MEMORY.md\");\n\n writeFileSync(path.join(tmpDir, \"raw_memories.md\"), rawMemories);\n filesWritten.push(\"raw_memories.md\");\n\n for (const rollout of rolloutFiles) {\n writeFileSync(path.join(tmpDir, ROLLOUT_SUBDIR, rollout.name), rollout.body);\n filesWritten.push(path.join(ROLLOUT_SUBDIR, rollout.name));\n }\n\n // Rename into place. Atomic per-file is sufficient — Codex reads each file\n // independently and tolerates an inconsistent in-between snapshot across\n // files for the duration of the rename loop (milliseconds).\n for (const rel of [\"memory_summary.md\", \"MEMORY.md\", \"raw_memories.md\"]) {\n const src = path.join(tmpDir, rel);\n const dest = path.join(memoriesDir, rel);\n renameSync(src, dest);\n }\n\n const destRolloutsDir = path.join(memoriesDir, ROLLOUT_SUBDIR);\n mkdirSync(destRolloutsDir, { recursive: true });\n // Only garbage-collect rollout files when the caller actually supplied a\n // `rolloutSummaries` array — otherwise we'd wipe legitimately\n // user/Codex-created recap files on every session-end run, since those\n // calls typically omit the rollout set entirely. When rollouts were\n // supplied (even as an empty array — meaning \"we own this dir and it\n // should be empty\"), we clear the stale files we previously owned.\n if (rolloutsSupplied) {\n const retainedRolloutNames = new Set(rolloutFiles.map((r) => r.name));\n try {\n for (const entry of readdirSync(destRolloutsDir, { withFileTypes: true })) {\n if (!entry.isFile()) continue;\n if (!entry.name.endsWith(\".md\")) continue;\n if (retainedRolloutNames.has(entry.name)) continue;\n try {\n unlinkSync(path.join(destRolloutsDir, entry.name));\n } catch {\n // ignore\n }\n }\n } catch {\n // ignore — directory may not exist yet\n }\n }\n\n for (const rollout of rolloutFiles) {\n const src = path.join(tmpDir, ROLLOUT_SUBDIR, rollout.name);\n const dest = path.join(destRolloutsDir, rollout.name);\n renameSync(src, dest);\n }\n\n // Update sentinel last so a crash leaves hash mismatched → next run rewrites.\n const sentinel: SentinelFile = {\n version: MATERIALIZE_VERSION,\n namespace,\n updated_at: now.toISOString(),\n content_hash: hash,\n };\n writeFileSync(sentinelPath, `${JSON.stringify(sentinel, null, 2)}\\n`);\n\n try {\n rmSync(tmpDir, { recursive: true, force: true });\n } catch {\n // ignore\n }\n\n logger.info(\n `materialized namespace=${namespace} files=${filesWritten.length} hash=${hash.slice(0, 12)}`,\n );\n\n return {\n namespace,\n memoriesDir,\n wrote: true,\n skippedNoSentinel: false,\n skippedIdempotent: false,\n filesWritten,\n contentHash: hash,\n };\n}\n\n/**\n * Create (or refresh) the `.remnic-managed` sentinel. Callers must do this\n * explicitly the first time they want Remnic to start managing a directory —\n * we never write it implicitly, because its presence is the user's opt-in.\n */\nexport function ensureSentinel(memoriesDir: string, namespace: string, now: Date = new Date()): void {\n mkdirSync(memoriesDir, { recursive: true });\n const sentinelPath = path.join(memoriesDir, SENTINEL_FILE);\n if (existsSync(sentinelPath)) return;\n const sentinel: SentinelFile = {\n version: MATERIALIZE_VERSION,\n namespace,\n updated_at: now.toISOString(),\n content_hash: \"\",\n };\n writeFileSync(sentinelPath, `${JSON.stringify(sentinel, null, 2)}\\n`);\n}\n\n// ─── Rendering ─────────────────────────────────────────────────────────────\n\ninterface RenderContext {\n namespace: string;\n memories: MemoryFile[];\n rolloutSummaries: RolloutSummaryInput[];\n // Historically this interface exposed a `now: Date` field, but neither\n // `renderMemoryMd` nor `renderMemorySummary` ever read it (rendered output\n // is purely a function of `namespace`, `memories`, and `rolloutSummaries`).\n // The field was flagged as dead weight in PR #392 review and removed.\n // If a future renderer needs a timestamp, re-add it here and update both\n // call sites and the schema test.\n}\n\ninterface SummaryRenderContext extends RenderContext {\n maxTokens: number;\n}\n\n/**\n * Render `memory_summary.md` — the always-loaded file.\n * Budget-capped at `maxTokens` whitespace tokens.\n */\nexport function renderMemorySummary(ctx: SummaryRenderContext): string {\n const lines: string[] = [];\n lines.push(\"# Memory Summary\");\n lines.push(\"\");\n lines.push(`_namespace: ${ctx.namespace}_`);\n lines.push(`_source: remnic_`);\n lines.push(\"\");\n\n const highValue = selectSummaryMemories(ctx.memories, 12);\n if (highValue.length > 0) {\n lines.push(\"## Top memories\");\n lines.push(\"\");\n for (const mem of highValue) {\n lines.push(`- ${oneLineSummary(mem)}`);\n }\n lines.push(\"\");\n }\n\n if (ctx.rolloutSummaries.length > 0) {\n lines.push(\"## Recent rollouts\");\n lines.push(\"\");\n const sorted = [...ctx.rolloutSummaries]\n .sort((a, b) => (b.updatedAt ?? \"\").localeCompare(a.updatedAt ?? \"\"))\n .slice(0, 5);\n for (const r of sorted) {\n const when = r.updatedAt ? ` (${r.updatedAt})` : \"\";\n lines.push(`- ${r.slug}${when}`);\n }\n lines.push(\"\");\n }\n\n const full = lines.join(\"\\n\").replace(/\\n+$/u, \"\\n\");\n return truncateToTokenBudget(full, ctx.maxTokens);\n}\n\n/**\n * Render `MEMORY.md` — the searchable handbook in Codex's task-group schema.\n */\nexport function renderMemoryMd(ctx: RenderContext): string {\n const lines: string[] = [];\n lines.push(`# Task Group: ${ctx.namespace}`);\n lines.push(`scope: ${ctx.namespace}`);\n lines.push(`applies_to: cwd=*; reuse_rule=namespace-match`);\n lines.push(\"\");\n\n // One \"task\" per top-level topic cluster. For the first cut we group by\n // memory category so the schema validator always sees at least one task.\n const byCategory = groupMemoriesByCategory(ctx.memories);\n let taskIndex = 1;\n if (byCategory.size === 0) {\n lines.push(`## Task ${taskIndex}: baseline — no memories yet`);\n lines.push(\"\");\n lines.push(\"### rollout_summary_files\");\n for (const r of ctx.rolloutSummaries) {\n lines.push(\n `- rollout_summaries/${sanitizeSlug(r.slug)}.md (cwd=${r.cwd ?? \"*\"}, rollout_path=${r.rolloutPath ?? \"\"}, updated_at=${r.updatedAt ?? \"\"}, thread_id=${r.threadId ?? \"\"})`,\n );\n }\n if (ctx.rolloutSummaries.length === 0) {\n lines.push(\"- (none)\");\n }\n lines.push(\"\");\n lines.push(\"### keywords\");\n lines.push(`- ${ctx.namespace}`);\n lines.push(\"\");\n taskIndex += 1;\n } else {\n for (const [category, mems] of byCategory) {\n lines.push(`## Task ${taskIndex}: ${category} memories, outcome=surface-to-codex`);\n lines.push(\"\");\n lines.push(\"### rollout_summary_files\");\n const relevantRollouts = ctx.rolloutSummaries.slice(0, 5);\n if (relevantRollouts.length === 0) {\n lines.push(\"- (none)\");\n } else {\n for (const r of relevantRollouts) {\n lines.push(\n `- rollout_summaries/${sanitizeSlug(r.slug)}.md (cwd=${r.cwd ?? \"*\"}, rollout_path=${r.rolloutPath ?? \"\"}, updated_at=${r.updatedAt ?? \"\"}, thread_id=${r.threadId ?? \"\"})`,\n );\n }\n }\n lines.push(\"\");\n lines.push(\"### keywords\");\n const keywords = collectKeywords(mems, category, ctx.namespace);\n lines.push(`- ${keywords.join(\", \")}`);\n lines.push(\"\");\n taskIndex += 1;\n }\n }\n\n lines.push(\"## User preferences\");\n const prefs = pickCategory(ctx.memories, [\"preference\"]);\n if (prefs.length === 0) {\n lines.push(\"- (none recorded)\");\n } else {\n for (const pref of prefs.slice(0, 20)) {\n lines.push(`- ${oneLineSummary(pref)}`);\n }\n }\n lines.push(\"\");\n\n lines.push(\"## Reusable knowledge\");\n const knowledge = pickCategory(ctx.memories, [\"fact\", \"decision\", \"principle\", \"rule\", \"skill\"]);\n if (knowledge.length === 0) {\n lines.push(\"- (none recorded)\");\n } else {\n for (const mem of knowledge.slice(0, 30)) {\n lines.push(`- ${oneLineSummary(mem)}`);\n }\n }\n lines.push(\"\");\n\n lines.push(\"## Failures and how to do differently\");\n const corrections = pickCategory(ctx.memories, [\"correction\"]);\n if (corrections.length === 0) {\n lines.push(\"- (none recorded)\");\n } else {\n for (const mem of corrections.slice(0, 20)) {\n lines.push(`- ${oneLineSummary(mem)}`);\n }\n }\n lines.push(\"\");\n\n return lines.join(\"\\n\");\n}\n\n/** Render `raw_memories.md` — mechanical dump, latest first. */\nexport function renderRawMemories(ctx: { memories: MemoryFile[] }): string {\n const sorted = [...ctx.memories].sort((a, b) => {\n const aUpdated = a.frontmatter.updated ?? a.frontmatter.created ?? \"\";\n const bUpdated = b.frontmatter.updated ?? b.frontmatter.created ?? \"\";\n return bUpdated.localeCompare(aUpdated);\n });\n\n const lines: string[] = [\"# Raw Memories\", \"\", \"_source: remnic — latest first_\", \"\"];\n for (const mem of sorted) {\n const fm = mem.frontmatter;\n const id = fm.id ?? \"unknown\";\n const category = fm.category ?? \"unknown\";\n const updated = fm.updated ?? fm.created ?? \"\";\n lines.push(`## ${id} (${category}, updated=${updated})`);\n lines.push(\"\");\n lines.push(mem.content.trim());\n lines.push(\"\");\n }\n return lines.join(\"\\n\");\n}\n\n/** Render a single rollout summary file. */\nexport function renderRolloutSummary(input: RolloutSummaryInput): string {\n const lines: string[] = [];\n lines.push(`# Rollout Summary: ${input.slug}`);\n lines.push(\"\");\n const meta: string[] = [];\n if (input.cwd) meta.push(`cwd=${input.cwd}`);\n if (input.rolloutPath) meta.push(`rollout_path=${input.rolloutPath}`);\n if (input.updatedAt) meta.push(`updated_at=${input.updatedAt}`);\n if (input.threadId) meta.push(`thread_id=${input.threadId}`);\n if (meta.length > 0) {\n lines.push(`_${meta.join(\"; \")}_`);\n lines.push(\"\");\n }\n if (input.keywords && input.keywords.length > 0) {\n lines.push(`**keywords:** ${input.keywords.join(\", \")}`);\n lines.push(\"\");\n }\n lines.push(input.body.trim());\n lines.push(\"\");\n return lines.join(\"\\n\");\n}\n\n// ─── Schema validation ─────────────────────────────────────────────────────\n\nexport interface MemoryMdValidation {\n valid: boolean;\n errors: string[];\n}\n\n/**\n * Validate that a rendered `MEMORY.md` matches Codex's task-group schema.\n * We enforce the minimum set of structural requirements called out in #378:\n *\n * - one `# Task Group:` header\n * - `scope:` and `applies_to:` lines directly beneath it\n * - at least one `## Task N:` section\n * - each task section has `### rollout_summary_files` and `### keywords`\n * - `## User preferences`, `## Reusable knowledge`,\n * `## Failures and how to do differently` sections all present\n */\nexport function validateMemoryMd(content: string): MemoryMdValidation {\n const errors: string[] = [];\n const lines = content.split(/\\r?\\n/u);\n\n const taskGroupIndex = lines.findIndex((l) => /^#\\s+Task Group:\\s+\\S+/u.test(l));\n if (taskGroupIndex === -1) {\n errors.push(\"missing `# Task Group:` header\");\n } else {\n const tail = lines.slice(taskGroupIndex + 1, taskGroupIndex + 5);\n if (!tail.some((l) => /^scope:\\s*\\S+/u.test(l))) {\n errors.push(\"missing `scope:` line under Task Group header\");\n }\n if (!tail.some((l) => /^applies_to:\\s*\\S+/u.test(l))) {\n errors.push(\"missing `applies_to:` line under Task Group header\");\n }\n }\n\n const taskHeaders = lines.filter((l) => /^##\\s+Task\\s+\\d+:/u.test(l));\n if (taskHeaders.length === 0) {\n errors.push(\"at least one `## Task N:` section is required\");\n }\n\n // For every task section, make sure we have rollout_summary_files + keywords\n // before the next `##` header at the same level.\n const sectionRegex = /^##\\s+/u;\n for (let i = 0; i < lines.length; i++) {\n if (!/^##\\s+Task\\s+\\d+:/u.test(lines[i])) continue;\n let hasRollout = false;\n let hasKeywords = false;\n for (let j = i + 1; j < lines.length; j++) {\n if (sectionRegex.test(lines[j])) break;\n if (/^###\\s+rollout_summary_files\\s*$/u.test(lines[j])) hasRollout = true;\n if (/^###\\s+keywords\\s*$/u.test(lines[j])) hasKeywords = true;\n }\n if (!hasRollout) errors.push(`task block at line ${i + 1} missing \\`### rollout_summary_files\\``);\n if (!hasKeywords) errors.push(`task block at line ${i + 1} missing \\`### keywords\\``);\n }\n\n const requiredSections = [\n /^##\\s+User preferences\\s*$/u,\n /^##\\s+Reusable knowledge\\s*$/u,\n /^##\\s+Failures and how to do differently\\s*$/u,\n ];\n for (const re of requiredSections) {\n if (!lines.some((l) => re.test(l))) {\n errors.push(`missing required section: ${re.source}`);\n }\n }\n\n return { valid: errors.length === 0, errors };\n}\n\n// ─── Helpers ───────────────────────────────────────────────────────────────\n\nfunction resolveCodexHome(override?: string): string {\n if (override && override.trim().length > 0) return override;\n const fromEnv = process.env.CODEX_HOME;\n if (fromEnv && fromEnv.trim().length > 0) return fromEnv;\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"\";\n return path.join(home, \".codex\");\n}\n\nfunction readSentinel(sentinelPath: string): SentinelFile | null {\n if (!existsSync(sentinelPath)) return null;\n try {\n const raw = readFileSync(sentinelPath, \"utf-8\");\n const parsed = JSON.parse(raw) as Partial<SentinelFile>;\n if (typeof parsed !== \"object\" || parsed === null) return null;\n return {\n version: typeof parsed.version === \"number\" ? parsed.version : MATERIALIZE_VERSION,\n namespace: typeof parsed.namespace === \"string\" ? parsed.namespace : \"\",\n updated_at: typeof parsed.updated_at === \"string\" ? parsed.updated_at : \"\",\n content_hash: typeof parsed.content_hash === \"string\" ? parsed.content_hash : \"\",\n };\n } catch {\n return null;\n }\n}\n\nfunction selectSummaryMemories(memories: MemoryFile[], limit: number): MemoryFile[] {\n const scored = memories\n .filter((m) => !m.frontmatter.status || m.frontmatter.status === \"active\")\n .map((m) => {\n const confidence = typeof m.frontmatter.confidence === \"number\" ? m.frontmatter.confidence : 0;\n const importance =\n typeof m.frontmatter.importance === \"object\" &&\n m.frontmatter.importance !== null &&\n typeof (m.frontmatter.importance as { score?: number }).score === \"number\"\n ? ((m.frontmatter.importance as { score: number }).score ?? 0)\n : 0;\n const updated = m.frontmatter.updated ?? m.frontmatter.created ?? \"\";\n return { memory: m, score: importance * 2 + confidence, updated };\n });\n\n scored.sort((a, b) => {\n if (b.score !== a.score) return b.score - a.score;\n return b.updated.localeCompare(a.updated);\n });\n\n return scored.slice(0, limit).map((s) => s.memory);\n}\n\nfunction oneLineSummary(memory: MemoryFile): string {\n const raw = memory.content.replace(/\\s+/gu, \" \").trim();\n if (raw.length <= 160) return raw;\n return `${raw.slice(0, 157)}...`;\n}\n\nfunction groupMemoriesByCategory(memories: MemoryFile[]): Map<string, MemoryFile[]> {\n const map = new Map<string, MemoryFile[]>();\n for (const memory of memories) {\n if (memory.frontmatter.status && memory.frontmatter.status !== \"active\") continue;\n const category = memory.frontmatter.category ?? \"unknown\";\n const list = map.get(category) ?? [];\n list.push(memory);\n map.set(category, list);\n }\n return map;\n}\n\nfunction pickCategory(memories: MemoryFile[], categories: string[]): MemoryFile[] {\n const allowed = new Set(categories);\n return memories.filter(\n (m) =>\n (!m.frontmatter.status || m.frontmatter.status === \"active\") &&\n allowed.has(m.frontmatter.category ?? \"\"),\n );\n}\n\nfunction collectKeywords(memories: MemoryFile[], category: string, namespace: string): string[] {\n const keywords = new Set<string>();\n keywords.add(category);\n keywords.add(namespace);\n for (const mem of memories.slice(0, 10)) {\n for (const tag of mem.frontmatter.tags ?? []) {\n if (typeof tag === \"string\" && tag.trim().length > 0) keywords.add(tag.trim());\n }\n }\n return [...keywords].slice(0, 16);\n}\n\nfunction pruneRollouts(\n rollouts: RolloutSummaryInput[],\n retentionDays: number,\n now: Date,\n): RolloutSummaryInput[] {\n // Negative retention → \"infinite retention\" escape hatch. `parseConfig`\n // clamps the knob to >= 0, so in practice only callers passing a negative\n // value intentionally get the all-pass behavior.\n if (retentionDays < 0) return rollouts;\n // retentionDays === 0 → cutoff is exactly `now`, which prunes every\n // rollout whose `updatedAt` is in the past (i.e. all of them in practice).\n // This matches the documented semantics of \"retain for 0 days\".\n const cutoffMs = now.getTime() - retentionDays * 24 * 60 * 60 * 1000;\n return rollouts.filter((r) => {\n if (!r.updatedAt) return true;\n const t = Date.parse(r.updatedAt);\n if (!Number.isFinite(t)) return true;\n return t >= cutoffMs;\n });\n}\n\nfunction sanitizeSlug(slug: string): string {\n return slug\n .toLowerCase()\n .replace(/[^a-z0-9._-]+/gu, \"-\")\n .replace(/^-+|-+$/gu, \"\")\n .slice(0, 96)\n || \"rollout\";\n}\n\n/**\n * Whitespace-tokenized approximation used by the budget check. Matches the\n * simple heuristic Codex's usage.rs reporting uses for the \"5000 token\"\n * memory_summary cap.\n */\nexport function approximateTokenCount(text: string): number {\n const trimmed = text.trim();\n if (trimmed.length === 0) return 0;\n return trimmed.split(/\\s+/u).length;\n}\n\n/**\n * Truncate `text` so it fits under `maxTokens` whitespace tokens. We drop\n * trailing lines until we're under the budget and then append an ellipsis\n * marker so downstream readers can see that truncation happened.\n */\nexport function truncateToTokenBudget(text: string, maxTokens: number): string {\n if (maxTokens <= 0) return \"\";\n if (approximateTokenCount(text) <= maxTokens) return text;\n\n // Reserve headroom for the truncation marker so the line-preserving path\n // can actually fit the marker without flipping to the hard-cut fallback.\n // Both markers are counted with the same whitespace heuristic the budget\n // check uses, so the arithmetic stays consistent.\n const lineMarker = \"_[truncated for summary budget]_\";\n const tailMarker = \"[truncated]\";\n const lineMarkerTokens = approximateTokenCount(lineMarker);\n const tailMarkerTokens = approximateTokenCount(tailMarker);\n\n const lines = text.split(/\\r?\\n/u);\n const lineBudget = Math.max(0, maxTokens - lineMarkerTokens);\n while (lines.length > 0 && approximateTokenCount(lines.join(\"\\n\")) > lineBudget) {\n lines.pop();\n }\n lines.push(lineMarker);\n let result = lines.join(\"\\n\");\n\n // If a single huge line still blows the budget, hard-cut tokens. Reserve\n // space for the tail marker's own token count so the final string stays\n // within maxTokens rather than sneaking over by a few tokens.\n if (approximateTokenCount(result) > maxTokens) {\n const tokens = result.split(/\\s+/u);\n const keep = Math.max(0, maxTokens - tailMarkerTokens);\n result = keep > 0 ? `${tokens.slice(0, keep).join(\" \")} ${tailMarker}` : tailMarker;\n }\n return result;\n}\n\nfunction computeContentHash(input: {\n namespace: string;\n memorySummary: string;\n memoryMd: string;\n rawMemories: string;\n rolloutFiles: Array<{ name: string; body: string }>;\n}): string {\n const hash = createHash(\"sha256\");\n hash.update(`v${MATERIALIZE_VERSION}\\n`);\n hash.update(`namespace=${input.namespace}\\n`);\n hash.update(\"---memory_summary---\\n\");\n hash.update(input.memorySummary);\n hash.update(\"\\n---memory_md---\\n\");\n hash.update(input.memoryMd);\n hash.update(\"\\n---raw_memories---\\n\");\n hash.update(input.rawMemories);\n const sortedRollouts = [...input.rolloutFiles].sort((a, b) => a.name.localeCompare(b.name));\n for (const r of sortedRollouts) {\n hash.update(`\\n---rollout:${r.name}---\\n`);\n hash.update(r.body);\n }\n return hash.digest(\"hex\");\n}\n\n// ─── Stat helper for tests / debugging ─────────────────────────────────────\n\n/**\n * Return basic stats about a materialized memories dir. Useful for tests and\n * debug CLI output. Returns `null` if the dir does not exist.\n */\nexport function describeMemoriesDir(memoriesDir: string): {\n exists: boolean;\n hasSentinel: boolean;\n files: string[];\n sentinel: SentinelFile | null;\n} | null {\n if (!existsSync(memoriesDir)) return null;\n const sentinelPath = path.join(memoriesDir, SENTINEL_FILE);\n const sentinel = readSentinel(sentinelPath);\n const files: string[] = [];\n for (const entry of readdirSync(memoriesDir, { withFileTypes: true })) {\n if (entry.isFile() && OWNED_FILES.has(entry.name)) files.push(entry.name);\n }\n const rolloutsDir = path.join(memoriesDir, ROLLOUT_SUBDIR);\n if (existsSync(rolloutsDir)) {\n try {\n for (const entry of readdirSync(rolloutsDir, { withFileTypes: true })) {\n if (entry.isFile() && entry.name.endsWith(\".md\")) {\n files.push(path.join(ROLLOUT_SUBDIR, entry.name));\n }\n }\n } catch {\n // ignore\n }\n }\n return {\n exists: true,\n hasSentinel: sentinel !== null,\n files: files.sort(),\n sentinel,\n };\n}\n","/**\n * semantic-consolidation.ts — Semantic Consolidation Engine\n *\n * Finds clusters of semantically similar memories using token overlap,\n * synthesizes canonical versions via LLM, and archives the originals.\n * Reduces memory store bloat while preserving all unique information.\n */\n\nimport type { MemoryFile, PluginConfig } from \"./types.js\";\nimport { normalizeRecallTokens, countRecallTokenOverlap } from \"./recall-tokenization.js\";\nimport { runPostConsolidationMaterialize } from \"./connectors/codex-materialize-runner.js\";\nimport type { MaterializeResult, RolloutSummaryInput } from \"./connectors/codex-materialize.js\";\nimport { discoverMemoryExtensions, renderExtensionsBlock, resolveExtensionsRoot } from \"./memory-extension-host/index.js\";\nimport { log } from \"./logger.js\";\n\n// Re-export resolveExtensionsRoot for backward compatibility — existing\n// consumers that import from semantic-consolidation.ts continue to work.\nexport { resolveExtensionsRoot } from \"./memory-extension-host/index.js\";\n\nexport interface ConsolidationCluster {\n category: string;\n memories: MemoryFile[];\n overlapScore: number;\n canonicalContent?: string;\n}\n\nexport interface SemanticConsolidationResult {\n clustersFound: number;\n memoriesConsolidated: number;\n memoriesArchived: number;\n errors: number;\n clusters: ConsolidationCluster[];\n}\n\n/**\n * Find clusters of semantically similar memories using token overlap.\n */\nexport function findSimilarClusters(\n memories: MemoryFile[],\n config: {\n threshold: number;\n minClusterSize: number;\n excludeCategories: string[];\n maxPerRun: number;\n },\n): ConsolidationCluster[] {\n const excluded = new Set(config.excludeCategories);\n\n // Group by category first\n const byCategory = new Map<string, MemoryFile[]>();\n for (const m of memories) {\n const cat = m.frontmatter.category;\n if (excluded.has(cat)) continue;\n if (m.frontmatter.status && m.frontmatter.status !== \"active\") continue;\n const list = byCategory.get(cat) ?? [];\n list.push(m);\n byCategory.set(cat, list);\n }\n\n const clusters: ConsolidationCluster[] = [];\n let totalCandidates = 0;\n\n for (const [category, mems] of byCategory) {\n if (totalCandidates >= config.maxPerRun) break;\n\n // Token-normalize all memories in this category\n const tokenized = mems.map((m) => ({\n memory: m,\n tokens: new Set(normalizeRecallTokens(m.content, [])),\n }));\n\n // Track which memories are already clustered\n const clustered = new Set<string>();\n\n for (let i = 0; i < tokenized.length && totalCandidates < config.maxPerRun; i++) {\n if (clustered.has(tokenized[i].memory.frontmatter.id)) continue;\n\n const cluster: MemoryFile[] = [tokenized[i].memory];\n let totalOverlap = 0;\n let comparisons = 0;\n\n for (let j = i + 1; j < tokenized.length; j++) {\n if (clustered.has(tokenized[j].memory.frontmatter.id)) continue;\n\n const aTokens = tokenized[i].tokens;\n const bTokens = tokenized[j].tokens;\n if (aTokens.size === 0 || bTokens.size === 0) continue;\n\n // Bidirectional overlap: what fraction of tokens are shared\n const overlap = countRecallTokenOverlap(aTokens, [...bTokens].join(\" \"));\n const maxTokens = Math.max(aTokens.size, bTokens.size);\n const score = maxTokens > 0 ? overlap / maxTokens : 0;\n\n if (score >= config.threshold) {\n cluster.push(tokenized[j].memory);\n totalOverlap += score;\n comparisons++;\n // Enforce maxPerRun within a single cluster\n if (totalCandidates + cluster.length >= config.maxPerRun) break;\n }\n }\n\n if (cluster.length >= config.minClusterSize) {\n for (const m of cluster) clustered.add(m.frontmatter.id);\n clusters.push({\n category,\n memories: cluster,\n overlapScore: comparisons > 0 ? totalOverlap / comparisons : 0,\n });\n totalCandidates += cluster.length;\n }\n }\n }\n\n return clusters;\n}\n\n/**\n * Build the LLM prompt for synthesizing a canonical memory from a cluster.\n */\nexport function buildConsolidationPrompt(cluster: ConsolidationCluster): string {\n const memoryTexts = cluster.memories\n .map(\n (m, i) =>\n `Memory ${i + 1} (${m.frontmatter.id}, created ${m.frontmatter.created}):\\n${m.content}`,\n )\n .join(\"\\n\\n\");\n\n return `You are a memory consolidation system. The following ${cluster.memories.length} memories in the \"${cluster.category}\" category contain overlapping information.\n\nSynthesize them into ONE canonical memory that:\n1. Preserves ALL unique information from every source memory\n2. Removes redundancy and repetition\n3. Uses clear, concise language\n4. Maintains the same category and tone\n5. Does NOT add information that isn't in the sources\n\n${memoryTexts}\n\nWrite ONLY the consolidated memory content (no metadata, no explanation, no preamble):`;\n}\n\n/**\n * Parse the LLM response to extract the canonical content.\n */\nexport function parseConsolidationResponse(response: string): string {\n return response.trim();\n}\n\n/**\n * Discover extensions and build the block to append to a consolidation prompt.\n * Returns \"\" when extensions are disabled or none are found.\n */\nexport async function buildExtensionsBlockForConsolidation(\n config: PluginConfig,\n): Promise<string> {\n if (!config.memoryExtensionsEnabled) return \"\";\n const root = resolveExtensionsRoot(config);\n const extensions = await discoverMemoryExtensions(root, log);\n if (extensions.length === 0) return \"\";\n return renderExtensionsBlock(extensions);\n}\n\n/**\n * Optional post-consolidation hook — materializes the namespace into Codex's\n * native memory layout when the consolidation run finishes. Kept here (rather\n * than in orchestrator.ts) so #378 doesn't conflict with Wave 1 edits.\n *\n * Safe to call regardless of config state: honors `codexMaterializeMemories`\n * and `codexMaterializeOnConsolidation` and silently becomes a no-op when\n * either is disabled.\n */\nexport async function materializeAfterSemanticConsolidation(options: {\n config: PluginConfig;\n namespace?: string;\n memories?: MemoryFile[];\n memoryDir?: string;\n codexHome?: string;\n rolloutSummaries?: RolloutSummaryInput[];\n now?: Date;\n}): Promise<MaterializeResult | null> {\n // Delegates to the shared post-consolidation helper so semantic and causal\n // flows stay in lock-step — any guard/logging change happens in one place.\n return runPostConsolidationMaterialize(\"[semantic-consolidation]\", options);\n}\n"],"mappings":"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","names":["path","existsSync","path","existsSync"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  log
3
- } from "./chunk-KWBU5S5U.js";
3
+ } from "./chunk-2ODBA7MQ.js";
4
4
 
5
5
  // src/recall-state.ts
6
6
  import { appendFile, mkdir, readFile, writeFile } from "fs/promises";
@@ -182,4 +182,4 @@ export {
182
182
  LastRecallStore,
183
183
  TierMigrationStatusStore
184
184
  };
185
- //# sourceMappingURL=chunk-HG2NKWR2.js.map
185
+ //# sourceMappingURL=chunk-S4LX5EBI.js.map
@@ -2,7 +2,7 @@ import {
2
2
  clamp01,
3
3
  computeLifecycleValueInputs,
4
4
  daysSince
5
- } from "./chunk-QCCCQT3O.js";
5
+ } from "./chunk-TBBDFYXW.js";
6
6
 
7
7
  // src/tier-routing.ts
8
8
  function computeTierValueScore(memory, now, signals) {
@@ -65,4 +65,4 @@ export {
65
65
  computeTierValueScore,
66
66
  decideTierTransition
67
67
  };
68
- //# sourceMappingURL=chunk-4A24LIM2.js.map
68
+ //# sourceMappingURL=chunk-S75M5ZRK.js.map
@@ -3,7 +3,7 @@ var DEFAULT_POLICY = {
3
3
  promoteHeatThreshold: 0.55,
4
4
  staleDecayThreshold: 0.65,
5
5
  archiveDecayThreshold: 0.85,
6
- protectedCategories: ["decision", "principle", "commitment", "preference"]
6
+ protectedCategories: ["decision", "principle", "commitment", "preference", "procedure"]
7
7
  };
8
8
  function clamp01(value) {
9
9
  if (!Number.isFinite(value)) return 0;
@@ -186,4 +186,4 @@ export {
186
186
  computeDecay,
187
187
  decideLifecycleTransition
188
188
  };
189
- //# sourceMappingURL=chunk-QCCCQT3O.js.map
189
+ //# sourceMappingURL=chunk-TBBDFYXW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lifecycle.ts"],"sourcesContent":["import type {\n LifecycleState,\n MemoryCategory,\n MemoryFile,\n MemoryFrontmatter,\n VerificationState,\n} from \"./types.js\";\n\nexport interface LifecyclePolicy {\n promoteHeatThreshold: number;\n staleDecayThreshold: number;\n archiveDecayThreshold: number;\n protectedCategories: MemoryCategory[];\n}\n\nexport interface LifecycleSignals {\n /**\n * Optional relevance feedback in [-1, 1]. Negative values increase decay.\n * Positive values raise heat.\n */\n feedbackScore?: number;\n /**\n * Optional bounded prior derived from memory-action outcomes in [-1, 1].\n * This is intentionally low-impact to avoid circular amplification.\n */\n actionPriorScore?: number;\n /**\n * Optional causal impact score from CMC chain analysis in [0, 0.3].\n * Computed as 0.1 * incomingEdges + 0.15 * outgoingEdges, clamped.\n * Weight in heat/decay is intentionally low (0.05) to avoid circular amplification.\n */\n causalImpactScore?: number;\n}\n\nexport interface LifecycleDecision {\n currentState: LifecycleState;\n nextState: LifecycleState;\n heatScore: number;\n decayScore: number;\n changed: boolean;\n reason: string;\n}\n\nexport interface LifecycleValueInputs {\n confidence: number;\n access: number;\n recency: number;\n importance: number;\n feedback: number;\n disputedPenalty: number;\n}\n\nconst DEFAULT_POLICY: LifecyclePolicy = {\n promoteHeatThreshold: 0.55,\n staleDecayThreshold: 0.65,\n archiveDecayThreshold: 0.85,\n protectedCategories: [\"decision\", \"principle\", \"commitment\", \"preference\", \"procedure\"],\n};\n\nexport function clamp01(value: number): number {\n if (!Number.isFinite(value)) return 0;\n if (value < 0) return 0;\n if (value > 1) return 1;\n return value;\n}\n\nexport function clampLifecycleThreshold(value: number): number {\n return clamp01(value);\n}\n\nfunction parseIsoMs(value?: string): number | null {\n if (!value) return null;\n const ms = Date.parse(value);\n return Number.isFinite(ms) ? ms : null;\n}\n\nexport function daysSince(value: string | undefined, nowMs: number): number {\n const ts = parseIsoMs(value);\n if (ts === null) return 365;\n return Math.max(0, (nowMs - ts) / 86_400_000);\n}\n\nfunction confidenceTierWeight(frontmatter: MemoryFrontmatter): number {\n switch (frontmatter.confidenceTier) {\n case \"explicit\":\n return 1;\n case \"implied\":\n return 0.8;\n case \"inferred\":\n return 0.6;\n case \"speculative\":\n return 0.35;\n default:\n return clamp01(frontmatter.confidence ?? 0.5);\n }\n}\n\nfunction accessWeight(accessCount: number | undefined): number {\n const raw = accessCount ?? 0;\n if (raw <= 0) return 0;\n return clamp01(Math.log1p(raw) / Math.log1p(20));\n}\n\nfunction recencyWeight(frontmatter: MemoryFrontmatter, nowMs: number): number {\n const lastTouch = frontmatter.lastAccessed ?? frontmatter.updated ?? frontmatter.created;\n const ageDays = daysSince(lastTouch, nowMs);\n return clamp01(1 - ageDays / 90);\n}\n\nfunction feedbackWeight(signals?: LifecycleSignals): number {\n const raw = (signals?.feedbackScore ?? 0) + (signals?.actionPriorScore ?? 0);\n return clamp01((raw + 1) / 2);\n}\n\nfunction boundedFeedbackScore(signals?: LifecycleSignals): number {\n const raw = (signals?.feedbackScore ?? 0) + (signals?.actionPriorScore ?? 0);\n if (!Number.isFinite(raw)) return 0;\n if (raw < -1) return -1;\n if (raw > 1) return 1;\n return raw;\n}\n\nfunction isProtectedMemory(\n frontmatter: MemoryFrontmatter,\n policy: LifecyclePolicy,\n): boolean {\n return frontmatter.policyClass === \"protected\" || policy.protectedCategories.includes(frontmatter.category);\n}\n\nexport function resolveLifecycleState(frontmatter: MemoryFrontmatter): LifecycleState {\n if (frontmatter.status === \"archived\") return \"archived\";\n return frontmatter.lifecycleState ?? \"candidate\";\n}\n\nexport function computeHeat(\n memory: Pick<MemoryFile, \"frontmatter\">,\n now: Date,\n signals?: LifecycleSignals,\n): number {\n const frontmatter = memory.frontmatter;\n if (frontmatter.status === \"archived\") return 0;\n\n const inputs = computeLifecycleValueInputs(memory, now, signals);\n const causalBoost = clamp01(signals?.causalImpactScore ?? 0) * 0.05;\n const score = (inputs.confidence * 0.25)\n + (inputs.access * 0.3)\n + (inputs.recency * 0.2)\n + (inputs.importance * 0.15)\n + (inputs.feedback * 0.1)\n + causalBoost\n - inputs.disputedPenalty;\n return clamp01(score);\n}\n\nexport function computeLifecycleValueInputs(\n memory: Pick<MemoryFile, \"frontmatter\">,\n now: Date,\n signals?: LifecycleSignals,\n): LifecycleValueInputs {\n const frontmatter = memory.frontmatter;\n const nowMs = now.getTime();\n return {\n confidence: confidenceTierWeight(frontmatter),\n access: accessWeight(frontmatter.accessCount),\n recency: recencyWeight(frontmatter, nowMs),\n importance: clamp01(frontmatter.importance?.score ?? 0.5),\n feedback: feedbackWeight(signals),\n disputedPenalty: frontmatter.verificationState === \"disputed\" ? 0.2 : 0,\n };\n}\n\nexport function computeDecay(\n memory: Pick<MemoryFile, \"frontmatter\">,\n now: Date,\n signals?: LifecycleSignals,\n): number {\n const frontmatter = memory.frontmatter;\n if (frontmatter.status === \"archived\") return 1;\n\n const nowMs = now.getTime();\n const ageDays = daysSince(frontmatter.updated ?? frontmatter.created, nowMs);\n const staleAccessDays = daysSince(frontmatter.lastAccessed, nowMs);\n const ageRisk = clamp01(ageDays / 180);\n const staleAccessRisk = clamp01(staleAccessDays / 120);\n const confidenceRisk = 1 - confidenceTierWeight(frontmatter);\n const feedbackRisk = clamp01((boundedFeedbackScore(signals) * -1 + 1) / 2);\n const heat = computeHeat(memory, now, signals);\n\n const score = (ageRisk * 0.3)\n + (staleAccessRisk * 0.25)\n + (confidenceRisk * 0.2)\n + (feedbackRisk * 0.1)\n + ((1 - heat) * 0.15);\n\n return clamp01(score);\n}\n\nfunction toTerminalDisputedState(\n currentState: LifecycleState,\n): LifecycleState {\n if (currentState === \"archived\") return \"archived\";\n return \"stale\";\n}\n\nfunction isActiveEligible(verificationState?: VerificationState): boolean {\n return verificationState === \"user_confirmed\" || verificationState === \"system_inferred\";\n}\n\nexport function decideLifecycleTransition(\n memory: Pick<MemoryFile, \"frontmatter\">,\n policy: Partial<LifecyclePolicy>,\n now: Date,\n signals?: LifecycleSignals,\n): LifecycleDecision {\n const mergedPolicy: LifecyclePolicy = { ...DEFAULT_POLICY, ...policy };\n const frontmatter = memory.frontmatter;\n const currentState = resolveLifecycleState(frontmatter);\n const heatScore = computeHeat(memory, now, signals);\n const decayScore = computeDecay(memory, now, signals);\n const protectedMemory = isProtectedMemory(frontmatter, mergedPolicy);\n\n if (currentState === \"archived\") {\n return {\n currentState,\n nextState: \"archived\",\n heatScore,\n decayScore,\n changed: false,\n reason: \"archived_is_terminal\",\n };\n }\n\n if (frontmatter.verificationState === \"disputed\") {\n const nextState = toTerminalDisputedState(currentState);\n return {\n currentState,\n nextState,\n heatScore,\n decayScore,\n changed: nextState !== currentState,\n reason: \"disputed_memories_do_not_promote_to_active\",\n };\n }\n\n if (decayScore >= mergedPolicy.archiveDecayThreshold && !protectedMemory) {\n return {\n currentState,\n nextState: \"archived\",\n heatScore,\n decayScore,\n changed: true,\n reason: \"decay_exceeded_archive_threshold\",\n };\n }\n\n if (decayScore >= mergedPolicy.staleDecayThreshold) {\n return {\n currentState,\n nextState: \"stale\",\n heatScore,\n decayScore,\n changed: currentState !== \"stale\",\n reason: \"decay_exceeded_stale_threshold\",\n };\n }\n\n if (heatScore >= mergedPolicy.promoteHeatThreshold) {\n const nextState = isActiveEligible(frontmatter.verificationState)\n ? \"active\"\n : \"validated\";\n return {\n currentState,\n nextState,\n heatScore,\n decayScore,\n changed: currentState !== nextState,\n reason: \"heat_exceeded_promote_threshold\",\n };\n }\n\n return {\n currentState,\n nextState: currentState,\n heatScore,\n decayScore,\n changed: false,\n reason: \"no_transition\",\n };\n}\n"],"mappings":"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","names":[]}
@@ -11,4 +11,4 @@ export {
11
11
  confidenceTier,
12
12
  SPECULATIVE_TTL_DAYS
13
13
  };
14
- //# sourceMappingURL=chunk-U4PV25RD.js.map
14
+ //# sourceMappingURL=chunk-U2IQTSBY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["export type ReasoningEffort = \"none\" | \"low\" | \"medium\" | \"high\";\nexport type TriggerMode = \"smart\" | \"every_n\" | \"time_based\";\nexport type SignalLevel = \"none\" | \"low\" | \"medium\" | \"high\";\nexport type MemoryCategory = \"fact\" | \"preference\" | \"correction\" | \"entity\" | \"decision\" | \"relationship\" | \"principle\" | \"commitment\" | \"moment\" | \"skill\" | \"rule\" | \"procedure\";\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\";\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 =\n | \"off\"\n | \"minimal\"\n | \"low\"\n | \"medium\"\n | \"high\"\n | \"xhigh\"\n | \"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 =\n | \"exact-cache\"\n | \"fuzzy-cache\"\n | \"direct-answer\"\n | \"hybrid\"\n | \"rerank-graph\"\n | \"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\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}\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\nexport interface AgentAccessHttpConfig {\n enabled: boolean;\n host: string;\n port: number;\n authToken?: string;\n principal?: string;\n maxBodyBytes: 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/** Procedural memory (issue #519): mining + recall gates. All sub-features default off. */\nexport interface ProceduralConfig {\n enabled: boolean;\n /** Minimum recurrence count before emitting a candidate procedure (0 disables mining threshold). */\n minOccurrences: number;\n /** Minimum success rate from trajectory outcomes in [0, 1]. */\n successFloor: number;\n /** When auto-promotion is enabled, promote pending_review → active after this many occurrences. */\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 /** Max procedure memories to inject on task-initiation recall (1–10). */\n recallMaxProcedures: number;\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.70) return \"implied\";\n if (score >= 0.40) 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}\n\nexport interface PluginConfig {\n openaiApiKey: string | undefined;\n openaiBaseUrl: string | undefined;\n model: string;\n reasoningEffort: ReasoningEffort;\n triggerMode: TriggerMode;\n bufferMaxTurns: number;\n bufferMaxMinutes: number;\n consolidateEveryN: number;\n highSignalPatterns: string[];\n maxMemoryTokens: number;\n memoryOsPreset?: MemoryOsPresetName;\n qmdEnabled: boolean;\n qmdCollection: string;\n qmdMaxResults: 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 embeddingFallbackEnabled: boolean;\n embeddingFallbackProvider: \"auto\" | \"openai\" | \"local\";\n /** Optional absolute path to qmd binary. If unset, PATH/fallback discovery is used. */\n qmdPath?: string;\n memoryDir: string;\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 // 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 // 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 /**\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 // 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 flushOnResetEnabled: boolean;\n commandsListEnabled: boolean;\n openclawToolsEnabled: boolean;\n openclawToolSnippetMaxChars: number;\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 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 procedural: ProceduralConfig;\n heartbeat: HeartbeatConfig;\n slotBehavior: SlotBehaviorConfig;\n codexCompat: CodexCompatConfig;\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 /** Shadow mode: log judge verdicts but do not filter facts. Default false. */\n extractionJudgeShadow: boolean;\n // Hourly summaries\n hourlySummariesEnabled: boolean;\n daySummaryEnabled: boolean;\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 // 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 // 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 // Local LLM Provider (v2.1)\n localLlmEnabled: boolean;\n localLlmUrl: string;\n localLlmModel: string;\n /** Optional API key for authenticated OpenAI-compatible endpoints. */\n localLlmApiKey?: string;\n /** Additional headers for local/compatible endpoint requests. */\n localLlmHeaders?: Record<string, string>;\n /** If false, do not send Authorization header even when localLlmApiKey is set. */\n localLlmAuthHeader: boolean;\n localLlmFallback: boolean;\n /** Optional home directory override for local LLM helpers (LM Studio settings, CLI PATH). */\n localLlmHomeDir?: string;\n /** Optional absolute path to LMS CLI binary (preferred over auto-detection). */\n localLmsCliPath?: string;\n /** Optional bin directory prepended to PATH for LMS CLI execution. */\n localLmsBinDir?: string;\n /** Hard timeout for local LLM requests (ms). */\n localLlmTimeoutMs: number;\n /** Max context window for local LLM (override auto-detection). Set lower if your LLM server defaults to smaller contexts. */\n localLlmMaxContext?: number;\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 extractionDedupeWindowMs: number;\n extractionMinChars: number;\n extractionMinUserTurns: number;\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 prompt injection, 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 consolidationRequireNonZeroExtraction: boolean;\n consolidationMinIntervalMs: number;\n // QMD maintenance (debounced singleflight)\n qmdMaintenanceEnabled: boolean;\n qmdMaintenanceDebounceMs: 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 // Local LLM fast tier (v9.1) — smaller model for quick ops\n localLlmFastEnabled: boolean;\n localLlmFastModel: string;\n localLlmFastUrl: string;\n localLlmFastTimeoutMs: number;\n // Gateway config for fallback AI\n gatewayConfig?: GatewayConfig;\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 // v3.0 Multi-agent memory (namespaces)\n namespacesEnabled: boolean;\n defaultNamespace: string;\n sharedNamespace: string;\n principalFromSessionKeyMode: PrincipalFromSessionKeyMode;\n principalFromSessionKeyRules: PrincipalRule[];\n namespacePolicies: NamespacePolicy[];\n defaultRecallNamespaces: Array<\"self\" | \"shared\">;\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 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\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 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 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 /**\n * Issue #373 — Write-time semantic similarity guard. When enabled (default),\n * the orchestrator embeds each candidate fact and queries the existing\n * embedding index for its top-K nearest neighbors. If the best cosine\n * similarity is at or above `semanticDedupThreshold`, the fact is dropped\n * as a near-duplicate. Fails open (keeps the fact) if the embedding backend\n * is unavailable.\n */\n semanticDedupEnabled: boolean;\n /** Cosine similarity threshold in [0, 1] above which a candidate fact is skipped. */\n semanticDedupThreshold: number;\n /** Number of nearest-neighbor candidates to consider during semantic dedup. */\n semanticDedupCandidates: 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 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 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 // 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 /** 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 // v8.2: Temporal Memory Tree\n temporalMemoryTreeEnabled: boolean;\n tmtHourlyMinMemories: number;\n tmtSummaryMaxTokens: 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\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 // 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 whitespace-tokenized 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 // 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\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\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\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\nexport interface BufferTurn {\n role: \"user\" | \"assistant\";\n content: string;\n timestamp: string;\n sessionKey?: string;\n logicalSessionKey?: string;\n providerThreadId?: string | null;\n turnFingerprint?: string;\n persistProcessedFingerprint?: boolean;\n}\n\nexport interface BufferEntryState {\n turns: BufferTurn[];\n lastExtractionAt: string | null;\n extractionCount: number;\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 = \"correction_override\" | \"preference_affinity\" | \"topic_revisitation\" | \"action_pattern\" | \"outcome_preference\" | \"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/** Memory status for lifecycle management */\nexport type MemoryStatus = \"active\" | \"pending_review\" | \"rejected\" | \"quarantined\" | \"superseded\" | \"archived\";\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 {\n id: string;\n category: MemoryCategory;\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, or archived */\n status?: MemoryStatus;\n /** ID of memory that superseded this one */\n supersededBy?: string;\n /** Timestamp when superseded */\n supersededAt?: string;\n /** Timestamp when archived */\n archivedAt?: 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 // 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\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// 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/** 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 source?: ExtractionPassSource;\n promptedByQuestion?: string;\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\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\nexport interface ExtractionResult {\n facts: ExtractedFact[];\n profileUpdates: string[];\n entities: EntityMention[];\n questions: ExtractedQuestion[];\n identityReflection?: string;\n relationships?: ExtractedRelationship[];\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}\n\nexport interface EntityTimelineEntry {\n timestamp: string;\n text: string;\n source?: string;\n sessionKey?: string;\n principal?: string;\n}\n\nexport interface EntityStructuredSection {\n key: string;\n title: string;\n facts: string[];\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}\n\nexport interface QmdSearchResult {\n docid: string;\n path: string;\n snippet: string;\n score: number;\n explain?: QmdSearchExplain;\n transport?: \"daemon\" | \"subprocess\" | \"hybrid\" | \"scoped_prefilter\";\n}\n\nexport interface QmdSearchExplain {\n ftsScores?: number[];\n vectorScores?: number[];\n rrf?: number;\n rerankScore?: number;\n blendedScore?: 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\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 =\n | \"extraction\"\n | \"consolidation\"\n | \"replay\"\n | \"manual\"\n | \"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}\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\nexport type ContinuityIncidentState = \"open\" | \"closed\";\n\nexport interface ContinuityIncidentRecord {\n id: string;\n state: ContinuityIncidentState;\n openedAt: string;\n updatedAt: string;\n triggerWindow?: string;\n symptom: string;\n suspectedCause?: string;\n fixApplied?: string;\n verificationResult?: string;\n preventiveRule?: string;\n closedAt?: string;\n filePath?: string;\n}\n\nexport interface ContinuityIncidentOpenInput {\n triggerWindow?: string;\n symptom: string;\n suspectedCause?: string;\n}\n\nexport interface ContinuityIncidentCloseInput {\n fixApplied: string;\n verificationResult: string;\n preventiveRule?: string;\n}\n\nexport type ContinuityLoopCadence = \"daily\" | \"weekly\" | \"monthly\" | \"quarterly\";\nexport type ContinuityLoopStatus = \"active\" | \"paused\" | \"retired\";\n\nexport interface ContinuityImprovementLoop {\n id: string;\n cadence: ContinuityLoopCadence;\n purpose: string;\n status: ContinuityLoopStatus;\n killCondition: string;\n lastReviewed: string;\n notes?: string;\n}\n\nexport interface ContinuityLoopUpsertInput {\n id: string;\n cadence: ContinuityLoopCadence;\n purpose: string;\n status: ContinuityLoopStatus;\n killCondition: string;\n lastReviewed?: string;\n notes?: string;\n}\n\nexport interface ContinuityLoopReviewInput {\n status?: ContinuityLoopStatus;\n notes?: string;\n reviewedAt?: string;\n}\n\n/** Entry in the access tracking buffer (batched updates) */\nexport interface AccessTrackingEntry {\n memoryId: 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 injected into the system prompt.\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\" | string;\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 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 };\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"],"mappings":";AA+NO,SAAS,eAAe,OAA+B;AAC5D,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,SAAS,IAAM,QAAO;AAC1B,MAAI,SAAS,IAAM,QAAO;AAC1B,SAAO;AACT;AAGO,IAAM,uBAAuB;","names":[]}
@@ -0,0 +1,31 @@
1
+ // src/plugin-id.ts
2
+ var PLUGIN_ID = "openclaw-remnic";
3
+ var LEGACY_PLUGIN_ID = "openclaw-engram";
4
+ var REMNIC_PLUGIN_IDS = /* @__PURE__ */ new Set([PLUGIN_ID, LEGACY_PLUGIN_ID]);
5
+ function resolveRemnicPluginEntry(raw, preferredId) {
6
+ if (!raw || typeof raw !== "object") return void 0;
7
+ const r = raw;
8
+ const plugins = r["plugins"] && typeof r["plugins"] === "object" ? r["plugins"] : void 0;
9
+ const entries = plugins && plugins["entries"] && typeof plugins["entries"] === "object" ? plugins["entries"] : void 0;
10
+ if (!entries) return void 0;
11
+ const rawSlot = plugins && plugins["slots"] && typeof plugins["slots"] === "object" ? plugins["slots"]["memory"] : void 0;
12
+ const activeId = typeof rawSlot === "string" && REMNIC_PLUGIN_IDS.has(rawSlot) ? rawSlot : void 0;
13
+ const ownId = !activeId && typeof preferredId === "string" && REMNIC_PLUGIN_IDS.has(preferredId) ? preferredId : void 0;
14
+ const candidateIds = [activeId, ownId, PLUGIN_ID, LEGACY_PLUGIN_ID].filter(
15
+ (id) => typeof id === "string"
16
+ );
17
+ for (const id of candidateIds) {
18
+ const entry = entries[id];
19
+ if (entry !== void 0) {
20
+ return entry;
21
+ }
22
+ }
23
+ return void 0;
24
+ }
25
+
26
+ export {
27
+ PLUGIN_ID,
28
+ LEGACY_PLUGIN_ID,
29
+ resolveRemnicPluginEntry
30
+ };
31
+ //# sourceMappingURL=chunk-U66YHYC7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/plugin-id.ts"],"sourcesContent":["/**\n * Canonical plugin id for @remnic/plugin-openclaw.\n *\n * This is the identifier OpenClaw uses as a lookup key in plugins.slots.memory\n * and plugins.entries.<id>. The legacy shim package @joshuaswarren/openclaw-engram\n * intentionally uses a different id (\"openclaw-engram\") as a backwards-compat alias.\n */\nexport const PLUGIN_ID = \"openclaw-remnic\" as const;\nexport const LEGACY_PLUGIN_ID = \"openclaw-engram\" as const;\n\n/** The set of plugin ids that belong to Remnic (new canonical + legacy). */\nconst REMNIC_PLUGIN_IDS: ReadonlySet<string> = new Set([PLUGIN_ID, LEGACY_PLUGIN_ID]);\n\n/**\n * Resolve the Remnic plugin entry from an OpenClaw-shaped config object.\n *\n * Lookup order:\n * 1. `plugins.slots.memory` — but **only** when it resolves to a known\n * Remnic plugin id; foreign slots are ignored so mixed-plugin installs\n * do not accidentally apply another plugin's config to Remnic.\n * 2. `preferredId` — the caller's own plugin id (e.g. `\"openclaw-engram\"` for\n * the shim package); only consulted when no active slot overrides the choice.\n * 3. `plugins.entries[\"openclaw-remnic\"]`\n * 4. `plugins.entries[\"openclaw-engram\"]` (legacy backward-compat)\n *\n * Returns `undefined` when no Remnic entry is found.\n *\n * All five config-loader sites (loadPluginEntryFromFile, readPluginHooksPolicy,\n * loadPluginConfig in access-cli.ts, loadCliPluginConfig in operator-toolkit.ts,\n * and unwrapOpenClawEntry in materialize.cjs) delegate here so fallback order\n * and guard logic are defined in exactly one place.\n *\n * @param raw - The raw OpenClaw config object.\n * @param preferredId - The calling plugin's own id. When present and no\n * `plugins.slots.memory` slot is set, this id is tried before the hardcoded\n * `PLUGIN_ID`/`LEGACY_PLUGIN_ID` fallbacks. Ignored if it is not a known\n * Remnic plugin id (safety guard against unexpected values).\n */\nexport function resolveRemnicPluginEntry(\n raw: unknown,\n preferredId?: string,\n): Record<string, unknown> | undefined {\n if (!raw || typeof raw !== \"object\") return undefined;\n const r = raw as Record<string, unknown>;\n const plugins =\n r[\"plugins\"] && typeof r[\"plugins\"] === \"object\"\n ? (r[\"plugins\"] as Record<string, unknown>)\n : undefined;\n const entries =\n plugins && plugins[\"entries\"] && typeof plugins[\"entries\"] === \"object\"\n ? (plugins[\"entries\"] as Record<string, unknown>)\n : undefined;\n if (!entries) return undefined;\n\n const rawSlot =\n plugins && plugins[\"slots\"] && typeof plugins[\"slots\"] === \"object\"\n ? ((plugins[\"slots\"] as Record<string, unknown>)[\"memory\"] as string | undefined)\n : undefined;\n const activeId =\n typeof rawSlot === \"string\" && REMNIC_PLUGIN_IDS.has(rawSlot)\n ? rawSlot\n : undefined;\n\n // When no slot is set, honour the caller's own plugin id so shim installs\n // (id=\"openclaw-engram\") prefer their own entry over the canonical one.\n const ownId =\n !activeId &&\n typeof preferredId === \"string\" &&\n REMNIC_PLUGIN_IDS.has(preferredId)\n ? preferredId\n : undefined;\n\n const candidateIds = [activeId, ownId, PLUGIN_ID, LEGACY_PLUGIN_ID].filter(\n (id): id is string => typeof id === \"string\",\n );\n\n for (const id of candidateIds) {\n const entry = entries[id];\n if (entry !== undefined) {\n return entry as Record<string, unknown>;\n }\n }\n return undefined;\n}\n"],"mappings":";AAOO,IAAM,YAAY;AAClB,IAAM,mBAAmB;AAGhC,IAAM,oBAAyC,oBAAI,IAAI,CAAC,WAAW,gBAAgB,CAAC;AA2B7E,SAAS,yBACd,KACA,aACqC;AACrC,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,QAAM,IAAI;AACV,QAAM,UACJ,EAAE,SAAS,KAAK,OAAO,EAAE,SAAS,MAAM,WACnC,EAAE,SAAS,IACZ;AACN,QAAM,UACJ,WAAW,QAAQ,SAAS,KAAK,OAAO,QAAQ,SAAS,MAAM,WAC1D,QAAQ,SAAS,IAClB;AACN,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,UACJ,WAAW,QAAQ,OAAO,KAAK,OAAO,QAAQ,OAAO,MAAM,WACrD,QAAQ,OAAO,EAA8B,QAAQ,IACvD;AACN,QAAM,WACJ,OAAO,YAAY,YAAY,kBAAkB,IAAI,OAAO,IACxD,UACA;AAIN,QAAM,QACJ,CAAC,YACD,OAAO,gBAAgB,YACvB,kBAAkB,IAAI,WAAW,IAC7B,cACA;AAEN,QAAM,eAAe,CAAC,UAAU,OAAO,WAAW,gBAAgB,EAAE;AAAA,IAClE,CAAC,OAAqB,OAAO,OAAO;AAAA,EACtC;AAEA,aAAW,MAAM,cAAc;AAC7B,UAAM,QAAQ,QAAQ,EAAE;AACxB,QAAI,UAAU,QAAW;AACvB,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
@@ -29,20 +29,51 @@ function parseMemoryActionEligibilityContext(value) {
29
29
  source: "unknown"
30
30
  };
31
31
  }
32
+ var ProcedureStepExtractSchema = z.object({
33
+ order: z.number(),
34
+ intent: z.string(),
35
+ toolCall: z.object({
36
+ kind: z.string(),
37
+ signature: z.string()
38
+ }).optional().nullable(),
39
+ expectedOutcome: z.string().optional().nullable(),
40
+ optional: z.boolean().optional().nullable()
41
+ });
32
42
  var ExtractedFactSchema = z.object({
33
- category: z.enum(["fact", "preference", "correction", "entity", "decision", "relationship", "principle", "commitment", "moment", "skill", "rule"]),
43
+ category: z.enum([
44
+ "fact",
45
+ "preference",
46
+ "correction",
47
+ "entity",
48
+ "decision",
49
+ "relationship",
50
+ "principle",
51
+ "commitment",
52
+ "moment",
53
+ "skill",
54
+ "rule",
55
+ "procedure"
56
+ ]),
34
57
  content: z.string().describe("The memory content \u2014 a clear, standalone statement"),
35
58
  confidence: z.number().min(0).max(1).describe("How confident are you this is correct (0-1)"),
36
59
  tags: z.array(z.string()).describe("Relevant tags for categorization"),
37
60
  entityRef: z.string().optional().nullable().describe("If about an entity, its normalized name (e.g. person-jane-doe)"),
38
61
  promptedByQuestion: z.string().optional().nullable().describe("Optional proactive follow-up question that surfaced this fact."),
39
- structuredAttributes: z.record(z.string(), z.string()).optional().nullable().describe('Structured key-value attributes when the fact contains measurable or categorical data (e.g., {"price": "29.99", "color": "blue", "date": "2024-03-15"}).')
62
+ structuredAttributes: z.record(z.string(), z.string()).optional().nullable().describe('Structured key-value attributes when the fact contains measurable or categorical data (e.g., {"price": "29.99", "color": "blue", "date": "2024-03-15"}).'),
63
+ procedureSteps: z.array(ProcedureStepExtractSchema).optional().nullable().describe(
64
+ 'For category "procedure" only: ordered steps (intent per step). At least two steps; include explicit trigger phrasing in content (e.g. "When you deploy\u2026").'
65
+ )
40
66
  });
41
67
  var EntityMentionSchema = z.object({
42
68
  name: z.string().describe("Normalized entity name (e.g. jane-doe, acme-corp, my-project)"),
43
69
  type: z.enum(["person", "project", "tool", "company", "place", "other"]),
44
70
  facts: z.array(z.string()).describe("New facts learned about this entity in this conversation"),
45
- promptedByQuestion: z.string().optional().nullable().describe("Optional proactive follow-up question that surfaced this entity.")
71
+ promptedByQuestion: z.string().optional().nullable().describe("Optional proactive follow-up question that surfaced this entity."),
72
+ structuredSections: z.array(z.object({
73
+ key: z.string(),
74
+ title: z.string(),
75
+ facts: z.array(z.string())
76
+ })).optional().nullable().describe("Optional named sections for entity-specific facts. Use when facts clearly belong under a durable heading such as Beliefs or Building / Working On.")
46
77
  });
47
78
  var ExtractedQuestionSchema = z.object({
48
79
  question: z.string().describe("A genuine question the AI is curious about based on this conversation"),
@@ -175,6 +206,7 @@ export {
175
206
  MemoryActionEligibilityContextSchema,
176
207
  parseMemoryActionType,
177
208
  parseMemoryActionEligibilityContext,
209
+ ProcedureStepExtractSchema,
178
210
  ExtractedFactSchema,
179
211
  EntityMentionSchema,
180
212
  ExtractedQuestionSchema,
@@ -195,4 +227,4 @@ export {
195
227
  BehaviorLoopAdjustmentSchema,
196
228
  BehaviorLoopPolicyStateSchema
197
229
  };
198
- //# sourceMappingURL=chunk-MWGVGUIS.js.map
230
+ //# sourceMappingURL=chunk-UEYA6UC7.js.map