@remnic/core 10.0.4 → 10.0.6

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 (308) hide show
  1. package/dist/access-admin-ops-surface.js +49 -49
  2. package/dist/access-authorization-probe.js +50 -50
  3. package/dist/access-boundary.js +49 -49
  4. package/dist/access-cli.d.ts.map +1 -1
  5. package/dist/access-cli.js +120 -115
  6. package/dist/access-cli.js.map +1 -1
  7. package/dist/access-extraction-force-flush.js +49 -49
  8. package/dist/access-http-briefing.js +50 -50
  9. package/dist/access-http-query.js +51 -51
  10. package/dist/access-http.d.ts.map +1 -1
  11. package/dist/access-http.js +72 -71
  12. package/dist/access-identity-continuity-surface.js +49 -49
  13. package/dist/access-lcm-surface.js +49 -49
  14. package/dist/access-mcp-correction-tools.d.ts +10 -0
  15. package/dist/access-mcp-correction-tools.d.ts.map +1 -0
  16. package/dist/access-mcp-correction-tools.js +8 -0
  17. package/dist/access-mcp.d.ts.map +1 -1
  18. package/dist/access-mcp.js +63 -62
  19. package/dist/access-namespace-preflight.js +49 -49
  20. package/dist/access-observe-write-category.js +3 -3
  21. package/dist/access-observe-write-surface.js +49 -49
  22. package/dist/access-offline-manifest.js +5 -5
  23. package/dist/access-operations-batch.js +52 -52
  24. package/dist/access-operations.d.ts +1 -0
  25. package/dist/access-operations.d.ts.map +1 -1
  26. package/dist/access-operations.js +59 -59
  27. package/dist/access-recall-concurrency.js +49 -49
  28. package/dist/access-recall-diagnostics.js +2 -2
  29. package/dist/access-recall-response.js +49 -49
  30. package/dist/access-recall-surface.js +49 -49
  31. package/dist/access-service.d.ts.map +1 -1
  32. package/dist/access-service.js +49 -49
  33. package/dist/access-surface-catalog.js +51 -51
  34. package/dist/active-recall.js +22 -22
  35. package/dist/adapters/index.js +4 -4
  36. package/dist/adapters/registry.js +2 -2
  37. package/dist/auto-sync-FN7E3OTO.js +2 -2
  38. package/dist/briefing.js +18 -18
  39. package/dist/buffer.js +1 -1
  40. package/dist/calibration.d.ts +31 -3
  41. package/dist/calibration.d.ts.map +1 -1
  42. package/dist/calibration.js +67 -16
  43. package/dist/calibration.js.map +1 -1
  44. package/dist/causal-consolidation.js +18 -18
  45. package/dist/{chunk-5YDWSWBU.js → chunk-2CP577K6.js} +2 -2
  46. package/dist/{chunk-QVBB2CLG.js → chunk-2UF42WXV.js} +27 -26
  47. package/dist/chunk-2UF42WXV.js.map +1 -0
  48. package/dist/{chunk-ABET5LA5.js → chunk-37HCPM56.js} +23 -23
  49. package/dist/{chunk-HSZBALHV.js → chunk-4JNTGPBR.js} +2 -2
  50. package/dist/{chunk-H6FA36LC.js → chunk-4K2H4KTR.js} +5 -5
  51. package/dist/{chunk-H7BZBSP6.js → chunk-52II7PB6.js} +4 -4
  52. package/dist/{chunk-RH6MQ5NG.js → chunk-5BPXMAUP.js} +17 -68
  53. package/dist/chunk-5BPXMAUP.js.map +1 -0
  54. package/dist/{chunk-U3LVARGV.js → chunk-5X4ZJESN.js} +2 -2
  55. package/dist/{chunk-SNGNGH53.js → chunk-63UNVGZW.js} +4 -4
  56. package/dist/{chunk-2KB7TTAP.js → chunk-64S6X4IU.js} +4 -4
  57. package/dist/{chunk-MEMH5NKJ.js → chunk-6C76FZRM.js} +2 -2
  58. package/dist/{chunk-3QAIX7TV.js → chunk-6KHF6PAB.js} +2 -2
  59. package/dist/{chunk-MYBCB4AT.js → chunk-6T42L5FI.js} +2 -2
  60. package/dist/{chunk-XTWF5XR6.js → chunk-7CZIKCJR.js} +69 -69
  61. package/dist/{chunk-R6EOFKZG.js → chunk-7LYRFI5A.js} +2 -2
  62. package/dist/{chunk-4NFRYOHT.js → chunk-ANZXWOYD.js} +2 -2
  63. package/dist/{chunk-5XXTZ4Y7.js → chunk-B657ZGDH.js} +2 -2
  64. package/dist/{chunk-5QHTQMRL.js → chunk-CCMXCZLT.js} +8 -6
  65. package/dist/chunk-CCMXCZLT.js.map +1 -0
  66. package/dist/{chunk-NSQ3GUVO.js → chunk-CYEK6Z3P.js} +4 -4
  67. package/dist/{chunk-4DXVWBDB.js → chunk-D6M3VKMY.js} +4 -10
  68. package/dist/chunk-D6M3VKMY.js.map +1 -0
  69. package/dist/{chunk-IRYKRBEX.js → chunk-DJMVSUPK.js} +3 -3
  70. package/dist/{chunk-7OOW72QA.js → chunk-DTXN4NVD.js} +307 -138
  71. package/dist/chunk-DTXN4NVD.js.map +1 -0
  72. package/dist/{chunk-UCCB7RKF.js → chunk-EMVJU7WC.js} +2 -2
  73. package/dist/{chunk-IY7ZISXV.js → chunk-ENNC3RZ6.js} +2 -2
  74. package/dist/{chunk-QLK3SL7Q.js → chunk-ETSJD5IV.js} +131 -12
  75. package/dist/chunk-ETSJD5IV.js.map +1 -0
  76. package/dist/{chunk-PTFL3INS.js → chunk-FC6RCOIU.js} +2 -2
  77. package/dist/{chunk-CQQTEDHS.js → chunk-FLV7M7CT.js} +2 -2
  78. package/dist/{chunk-3SL52JKD.js → chunk-FZVROZKC.js} +5 -5
  79. package/dist/{chunk-Z2EVKNZR.js → chunk-GEGUBU3D.js} +2 -2
  80. package/dist/{chunk-LIGP5M2C.js → chunk-GF4H3B3R.js} +8 -8
  81. package/dist/{chunk-FMIVV5SV.js → chunk-I56MTWE7.js} +2 -2
  82. package/dist/{chunk-EUL3S3LE.js → chunk-J5WI4QRP.js} +4 -4
  83. package/dist/{chunk-436IRJUD.js → chunk-JA54KIYD.js} +2 -2
  84. package/dist/{chunk-QWNBQ2T5.js → chunk-JMF5Q4JY.js} +10 -10
  85. package/dist/{chunk-IDSSDL4W.js → chunk-JXSYWTFK.js} +21 -4
  86. package/dist/{chunk-IDSSDL4W.js.map → chunk-JXSYWTFK.js.map} +1 -1
  87. package/dist/{chunk-ABDTBGI2.js → chunk-KCCZFDJD.js} +2 -2
  88. package/dist/chunk-KIUVEMBI.js +49 -0
  89. package/dist/chunk-KIUVEMBI.js.map +1 -0
  90. package/dist/{chunk-IINXHPCV.js → chunk-KQI2PKYP.js} +2 -2
  91. package/dist/{chunk-ZOGZMAGI.js → chunk-KQT4DRKF.js} +2 -2
  92. package/dist/{chunk-RGPPNNDJ.js → chunk-KSUOM3YU.js} +9 -9
  93. package/dist/{chunk-CXG2T7BJ.js → chunk-KZEMFD6E.js} +3 -3
  94. package/dist/{chunk-O57VUN4U.js → chunk-L2VS27PZ.js} +2 -2
  95. package/dist/{chunk-IVGJXS5F.js → chunk-L34W4W4I.js} +2 -2
  96. package/dist/{chunk-MEP3POVW.js → chunk-L3PWXD73.js} +2 -2
  97. package/dist/{chunk-NQHSXHJP.js → chunk-L6GNS5GB.js} +2 -2
  98. package/dist/{chunk-QDKABMUE.js → chunk-MH2DXDN6.js} +2 -2
  99. package/dist/{chunk-DQJCQHII.js → chunk-MQNOIH2C.js} +6 -6
  100. package/dist/{chunk-IDSZNHIP.js → chunk-MULNBSPP.js} +4 -4
  101. package/dist/{chunk-BDHWKFGM.js → chunk-OFEHITAZ.js} +2 -2
  102. package/dist/{chunk-RYN4AIRM.js → chunk-OZABSIOV.js} +4 -4
  103. package/dist/{chunk-IXF7TRMQ.js → chunk-Q562GF7J.js} +2 -2
  104. package/dist/{chunk-H2XCCUOT.js → chunk-QEPL7KIZ.js} +15 -15
  105. package/dist/{chunk-WLC6XA6C.js → chunk-QGLEM46C.js} +16 -53
  106. package/dist/chunk-QGLEM46C.js.map +1 -0
  107. package/dist/{chunk-Y7CXM7UM.js → chunk-QMFKW4OU.js} +3 -3
  108. package/dist/{chunk-Z63GU5NN.js → chunk-QWWMS4AE.js} +1 -1
  109. package/dist/chunk-QWWMS4AE.js.map +1 -0
  110. package/dist/{chunk-R4O2G6ML.js → chunk-RSMIRLKM.js} +2 -2
  111. package/dist/{chunk-6AH2DDHI.js → chunk-S5TGRZWY.js} +2 -2
  112. package/dist/{chunk-TYOBAFCY.js → chunk-SNXBXYUE.js} +3 -3
  113. package/dist/{chunk-IHIP7TJ3.js → chunk-SWRN6U7Q.js} +2 -2
  114. package/dist/{chunk-DBA2W6VV.js → chunk-SZXPKUMN.js} +6 -6
  115. package/dist/{chunk-WQUGCOKF.js → chunk-TFA6HPOA.js} +4 -4
  116. package/dist/{chunk-DYRXRQHV.js → chunk-TL4PM7PC.js} +2 -2
  117. package/dist/{chunk-TSEUVMJL.js → chunk-TRBLJU6Y.js} +39 -39
  118. package/dist/{chunk-VWCFR4NR.js → chunk-U6OXJQEH.js} +5 -5
  119. package/dist/{chunk-YMXCJ7SE.js → chunk-VUEG3TZ4.js} +2 -2
  120. package/dist/{chunk-ORAL4HJC.js → chunk-WG4TMZJ2.js} +2 -2
  121. package/dist/{chunk-CV5AMCPX.js → chunk-XPRAOTJY.js} +2 -2
  122. package/dist/{chunk-RZCRXCLI.js → chunk-XQSJXNWC.js} +2 -2
  123. package/dist/{chunk-GLS3A4SQ.js → chunk-YGPXV546.js} +2 -2
  124. package/dist/{chunk-P22M3DX3.js → chunk-YJAOYEWI.js} +2 -2
  125. package/dist/{chunk-OAFRMLL4.js → chunk-YJEUHGZZ.js} +2 -2
  126. package/dist/cli.js +92 -91
  127. package/dist/coding/export-okf-codegraph.js +16 -16
  128. package/dist/compounding/engine.d.ts.map +1 -1
  129. package/dist/compounding/engine.js +17 -17
  130. package/dist/config.d.ts.map +1 -1
  131. package/dist/config.js +22 -22
  132. package/dist/connectors/codex-materialize-runner.js +17 -17
  133. package/dist/connectors/index.js +17 -17
  134. package/dist/consolidation-provenance-check.js +2 -2
  135. package/dist/contradiction/index.js +7 -7
  136. package/dist/convergence-refresh.js +19 -19
  137. package/dist/conversation-index/backend.js +2 -2
  138. package/dist/corpus-watermark.js +19 -19
  139. package/dist/correction/correction-access-wiring.d.ts +6 -6
  140. package/dist/correction/correction-access-wiring.d.ts.map +1 -1
  141. package/dist/correction/correction-classify.d.ts +25 -0
  142. package/dist/correction/correction-classify.d.ts.map +1 -0
  143. package/dist/correction/correction-config.d.ts +11 -0
  144. package/dist/correction/correction-config.d.ts.map +1 -0
  145. package/dist/correction/correction-contract.d.ts +13 -1
  146. package/dist/correction/correction-contract.d.ts.map +1 -1
  147. package/dist/correction/correction-executor.d.ts +2 -0
  148. package/dist/correction/correction-executor.d.ts.map +1 -1
  149. package/dist/correction/correction-planner.d.ts +24 -3
  150. package/dist/correction/correction-planner.d.ts.map +1 -1
  151. package/dist/entity-retrieval.js +17 -17
  152. package/dist/event-order-recall.js +2 -2
  153. package/dist/explicit-capture.js +2 -2
  154. package/dist/external-wiki-access.js +50 -50
  155. package/dist/extraction-span-materialize.js +2 -2
  156. package/dist/extraction.js +25 -25
  157. package/dist/harmonic-retrieval.js +17 -17
  158. package/dist/index.js +192 -191
  159. package/dist/lcm/index.js +3 -3
  160. package/dist/local-llm.d.ts.map +1 -1
  161. package/dist/local-llm.js +1 -1
  162. package/dist/location/index.js +19 -19
  163. package/dist/maintenance/memory-governance.js +17 -17
  164. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +17 -17
  165. package/dist/maintenance/rebuild-memory-projection-runner.js +19 -19
  166. package/dist/maintenance/rebuild-memory-projection.js +18 -18
  167. package/dist/memory-browse-operations.js +50 -50
  168. package/dist/memory-browse.js +1 -1
  169. package/dist/memory-subject.js +1 -1
  170. package/dist/namespaces/migrate.js +18 -18
  171. package/dist/namespaces/search.js +6 -6
  172. package/dist/namespaces/storage.js +17 -17
  173. package/dist/operator-doctor-corpus.js +20 -20
  174. package/dist/operator-toolkit.js +32 -32
  175. package/dist/orchestration/maintenance.js +28 -28
  176. package/dist/orchestration/session-context.d.ts.map +1 -1
  177. package/dist/orchestrator.js +116 -115
  178. package/dist/recall-explain-renderer.js +3 -3
  179. package/dist/recall-navigate-access.js +2 -2
  180. package/dist/recall-navigate-disclosure.js +2 -2
  181. package/dist/recall-navigate.js +2 -2
  182. package/dist/recall-navigation-operations.js +50 -50
  183. package/dist/recall-navigation.js +49 -49
  184. package/dist/recall-why-service.js +2 -2
  185. package/dist/recall-xray-cli.js +4 -4
  186. package/dist/recall-xray-renderer.js +3 -3
  187. package/dist/recall-xray.js +2 -2
  188. package/dist/reconcile/manifest.js +3 -3
  189. package/dist/{resolution-RSG4HZTL.js → resolution-TV3F7ZTH.js} +7 -7
  190. package/dist/resolution-TV3F7ZTH.js.map +1 -0
  191. package/dist/resume-bundles.js +23 -23
  192. package/dist/salvage-envelope.js +1 -1
  193. package/dist/search/factory.js +5 -5
  194. package/dist/search/index.js +9 -9
  195. package/dist/semantic-consolidation.js +23 -23
  196. package/dist/semantic-rule-promotion.js +17 -17
  197. package/dist/semantic-rule-verifier.js +17 -17
  198. package/dist/source-agent-qualifier.js +17 -17
  199. package/dist/standup/build.js +17 -17
  200. package/dist/storage.js +16 -16
  201. package/dist/subject-config.js +1 -1
  202. package/dist/summarizer.js +3 -3
  203. package/dist/support-passport/index.js +66 -66
  204. package/dist/transfer/export-okf.js +17 -17
  205. package/dist/types.d.ts +6 -23
  206. package/dist/types.d.ts.map +1 -1
  207. package/dist/types.js +1 -1
  208. package/dist/verified-recall.js +17 -17
  209. package/dist/who-knows-cli.js +18 -18
  210. package/dist/who-knows.js +17 -17
  211. package/dist/write-envelope.js +1 -1
  212. package/package.json +2 -2
  213. package/src/access-cli.ts +4 -0
  214. package/src/access-http.ts +1 -0
  215. package/src/access-mcp-correction-tools.ts +56 -0
  216. package/src/access-mcp.ts +4 -49
  217. package/src/access-operations.ts +3 -0
  218. package/src/access-service.ts +12 -21
  219. package/src/calibration-local-llm.test.ts +361 -0
  220. package/src/calibration.ts +109 -26
  221. package/src/compounding/engine.ts +2 -8
  222. package/src/config.test.ts +26 -0
  223. package/src/config.ts +4 -60
  224. package/src/correction/correction-access-wiring.ts +56 -16
  225. package/src/correction/correction-classify.test.ts +185 -0
  226. package/src/correction/correction-classify.ts +157 -0
  227. package/src/correction/correction-config.ts +93 -0
  228. package/src/correction/correction-contract.ts +61 -14
  229. package/src/correction/correction-executor.test.ts +102 -2
  230. package/src/correction/correction-executor.ts +19 -1
  231. package/src/correction/correction-planner.test.ts +141 -0
  232. package/src/correction/correction-planner.ts +117 -11
  233. package/src/correction/correction-surfaces.test.ts +22 -1
  234. package/src/local-llm-qos.test.ts +36 -0
  235. package/src/local-llm.ts +20 -3
  236. package/src/orchestration/session-context.ts +0 -15
  237. package/src/recall-handles-wiring.test.ts +24 -0
  238. package/src/types.ts +6 -23
  239. package/dist/chunk-4DXVWBDB.js.map +0 -1
  240. package/dist/chunk-5QHTQMRL.js.map +0 -1
  241. package/dist/chunk-7OOW72QA.js.map +0 -1
  242. package/dist/chunk-QLK3SL7Q.js.map +0 -1
  243. package/dist/chunk-QVBB2CLG.js.map +0 -1
  244. package/dist/chunk-RH6MQ5NG.js.map +0 -1
  245. package/dist/chunk-WLC6XA6C.js.map +0 -1
  246. package/dist/chunk-Z63GU5NN.js.map +0 -1
  247. /package/dist/{resolution-RSG4HZTL.js.map → access-mcp-correction-tools.js.map} +0 -0
  248. /package/dist/{chunk-5YDWSWBU.js.map → chunk-2CP577K6.js.map} +0 -0
  249. /package/dist/{chunk-ABET5LA5.js.map → chunk-37HCPM56.js.map} +0 -0
  250. /package/dist/{chunk-HSZBALHV.js.map → chunk-4JNTGPBR.js.map} +0 -0
  251. /package/dist/{chunk-H6FA36LC.js.map → chunk-4K2H4KTR.js.map} +0 -0
  252. /package/dist/{chunk-H7BZBSP6.js.map → chunk-52II7PB6.js.map} +0 -0
  253. /package/dist/{chunk-U3LVARGV.js.map → chunk-5X4ZJESN.js.map} +0 -0
  254. /package/dist/{chunk-SNGNGH53.js.map → chunk-63UNVGZW.js.map} +0 -0
  255. /package/dist/{chunk-2KB7TTAP.js.map → chunk-64S6X4IU.js.map} +0 -0
  256. /package/dist/{chunk-MEMH5NKJ.js.map → chunk-6C76FZRM.js.map} +0 -0
  257. /package/dist/{chunk-3QAIX7TV.js.map → chunk-6KHF6PAB.js.map} +0 -0
  258. /package/dist/{chunk-MYBCB4AT.js.map → chunk-6T42L5FI.js.map} +0 -0
  259. /package/dist/{chunk-XTWF5XR6.js.map → chunk-7CZIKCJR.js.map} +0 -0
  260. /package/dist/{chunk-R6EOFKZG.js.map → chunk-7LYRFI5A.js.map} +0 -0
  261. /package/dist/{chunk-4NFRYOHT.js.map → chunk-ANZXWOYD.js.map} +0 -0
  262. /package/dist/{chunk-5XXTZ4Y7.js.map → chunk-B657ZGDH.js.map} +0 -0
  263. /package/dist/{chunk-NSQ3GUVO.js.map → chunk-CYEK6Z3P.js.map} +0 -0
  264. /package/dist/{chunk-IRYKRBEX.js.map → chunk-DJMVSUPK.js.map} +0 -0
  265. /package/dist/{chunk-UCCB7RKF.js.map → chunk-EMVJU7WC.js.map} +0 -0
  266. /package/dist/{chunk-IY7ZISXV.js.map → chunk-ENNC3RZ6.js.map} +0 -0
  267. /package/dist/{chunk-PTFL3INS.js.map → chunk-FC6RCOIU.js.map} +0 -0
  268. /package/dist/{chunk-CQQTEDHS.js.map → chunk-FLV7M7CT.js.map} +0 -0
  269. /package/dist/{chunk-3SL52JKD.js.map → chunk-FZVROZKC.js.map} +0 -0
  270. /package/dist/{chunk-Z2EVKNZR.js.map → chunk-GEGUBU3D.js.map} +0 -0
  271. /package/dist/{chunk-LIGP5M2C.js.map → chunk-GF4H3B3R.js.map} +0 -0
  272. /package/dist/{chunk-FMIVV5SV.js.map → chunk-I56MTWE7.js.map} +0 -0
  273. /package/dist/{chunk-EUL3S3LE.js.map → chunk-J5WI4QRP.js.map} +0 -0
  274. /package/dist/{chunk-436IRJUD.js.map → chunk-JA54KIYD.js.map} +0 -0
  275. /package/dist/{chunk-QWNBQ2T5.js.map → chunk-JMF5Q4JY.js.map} +0 -0
  276. /package/dist/{chunk-ABDTBGI2.js.map → chunk-KCCZFDJD.js.map} +0 -0
  277. /package/dist/{chunk-IINXHPCV.js.map → chunk-KQI2PKYP.js.map} +0 -0
  278. /package/dist/{chunk-ZOGZMAGI.js.map → chunk-KQT4DRKF.js.map} +0 -0
  279. /package/dist/{chunk-RGPPNNDJ.js.map → chunk-KSUOM3YU.js.map} +0 -0
  280. /package/dist/{chunk-CXG2T7BJ.js.map → chunk-KZEMFD6E.js.map} +0 -0
  281. /package/dist/{chunk-O57VUN4U.js.map → chunk-L2VS27PZ.js.map} +0 -0
  282. /package/dist/{chunk-IVGJXS5F.js.map → chunk-L34W4W4I.js.map} +0 -0
  283. /package/dist/{chunk-MEP3POVW.js.map → chunk-L3PWXD73.js.map} +0 -0
  284. /package/dist/{chunk-NQHSXHJP.js.map → chunk-L6GNS5GB.js.map} +0 -0
  285. /package/dist/{chunk-QDKABMUE.js.map → chunk-MH2DXDN6.js.map} +0 -0
  286. /package/dist/{chunk-DQJCQHII.js.map → chunk-MQNOIH2C.js.map} +0 -0
  287. /package/dist/{chunk-IDSZNHIP.js.map → chunk-MULNBSPP.js.map} +0 -0
  288. /package/dist/{chunk-BDHWKFGM.js.map → chunk-OFEHITAZ.js.map} +0 -0
  289. /package/dist/{chunk-RYN4AIRM.js.map → chunk-OZABSIOV.js.map} +0 -0
  290. /package/dist/{chunk-IXF7TRMQ.js.map → chunk-Q562GF7J.js.map} +0 -0
  291. /package/dist/{chunk-H2XCCUOT.js.map → chunk-QEPL7KIZ.js.map} +0 -0
  292. /package/dist/{chunk-Y7CXM7UM.js.map → chunk-QMFKW4OU.js.map} +0 -0
  293. /package/dist/{chunk-R4O2G6ML.js.map → chunk-RSMIRLKM.js.map} +0 -0
  294. /package/dist/{chunk-6AH2DDHI.js.map → chunk-S5TGRZWY.js.map} +0 -0
  295. /package/dist/{chunk-TYOBAFCY.js.map → chunk-SNXBXYUE.js.map} +0 -0
  296. /package/dist/{chunk-IHIP7TJ3.js.map → chunk-SWRN6U7Q.js.map} +0 -0
  297. /package/dist/{chunk-DBA2W6VV.js.map → chunk-SZXPKUMN.js.map} +0 -0
  298. /package/dist/{chunk-WQUGCOKF.js.map → chunk-TFA6HPOA.js.map} +0 -0
  299. /package/dist/{chunk-DYRXRQHV.js.map → chunk-TL4PM7PC.js.map} +0 -0
  300. /package/dist/{chunk-TSEUVMJL.js.map → chunk-TRBLJU6Y.js.map} +0 -0
  301. /package/dist/{chunk-VWCFR4NR.js.map → chunk-U6OXJQEH.js.map} +0 -0
  302. /package/dist/{chunk-YMXCJ7SE.js.map → chunk-VUEG3TZ4.js.map} +0 -0
  303. /package/dist/{chunk-ORAL4HJC.js.map → chunk-WG4TMZJ2.js.map} +0 -0
  304. /package/dist/{chunk-CV5AMCPX.js.map → chunk-XPRAOTJY.js.map} +0 -0
  305. /package/dist/{chunk-RZCRXCLI.js.map → chunk-XQSJXNWC.js.map} +0 -0
  306. /package/dist/{chunk-GLS3A4SQ.js.map → chunk-YGPXV546.js.map} +0 -0
  307. /package/dist/{chunk-P22M3DX3.js.map → chunk-YJAOYEWI.js.map} +0 -0
  308. /package/dist/{chunk-OAFRMLL4.js.map → chunk-YJEUHGZZ.js.map} +0 -0
package/src/access-cli.ts CHANGED
@@ -207,6 +207,7 @@ function usage(): string {
207
207
  "Correct options (issue #1580 — one plan/apply pipeline for all corrections):",
208
208
  " --text \"<correction>\" plan a correction from natural language",
209
209
  " --id <memoryId> (repeatable) explicit target memory ids",
210
+ " --duplicate-of <memoryId> winner id; requires exactly one --id (skips the classifier)",
210
211
  " --apply apply a pending plan (requires --plan-id + --confirm)",
211
212
  " --plan-id <id> the plan to apply or discard",
212
213
  " --confirm confirm the apply (safety guard, rule 48)",
@@ -297,6 +298,7 @@ const COMMAND_SPECS: Record<CommandName, CommandSpec> = {
297
298
  valueOptions: new Set([
298
299
  "text",
299
300
  "id",
301
+ "duplicate-of",
300
302
  "plan-id",
301
303
  "namespace",
302
304
  "session-key",
@@ -830,6 +832,7 @@ async function runCorrect(args: ParsedArgs, preferredId?: string): Promise<void>
830
832
  // so schema validation + namespace policy reach this path (same as MCP/HTTP).
831
833
  const text = requireOption(args, "text");
832
834
  const targetIds = getAllOptions(args, "id");
835
+ const duplicateOf = getLastOption(args, "duplicate-of");
833
836
  const planOp = getOperation("memory_correct_plan");
834
837
  if (!planOp) {
835
838
  throw new Error("access-boundary: operation not registered: memory_correct_plan");
@@ -838,6 +841,7 @@ async function runCorrect(args: ParsedArgs, preferredId?: string): Promise<void>
838
841
  {
839
842
  text,
840
843
  ...(targetIds.length > 0 ? { targetIds } : {}),
844
+ ...(duplicateOf ? { duplicateOf } : {}),
841
845
  ...(sessionKey ? { sessionKey } : {}),
842
846
  ...(namespace ? { namespace } : {}),
843
847
  },
@@ -1999,6 +1999,7 @@ export class EngramAccessHttpServer extends ReviewDeckAccessHttpBase {
1999
1999
  const output = (await op.run(this.gatedBodyNamespace(req, body), {
2000
2000
  service: this.service,
2001
2001
  authenticatedPrincipal: this.resolveRequestPrincipal(req),
2002
+ ...(abortSignal ? { abortSignal } : {}),
2002
2003
  })) as { result: unknown };
2003
2004
  this.recordWriteRateLimitHit(req);
2004
2005
  this.respondJson(res, 200, output.result);
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Correction MCP tool descriptors.
3
+ *
4
+ * Extracted from access-mcp.ts (over its fileSizeGrandfather ceiling) so
5
+ * `duplicateOf` can land without growing that file. Aliases are applied by
6
+ * the caller via withToolAliases.
7
+ */
8
+ import type { McpTool } from "./access-mcp.js";
9
+
10
+ export const correctionMcpTools: McpTool[] = [
11
+ {
12
+ name: "engram.memory_correct_plan",
13
+ description:
14
+ "Plan a memory correction from a plain-language statement. Returns a CorrectionPlan with a diff preview; apply via memory_correct_apply. Pass duplicateOf plus one targetIds entry to supersede that duplicate without calling the classifier.",
15
+ inputSchema: {
16
+ type: "object",
17
+ properties: {
18
+ text: {
19
+ type: "string",
20
+ description: 'The natural-language correction (e.g. "we migrated to MySQL in March").',
21
+ },
22
+ targetIds: {
23
+ type: "array",
24
+ items: { type: "string" },
25
+ description:
26
+ "Optional explicit target memory ids. When omitted, the planner searches. With duplicateOf, pass exactly the duplicate id.",
27
+ },
28
+ duplicateOf: {
29
+ type: "string",
30
+ description:
31
+ "Optional winner memory id. Requires targetIds to be exactly the duplicate. Skips classification and drafts one recoverable supersede (no hard delete) linking the duplicate to this id.",
32
+ },
33
+ sessionKey: { type: "string", description: "Session identifier for namespace resolution." },
34
+ namespace: { type: "string", description: "Optional explicit namespace (validated by policy)." },
35
+ },
36
+ required: ["text"],
37
+ additionalProperties: false,
38
+ },
39
+ },
40
+ {
41
+ name: "engram.memory_correct_apply",
42
+ description:
43
+ "Apply a planned memory correction by planId. Requires confirm: true when correction.applyRequiresConfirm is on (default).",
44
+ inputSchema: {
45
+ type: "object",
46
+ properties: {
47
+ planId: { type: "string", description: "The plan id returned by memory_correct_plan." },
48
+ confirm: { type: "boolean", description: "Must be true to apply (safety guard)." },
49
+ sessionKey: { type: "string", description: "Session identifier for namespace resolution." },
50
+ namespace: { type: "string", description: "Optional explicit namespace (validated by policy)." },
51
+ },
52
+ required: ["planId"],
53
+ additionalProperties: false,
54
+ },
55
+ },
56
+ ];
package/src/access-mcp.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { sharedContextMcpTools } from "./access-mcp-shared-context-tools.js";
2
+ import { correctionMcpTools } from "./access-mcp-correction-tools.js";
2
3
  import { randomUUID } from "node:crypto";
3
4
  import {
4
5
  MCP_ADMIN_OPS_MIGRATED_OPERATIONS,
@@ -2133,56 +2134,10 @@ export class EngramMcpServer {
2133
2134
  this.tools = [...this.tools, ...deltaTools];
2134
2135
  }
2135
2136
  if (this.correctionVisible) {
2136
- // Correction Contract (issue #1580) — one plan/apply pipeline for every
2137
- // memory correction. Both tools dispatch through the boundary operations
2138
- // (memory_correct_plan / memory_correct_apply) which delegate to the
2139
- // CorrectionService.
2140
- const planTool = withToolAliases(
2141
- {
2142
- name: "engram.memory_correct_plan",
2143
- description:
2144
- "Plan a memory correction from a plain-language statement (issue #1580). Returns a CorrectionPlan with a diff preview; apply via memory_correct_apply.",
2145
- inputSchema: {
2146
- type: "object",
2147
- properties: {
2148
- text: {
2149
- type: "string",
2150
- description: 'The natural-language correction (e.g. "we migrated to MySQL in March").',
2151
- },
2152
- targetIds: {
2153
- type: "array",
2154
- items: { type: "string" },
2155
- description: "Optional explicit target memory ids. When omitted, the planner searches.",
2156
- },
2157
- sessionKey: { type: "string", description: "Session identifier for namespace resolution." },
2158
- namespace: { type: "string", description: "Optional explicit namespace (validated by policy)." },
2159
- },
2160
- required: ["text"],
2161
- additionalProperties: false,
2162
- },
2163
- },
2164
- this.emitLegacyTools
2165
- );
2166
- const applyTool = withToolAliases(
2167
- {
2168
- name: "engram.memory_correct_apply",
2169
- description:
2170
- "Apply a planned memory correction by planId (issue #1580). Requires confirm: true when correction.applyRequiresConfirm is on (default).",
2171
- inputSchema: {
2172
- type: "object",
2173
- properties: {
2174
- planId: { type: "string", description: "The plan id returned by memory_correct_plan." },
2175
- confirm: { type: "boolean", description: "Must be true to apply (safety guard, rule 48)." },
2176
- sessionKey: { type: "string", description: "Session identifier for namespace resolution." },
2177
- namespace: { type: "string", description: "Optional explicit namespace (validated by policy)." },
2178
- },
2179
- required: ["planId"],
2180
- additionalProperties: false,
2181
- },
2182
- },
2183
- this.emitLegacyTools
2137
+ const correctionTools = correctionMcpTools.flatMap((tool) =>
2138
+ withToolAliases(tool, this.emitLegacyTools),
2184
2139
  );
2185
- this.tools = [...this.tools, ...planTool, ...applyTool];
2140
+ this.tools = [...this.tools, ...correctionTools];
2186
2141
  }
2187
2142
  if (this.chatVisible) {
2188
2143
  const chatTools = withToolAliases(
@@ -384,6 +384,7 @@ const memoryCorrectPlanSchema = z.preprocess(
384
384
  z.object({
385
385
  text: z.string().trim().min(1).max(10_000),
386
386
  targetIds: z.array(z.string().trim().min(1).max(512)).optional(),
387
+ duplicateOf: z.string().trim().min(1).max(512).optional(),
387
388
  sessionKey: z.string().trim().max(512).optional(),
388
389
  namespace: z.string().trim().max(256).optional(),
389
390
  }),
@@ -392,6 +393,7 @@ const memoryCorrectPlanSchema = z.preprocess(
392
393
  export interface MemoryCorrectPlanInput {
393
394
  text: string;
394
395
  targetIds?: string[];
396
+ duplicateOf?: string;
395
397
  sessionKey?: string;
396
398
  namespace?: string;
397
399
  }
@@ -414,6 +416,7 @@ export const memoryCorrectPlanOperation = defineOperation<
414
416
  {
415
417
  text: input.text,
416
418
  ...(input.targetIds ? { targetIds: input.targetIds } : {}),
419
+ ...(input.duplicateOf ? { duplicateOf: input.duplicateOf } : {}),
417
420
  ...(input.sessionKey ? { sessionKey: input.sessionKey } : {}),
418
421
  ...(input.namespace ? { namespace: input.namespace } : {}),
419
422
  ...(ctx.authenticatedPrincipal ? { principal: ctx.authenticatedPrincipal } : {}),
@@ -3349,24 +3349,6 @@ export class EngramAccessService extends SupportPassportAccessServiceBase {
3349
3349
  return Promise.resolve(false);
3350
3350
  }
3351
3351
  },
3352
- // Wire the orchestrator's extraction LLM so classify+draft actually
3353
- // drafts actions instead of always hitting the deterministic fallback
3354
- // (review thread PG5). The planner's classifyAndDraft already falls back
3355
- // on any LLM outage, so returning null (LLM disabled/cooldown) or a
3356
- // thrown error both degrade safely to the deterministic path (rule 13).
3357
- llmComplete: async ({ system, user }) => {
3358
- const result = await this.orchestrator.localLlm.chatCompletion(
3359
- [
3360
- { role: "system", content: system },
3361
- { role: "user", content: user },
3362
- ],
3363
- { operation: "correction-classify", priority: "background" }
3364
- );
3365
- if (!result) {
3366
- throw new Error("correction classify+draft: local LLM unavailable (disabled or in cooldown)");
3367
- }
3368
- return result.content;
3369
- },
3370
3352
  });
3371
3353
  this._correctionService = service;
3372
3354
  return service;
@@ -3389,11 +3371,20 @@ export class EngramAccessService extends SupportPassportAccessServiceBase {
3389
3371
  }
3390
3372
 
3391
3373
  async correctionPlan(request: CorrectionRequest, opts?: { abortSignal?: AbortSignal }): Promise<CorrectionPlan> {
3374
+ let next = request;
3392
3375
  if (request.targetIds?.some((id) => isHandleToken(id))) {
3393
- const targetIds = request.targetIds.map((id) => this.resolveMemoryIdOrHandleInput(id, request.sessionKey));
3394
- return this.correctionService().plan({ ...request, targetIds }, opts);
3376
+ next = {
3377
+ ...next,
3378
+ targetIds: request.targetIds.map((id) => this.resolveMemoryIdOrHandleInput(id, request.sessionKey)),
3379
+ };
3380
+ }
3381
+ if (request.duplicateOf && isHandleToken(request.duplicateOf)) {
3382
+ next = {
3383
+ ...next,
3384
+ duplicateOf: this.resolveMemoryIdOrHandleInput(request.duplicateOf, request.sessionKey),
3385
+ };
3395
3386
  }
3396
- return this.correctionService().plan(request, opts);
3387
+ return this.correctionService().plan(next, opts);
3397
3388
  }
3398
3389
 
3399
3390
  async correctionApply(
@@ -0,0 +1,361 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
3
+ import http from "node:http";
4
+ import type { AddressInfo } from "node:net";
5
+ import os from "node:os";
6
+ import path from "node:path";
7
+ import test from "node:test";
8
+
9
+ import { calibrationConsolidationInput, readCalibrationIndex, runCalibrationConsolidation } from "./calibration.js";
10
+ import { parseConfig } from "./config.js";
11
+ import { type LoggerBackend, initLogger, resetLogger } from "./logger.js";
12
+ import type { GatewayConfig } from "./types.js";
13
+
14
+ /**
15
+ * Gateway-less calibration (standalone daemon): no gateway model chain, so
16
+ * consolidation must call the configured local LLM and surface skips above
17
+ * debug. These tests drive the real LocalLlmClient against a loopback stub.
18
+ */
19
+
20
+ interface CapturedRequest {
21
+ method: string;
22
+ path: string;
23
+ body: string;
24
+ headers: http.IncomingHttpHeaders;
25
+ }
26
+
27
+ interface StubServer {
28
+ url: string;
29
+ requests: CapturedRequest[];
30
+ close(): Promise<void>;
31
+ }
32
+
33
+ function startServer(handle: (req: CapturedRequest, res: http.ServerResponse) => void): Promise<StubServer> {
34
+ const requests: CapturedRequest[] = [];
35
+ const server = http.createServer((req, res) => {
36
+ const chunks: Buffer[] = [];
37
+ req.on("data", (chunk: Buffer) => chunks.push(chunk));
38
+ req.on("end", () => {
39
+ const captured: CapturedRequest = {
40
+ method: req.method ?? "",
41
+ path: req.url ?? "",
42
+ body: Buffer.concat(chunks).toString("utf8"),
43
+ headers: req.headers,
44
+ };
45
+ requests.push(captured);
46
+ handle(captured, res);
47
+ });
48
+ });
49
+ return new Promise((resolve) => {
50
+ server.listen(0, "127.0.0.1", () => {
51
+ const { port } = server.address() as AddressInfo;
52
+ resolve({
53
+ url: `http://127.0.0.1:${port}/v1`,
54
+ requests,
55
+ close: () => new Promise((done) => server.close(() => done())),
56
+ });
57
+ });
58
+ });
59
+ }
60
+
61
+ function json(res: http.ServerResponse, body: unknown): void {
62
+ res.writeHead(200, { "content-type": "application/json" });
63
+ res.end(JSON.stringify(body));
64
+ }
65
+
66
+ function ruleJson(condition: string): string {
67
+ return JSON.stringify({
68
+ rules: [
69
+ {
70
+ ruleType: "scope_boundary",
71
+ condition,
72
+ modelTendency: "It expands the task",
73
+ userExpectation: "Stay inside the request",
74
+ calibration: "Ask before widening the task.",
75
+ confidence: 0.8,
76
+ evidenceIds: ["correction-1", "correction-2"],
77
+ },
78
+ ],
79
+ });
80
+ }
81
+
82
+ function chatCompletionBody(content: string): unknown {
83
+ return { choices: [{ message: { content } }] };
84
+ }
85
+
86
+ async function writeCorrections(memoryDir: string, count: number): Promise<void> {
87
+ const dir = path.join(memoryDir, "corrections");
88
+ await mkdir(dir, { recursive: true });
89
+ for (let i = 1; i <= count; i += 1) {
90
+ const body = [
91
+ "---",
92
+ `id: correction-${i}`,
93
+ "confidence: 0.9",
94
+ "---",
95
+ "",
96
+ `Correction ${i}: the model assumed a broader scope than the user stated for task ${i}.`,
97
+ "",
98
+ ].join("\n");
99
+ await writeFile(path.join(dir, `correction-${i}.md`), body, "utf8");
100
+ }
101
+ }
102
+
103
+ function captureLogs(debug = true): { lines: Array<{ level: string; msg: string }> } {
104
+ const lines: Array<{ level: string; msg: string }> = [];
105
+ const backend: LoggerBackend = {
106
+ info(msg) {
107
+ lines.push({ level: "info", msg: String(msg) });
108
+ },
109
+ warn(msg) {
110
+ lines.push({ level: "warn", msg: String(msg) });
111
+ },
112
+ error(msg) {
113
+ lines.push({ level: "error", msg: String(msg) });
114
+ },
115
+ debug(msg) {
116
+ lines.push({ level: "debug", msg: String(msg) });
117
+ },
118
+ };
119
+ initLogger(backend, debug);
120
+ return { lines };
121
+ }
122
+
123
+ function gatewayConfig(baseUrl: string): GatewayConfig {
124
+ return {
125
+ agents: { defaults: { model: { primary: "stub/calibration-gateway" } } },
126
+ models: {
127
+ providers: {
128
+ stub: {
129
+ baseUrl,
130
+ api: "openai-completions",
131
+ apiKey: "gateway-test-key",
132
+ models: [],
133
+ },
134
+ },
135
+ },
136
+ };
137
+ }
138
+
139
+ test.describe("gateway-less calibration", { concurrency: false }, () => {
140
+ test("plugin mode supplies localLlm settings and leaves the gateway chain unset", () => {
141
+ const config = parseConfig({
142
+ memoryDir: "/tmp/remnic-cal-plugin",
143
+ modelSource: "plugin",
144
+ localLlmEnabled: true,
145
+ localLlmModel: "daemon-calibration-model",
146
+ gatewayAgentId: "ignored-in-plugin-mode",
147
+ taskModelChain: { primary: "stub/ignored" },
148
+ });
149
+ const input = calibrationConsolidationInput(config);
150
+ assert.equal(input.pluginConfig, config);
151
+ assert.equal(input.pluginConfig.localLlmModel, "daemon-calibration-model");
152
+ assert.equal(input.pluginConfig.localLlmEnabled, true);
153
+ assert.equal(input.gatewayAgentId, undefined);
154
+ assert.equal(input.modelChain, undefined);
155
+ assert.equal(input.gatewayConfig, undefined);
156
+ });
157
+
158
+ test("gateway mode keeps the agent and task chain on the calibration input", () => {
159
+ const config = parseConfig({
160
+ memoryDir: "/tmp/remnic-cal-gateway-input",
161
+ modelSource: "gateway",
162
+ gatewayAgentId: "agent-a",
163
+ taskModelChain: { primary: "stub/calibration-gateway" },
164
+ });
165
+ const input = calibrationConsolidationInput(config);
166
+ assert.equal(input.gatewayAgentId, "agent-a");
167
+ assert.deepEqual(input.modelChain, { primary: "stub/calibration-gateway" });
168
+ assert.equal(input.pluginConfig, config);
169
+ });
170
+
171
+ test("uses the daemon localLlm* settings when no gateway model chain exists", async () => {
172
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-cal-local-"));
173
+ const logs = captureLogs();
174
+ const server = await startServer((req, res) => {
175
+ if (req.method === "GET" && req.path === "/") {
176
+ res.writeHead(200, { "content-type": "text/plain" });
177
+ res.end("Ollama is running");
178
+ return;
179
+ }
180
+ if (req.method === "POST" && req.path === "/v1/chat/completions") {
181
+ json(res, chatCompletionBody(ruleJson("When the daemon has no gateway chain")));
182
+ return;
183
+ }
184
+ res.writeHead(404);
185
+ res.end();
186
+ });
187
+ try {
188
+ await writeCorrections(memoryDir, 3);
189
+ const pluginConfig = parseConfig({
190
+ memoryDir,
191
+ modelSource: "plugin",
192
+ localLlmEnabled: true,
193
+ localLlmUrl: server.url,
194
+ localLlmModel: "daemon-calibration-model",
195
+ localLlmHeaders: { "X-Remnic-Local": "calibration" },
196
+ localLlmApiKey: "local-test-key",
197
+ localLlmAuthHeader: true,
198
+ });
199
+ const rules = await runCalibrationConsolidation({
200
+ memoryDir,
201
+ workspaceDir: memoryDir,
202
+ pluginConfig,
203
+ });
204
+
205
+ assert.equal(rules.length, 1);
206
+ assert.equal(rules[0]?.condition, "When the daemon has no gateway chain");
207
+ const stored = await readCalibrationIndex(memoryDir);
208
+ assert.equal(stored.rules.length, 1);
209
+ assert.equal(stored.totalCorrectionsAnalyzed, 3);
210
+
211
+ const completion = server.requests.find((req) => req.method === "POST" && req.path === "/v1/chat/completions");
212
+ assert.ok(completion, "local LLM chat completion was not called");
213
+ const body = JSON.parse(completion.body) as { model?: string };
214
+ assert.equal(body.model, "daemon-calibration-model");
215
+ assert.equal(completion.headers["x-remnic-local"], "calibration");
216
+ assert.equal(completion.headers.authorization, "Bearer local-test-key");
217
+ assert.ok(
218
+ logs.lines.some((line) => line.level === "info" && line.msg.includes("model=daemon-calibration-model"))
219
+ );
220
+ assert.equal(
221
+ logs.lines.some((line) => line.msg.includes("no LLM available")),
222
+ false
223
+ );
224
+ } finally {
225
+ resetLogger();
226
+ await server.close();
227
+ await rm(memoryDir, { recursive: true, force: true });
228
+ }
229
+ });
230
+
231
+ test("warns and skips when neither a gateway chain nor a local LLM is configured", async () => {
232
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-cal-skip-"));
233
+ const logs = captureLogs(true);
234
+ try {
235
+ await writeCorrections(memoryDir, 3);
236
+ const pluginConfig = parseConfig({
237
+ memoryDir,
238
+ modelSource: "plugin",
239
+ localLlmEnabled: false,
240
+ });
241
+ const rules = await runCalibrationConsolidation({
242
+ memoryDir,
243
+ workspaceDir: memoryDir,
244
+ pluginConfig,
245
+ });
246
+ assert.deepEqual(rules, []);
247
+ const stored = await readCalibrationIndex(memoryDir);
248
+ assert.equal(stored.rules.length, 0);
249
+ assert.ok(logs.lines.some((line) => line.level === "warn" && line.msg.includes("no LLM available")));
250
+ assert.equal(
251
+ logs.lines.some((line) => line.level === "debug" && line.msg.includes("no LLM available")),
252
+ false
253
+ );
254
+ } finally {
255
+ resetLogger();
256
+ await rm(memoryDir, { recursive: true, force: true });
257
+ }
258
+ });
259
+
260
+ test("logs a short correction set at info", async () => {
261
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-cal-few-"));
262
+ const logs = captureLogs(true);
263
+ const server = await startServer((req, res) => {
264
+ if (req.method === "GET" && req.path === "/") {
265
+ res.writeHead(200, { "content-type": "text/plain" });
266
+ res.end("Ollama is running");
267
+ return;
268
+ }
269
+ res.writeHead(500);
270
+ res.end();
271
+ });
272
+ try {
273
+ await writeCorrections(memoryDir, 1);
274
+ const pluginConfig = parseConfig({
275
+ memoryDir,
276
+ modelSource: "plugin",
277
+ localLlmEnabled: true,
278
+ localLlmUrl: server.url,
279
+ localLlmModel: "daemon-calibration-model",
280
+ });
281
+ const rules = await runCalibrationConsolidation({
282
+ memoryDir,
283
+ workspaceDir: memoryDir,
284
+ pluginConfig,
285
+ });
286
+ assert.deepEqual(rules, []);
287
+ assert.equal(
288
+ server.requests.some((req) => req.method === "POST"),
289
+ false,
290
+ "synthesis must not call the LLM before the correction minimum"
291
+ );
292
+ assert.ok(logs.lines.some((line) => line.level === "info" && line.msg.includes("need at least 3")));
293
+ assert.equal(
294
+ logs.lines.some((line) => line.level === "debug" && line.msg.includes("need at least 3")),
295
+ false
296
+ );
297
+ } finally {
298
+ resetLogger();
299
+ await server.close();
300
+ await rm(memoryDir, { recursive: true, force: true });
301
+ }
302
+ });
303
+
304
+ test("keeps the gateway chain when one is configured", async () => {
305
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-cal-gateway-"));
306
+ const logs = captureLogs();
307
+ const local = await startServer((req, res) => {
308
+ if (req.method === "GET" && req.path === "/") {
309
+ res.writeHead(200, { "content-type": "text/plain" });
310
+ res.end("Ollama is running");
311
+ return;
312
+ }
313
+ if (req.method === "POST") {
314
+ json(res, chatCompletionBody(ruleJson("When the local model was used by mistake")));
315
+ return;
316
+ }
317
+ res.writeHead(404);
318
+ res.end();
319
+ });
320
+ const gateway = await startServer((req, res) => {
321
+ if (req.method === "POST" && req.path === "/v1/chat/completions") {
322
+ json(res, chatCompletionBody(ruleJson("When the gateway chain is present")));
323
+ return;
324
+ }
325
+ res.writeHead(404);
326
+ res.end();
327
+ });
328
+ try {
329
+ await writeCorrections(memoryDir, 3);
330
+ const pluginConfig = parseConfig({
331
+ memoryDir,
332
+ modelSource: "plugin",
333
+ localLlmEnabled: true,
334
+ localLlmUrl: local.url,
335
+ localLlmModel: "daemon-calibration-model",
336
+ });
337
+ const rules = await runCalibrationConsolidation({
338
+ memoryDir,
339
+ workspaceDir: memoryDir,
340
+ gatewayConfig: gatewayConfig(gateway.url),
341
+ pluginConfig,
342
+ });
343
+ assert.equal(rules.length, 1);
344
+ assert.equal(rules[0]?.condition, "When the gateway chain is present");
345
+ assert.equal(local.requests.length, 0);
346
+ assert.equal(
347
+ gateway.requests.filter((req) => req.method === "POST" && req.path === "/v1/chat/completions").length,
348
+ 1
349
+ );
350
+ assert.equal(
351
+ logs.lines.some((line) => line.msg.includes("using configured local LLM")),
352
+ false
353
+ );
354
+ } finally {
355
+ resetLogger();
356
+ await local.close();
357
+ await gateway.close();
358
+ await rm(memoryDir, { recursive: true, force: true });
359
+ }
360
+ });
361
+ });