@remnic/core 10.0.3 → 10.0.5

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 (410) hide show
  1. package/dist/access-admin-ops-surface.js +62 -62
  2. package/dist/access-authorization-probe.js +63 -63
  3. package/dist/access-boundary.js +62 -62
  4. package/dist/access-cli.d.ts.map +1 -1
  5. package/dist/access-cli.js +146 -139
  6. package/dist/access-cli.js.map +1 -1
  7. package/dist/access-extraction-force-flush.js +62 -62
  8. package/dist/access-http-briefing.js +63 -63
  9. package/dist/access-http-query.js +64 -64
  10. package/dist/access-http.d.ts.map +1 -1
  11. package/dist/access-http.js +86 -85
  12. package/dist/access-identity-continuity-surface.js +62 -62
  13. package/dist/access-lcm-surface.js +62 -62
  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 +76 -75
  19. package/dist/access-namespace-preflight.js +62 -62
  20. package/dist/access-observe-write-category.js +5 -5
  21. package/dist/access-observe-write-surface.js +62 -62
  22. package/dist/access-offline-manifest.js +8 -8
  23. package/dist/access-operations-batch.js +65 -65
  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 +72 -72
  27. package/dist/access-recall-concurrency.js +62 -62
  28. package/dist/access-recall-diagnostics.js +2 -2
  29. package/dist/access-recall-response.js +62 -62
  30. package/dist/access-recall-surface.js +62 -62
  31. package/dist/access-service.d.ts.map +1 -1
  32. package/dist/access-service.js +62 -62
  33. package/dist/access-surface-catalog.js +64 -64
  34. package/dist/active-recall.js +31 -30
  35. package/dist/active-recall.js.map +1 -1
  36. package/dist/adapters/index.js +7 -7
  37. package/dist/adapters/registry.js +3 -3
  38. package/dist/{auto-sync-PAWBFQWL.js → auto-sync-FN7E3OTO.js} +6 -6
  39. package/dist/briefing.js +26 -26
  40. package/dist/buffer.js +8 -8
  41. package/dist/causal-consolidation.js +26 -26
  42. package/dist/{chunk-YQSWNFZE.js → chunk-2CP577K6.js} +2 -2
  43. package/dist/{chunk-6JIHC5AS.js → chunk-37HCPM56.js} +78 -67
  44. package/dist/chunk-37HCPM56.js.map +1 -0
  45. package/dist/{chunk-345MEMRU.js → chunk-3OUGSOJP.js} +2 -2
  46. package/dist/{chunk-CHI37EHP.js → chunk-4CWUZBPF.js} +8 -6
  47. package/dist/chunk-4CWUZBPF.js.map +1 -0
  48. package/dist/{chunk-G2STTRYB.js → chunk-4D2YH47Z.js} +2 -2
  49. package/dist/{chunk-IXYIW3MZ.js → chunk-4K2H4KTR.js} +5 -5
  50. package/dist/{chunk-UVVSOSHL.js → chunk-52CQKST6.js} +53 -53
  51. package/dist/{chunk-H7BZBSP6.js → chunk-52II7PB6.js} +4 -4
  52. package/dist/{chunk-BLHPADUX.js → chunk-5BPXMAUP.js} +22 -78
  53. package/dist/chunk-5BPXMAUP.js.map +1 -0
  54. package/dist/{chunk-ZS7RK6H6.js → chunk-5BY3IA2U.js} +16 -53
  55. package/dist/chunk-5BY3IA2U.js.map +1 -0
  56. package/dist/{chunk-2QZFZNXO.js → chunk-5CQO6LVM.js} +2 -2
  57. package/dist/{chunk-NLDMTXWV.js → chunk-5LNPW7NE.js} +2 -2
  58. package/dist/{chunk-WHTMVTU4.js → chunk-5X4ZJESN.js} +2 -2
  59. package/dist/{chunk-WI37MWRZ.js → chunk-63UNVGZW.js} +5 -5
  60. package/dist/{chunk-G3T6YMBO.js → chunk-64S6X4IU.js} +7 -7
  61. package/dist/{chunk-CFCEJNOE.js → chunk-6KHF6PAB.js} +2 -2
  62. package/dist/{chunk-5GINB5LC.js → chunk-6MKLMEBE.js} +1 -1
  63. package/dist/{chunk-5GINB5LC.js.map → chunk-6MKLMEBE.js.map} +1 -1
  64. package/dist/{chunk-35A7GCHG.js → chunk-6T42L5FI.js} +2 -2
  65. package/dist/{chunk-JDOBIFBK.js → chunk-7CXL25AA.js} +2 -2
  66. package/dist/{chunk-YLBE6PLR.js → chunk-7IGMKQWK.js} +2 -2
  67. package/dist/{chunk-CSLK7BN6.js → chunk-7LYRFI5A.js} +2 -2
  68. package/dist/{chunk-IB63OVUB.js → chunk-AJ6ORARY.js} +6 -6
  69. package/dist/{chunk-CGOKNQDK.js → chunk-ANZXWOYD.js} +2 -2
  70. package/dist/{chunk-5QXMESS7.js → chunk-AXC7UTLM.js} +2 -2
  71. package/dist/{chunk-RFIJRZ4K.js → chunk-B657ZGDH.js} +2 -2
  72. package/dist/{chunk-AW4QUFQF.js → chunk-BTNNL7ZW.js} +3 -2
  73. package/dist/{chunk-AW4QUFQF.js.map → chunk-BTNNL7ZW.js.map} +1 -1
  74. package/dist/{chunk-MY7B3HYC.js → chunk-CLCMDKTB.js} +2 -2
  75. package/dist/{chunk-QPWUG2S3.js → chunk-CYEK6Z3P.js} +4 -4
  76. package/dist/{chunk-IHCYCE6D.js → chunk-DJMVSUPK.js} +4 -4
  77. package/dist/{chunk-HP4CU2ZA.js → chunk-DST7HRSG.js} +2 -2
  78. package/dist/{chunk-IIFFC3RL.js → chunk-EDR6CVWZ.js} +2 -2
  79. package/dist/{chunk-AUWMKDKL.js → chunk-EMVJU7WC.js} +2 -2
  80. package/dist/{chunk-O6AYMAPJ.js → chunk-ENNC3RZ6.js} +2 -2
  81. package/dist/{chunk-2JRB3Z5N.js → chunk-EP5WXE2G.js} +3 -3
  82. package/dist/{chunk-QLK3SL7Q.js → chunk-ETSJD5IV.js} +131 -12
  83. package/dist/chunk-ETSJD5IV.js.map +1 -0
  84. package/dist/chunk-EWMRFW3F.js +38 -0
  85. package/dist/chunk-EWMRFW3F.js.map +1 -0
  86. package/dist/{chunk-I2KEH2LS.js → chunk-FC6RCOIU.js} +2 -2
  87. package/dist/{chunk-OZVHLBH6.js → chunk-FLV7M7CT.js} +2 -2
  88. package/dist/{chunk-Q6GI7HEC.js → chunk-FRH2UC7Q.js} +2 -2
  89. package/dist/{chunk-3SL52JKD.js → chunk-FZVROZKC.js} +5 -5
  90. package/dist/{chunk-2EZB2DL5.js → chunk-GE6HETIF.js} +3 -3
  91. package/dist/{chunk-2DHJTJIY.js → chunk-GEGUBU3D.js} +2 -2
  92. package/dist/{chunk-6AA7XFMO.js → chunk-GF4H3B3R.js} +8 -8
  93. package/dist/{chunk-NGOZH7R5.js → chunk-GVUW5XK5.js} +311 -142
  94. package/dist/chunk-GVUW5XK5.js.map +1 -0
  95. package/dist/{chunk-ME2LMFHF.js → chunk-HPAOMYKM.js} +92 -92
  96. package/dist/{chunk-4NRZ7BRS.js → chunk-I25RINZK.js} +2 -2
  97. package/dist/{chunk-XG4XXI66.js → chunk-I25RQA65.js} +2 -2
  98. package/dist/chunk-I56MTWE7.js +80 -0
  99. package/dist/chunk-I56MTWE7.js.map +1 -0
  100. package/dist/{chunk-EJJMDLVB.js → chunk-IJ4FEGZR.js} +2 -2
  101. package/dist/{chunk-EUL3S3LE.js → chunk-J5WI4QRP.js} +4 -4
  102. package/dist/{chunk-LMMC55WM.js → chunk-JA54KIYD.js} +4 -4
  103. package/dist/{chunk-5FLFPTDK.js → chunk-JA72X46J.js} +2 -2
  104. package/dist/{chunk-6GU3TNJJ.js → chunk-JMF5Q4JY.js} +246 -49
  105. package/dist/chunk-JMF5Q4JY.js.map +1 -0
  106. package/dist/{chunk-IDSSDL4W.js → chunk-JXSYWTFK.js} +21 -4
  107. package/dist/{chunk-IDSSDL4W.js.map → chunk-JXSYWTFK.js.map} +1 -1
  108. package/dist/{chunk-NLYOOGZ7.js → chunk-KE5PWLI6.js} +3 -3
  109. package/dist/chunk-KIUVEMBI.js +49 -0
  110. package/dist/chunk-KIUVEMBI.js.map +1 -0
  111. package/dist/{chunk-4DDIWRT7.js → chunk-KQI2PKYP.js} +2 -2
  112. package/dist/{chunk-Y2ADLCB3.js → chunk-KSUOM3YU.js} +9 -9
  113. package/dist/{chunk-BTOHGPHC.js → chunk-KZEMFD6E.js} +3 -3
  114. package/dist/{chunk-NXVD46HT.js → chunk-L2VS27PZ.js} +4 -4
  115. package/dist/{chunk-MYYHACOW.js → chunk-L34W4W4I.js} +2 -2
  116. package/dist/{chunk-KRTIHXWC.js → chunk-L3PWXD73.js} +2 -2
  117. package/dist/{chunk-NQHSXHJP.js → chunk-L6GNS5GB.js} +2 -2
  118. package/dist/{chunk-44PBRUXB.js → chunk-M47Q6566.js} +2 -2
  119. package/dist/{chunk-E7FN5N5E.js → chunk-MQNOIH2C.js} +7 -7
  120. package/dist/{chunk-IDSZNHIP.js → chunk-MULNBSPP.js} +4 -4
  121. package/dist/{chunk-RKSGUMVL.js → chunk-NBQIFCJE.js} +2 -2
  122. package/dist/{chunk-I6WFBNA4.js → chunk-NHSVW7UT.js} +15 -15
  123. package/dist/{chunk-32HO62I4.js → chunk-OFEHITAZ.js} +2 -2
  124. package/dist/{chunk-PCO3P7WJ.js → chunk-OGVJXIHX.js} +2 -2
  125. package/dist/{chunk-RYN4AIRM.js → chunk-OZABSIOV.js} +4 -4
  126. package/dist/{chunk-HO7IQP7G.js → chunk-QMFKW4OU.js} +3 -3
  127. package/dist/{chunk-34G4U6HS.js → chunk-QRZMJ2WN.js} +2 -2
  128. package/dist/{chunk-RKDSELA3.js → chunk-QWWMS4AE.js} +1 -1
  129. package/dist/chunk-QWWMS4AE.js.map +1 -0
  130. package/dist/{chunk-GUD6FYQG.js → chunk-RSMIRLKM.js} +2 -2
  131. package/dist/{chunk-TNVZCLJU.js → chunk-S5TGRZWY.js} +2 -2
  132. package/dist/{chunk-O24EWFPQ.js → chunk-SNXBXYUE.js} +3 -3
  133. package/dist/{chunk-KW77LGDX.js → chunk-SUAX5JWY.js} +2 -2
  134. package/dist/{chunk-PNFWIB4X.js → chunk-SWRN6U7Q.js} +2 -2
  135. package/dist/{chunk-X6X4UASX.js → chunk-SZXPKUMN.js} +232 -83
  136. package/dist/chunk-SZXPKUMN.js.map +1 -0
  137. package/dist/{chunk-R4NGLTUS.js → chunk-TDMNADPI.js} +2 -2
  138. package/dist/{chunk-QCP75JGO.js → chunk-TFA6HPOA.js} +5 -5
  139. package/dist/{chunk-LI76ESJL.js → chunk-TZQOCQXB.js} +2 -2
  140. package/dist/{chunk-OV7EYM6M.js → chunk-U6OXJQEH.js} +12 -12
  141. package/dist/{chunk-OWHPA4JS.js → chunk-V32NHP2R.js} +2 -2
  142. package/dist/{chunk-JSUA4YFE.js → chunk-WG4TMZJ2.js} +2 -2
  143. package/dist/{chunk-DQTCGVFK.js → chunk-WHCD4Z3D.js} +2 -2
  144. package/dist/{chunk-GBA4N2SG.js → chunk-WRICYGYG.js} +27 -26
  145. package/dist/chunk-WRICYGYG.js.map +1 -0
  146. package/dist/{chunk-KATEWTQJ.js → chunk-X7J56GPG.js} +2 -2
  147. package/dist/{chunk-7KICGR5W.js → chunk-XPRAOTJY.js} +2 -2
  148. package/dist/{chunk-2NV7MMMM.js → chunk-XQSJXNWC.js} +2 -2
  149. package/dist/{chunk-22DYJLFN.js → chunk-XVC7JU2N.js} +39 -18
  150. package/dist/chunk-XVC7JU2N.js.map +1 -0
  151. package/dist/{chunk-RXT6UB25.js → chunk-YJAOYEWI.js} +2 -2
  152. package/dist/{chunk-L6DJJEFW.js → chunk-YJEUHGZZ.js} +2 -2
  153. package/dist/{chunk-AXFWMRKI.js → chunk-YQJCWR4R.js} +2 -2
  154. package/dist/cli.js +110 -108
  155. package/dist/coding/export-okf-codegraph.js +25 -25
  156. package/dist/compounding/engine.js +25 -25
  157. package/dist/config.d.ts.map +1 -1
  158. package/dist/config.js +31 -30
  159. package/dist/connectors/codex-materialize-runner.js +25 -25
  160. package/dist/connectors/index.js +25 -25
  161. package/dist/consolidation-provenance-check.js +2 -2
  162. package/dist/contradiction/index.js +7 -7
  163. package/dist/convergence-refresh.js +27 -27
  164. package/dist/conversation-index/backend.js +2 -2
  165. package/dist/corpus-read-cancellation.d.ts +7 -0
  166. package/dist/corpus-read-cancellation.d.ts.map +1 -1
  167. package/dist/corpus-read-cancellation.js +1 -1
  168. package/dist/corpus-watermark.js +27 -27
  169. package/dist/correction/correction-access-wiring.d.ts +6 -6
  170. package/dist/correction/correction-access-wiring.d.ts.map +1 -1
  171. package/dist/correction/correction-classify.d.ts +25 -0
  172. package/dist/correction/correction-classify.d.ts.map +1 -0
  173. package/dist/correction/correction-config.d.ts +11 -0
  174. package/dist/correction/correction-config.d.ts.map +1 -0
  175. package/dist/correction/correction-contract.d.ts +13 -1
  176. package/dist/correction/correction-contract.d.ts.map +1 -1
  177. package/dist/correction/correction-executor.d.ts +2 -0
  178. package/dist/correction/correction-executor.d.ts.map +1 -1
  179. package/dist/correction/correction-planner.d.ts +24 -3
  180. package/dist/correction/correction-planner.d.ts.map +1 -1
  181. package/dist/directory-sidecars.js +2 -2
  182. package/dist/entity-retrieval.js +25 -25
  183. package/dist/event-order-recall.js +2 -2
  184. package/dist/explicit-capture.js +2 -2
  185. package/dist/external-wiki-access.js +63 -63
  186. package/dist/extraction-span-materialize.js +2 -2
  187. package/dist/extraction.js +34 -34
  188. package/dist/harmonic-retrieval.js +25 -25
  189. package/dist/index.js +221 -218
  190. package/dist/lcm/index.js +3 -3
  191. package/dist/local-llm.d.ts.map +1 -1
  192. package/dist/local-llm.js +1 -1
  193. package/dist/location/index.js +28 -28
  194. package/dist/maintenance/archive-observations.js +2 -2
  195. package/dist/maintenance/atomic-file.d.ts +1 -1
  196. package/dist/maintenance/atomic-file.d.ts.map +1 -1
  197. package/dist/maintenance/atomic-file.js +1 -1
  198. package/dist/maintenance/memory-governance.js +25 -25
  199. package/dist/maintenance/migrate-observations.js +3 -3
  200. package/dist/maintenance/observation-ledger-utils.js +2 -2
  201. package/dist/maintenance/projection-rebuild-abort.d.ts +17 -0
  202. package/dist/maintenance/projection-rebuild-abort.d.ts.map +1 -0
  203. package/dist/maintenance/projection-timeline.d.ts +19 -0
  204. package/dist/maintenance/projection-timeline.d.ts.map +1 -0
  205. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +25 -25
  206. package/dist/maintenance/rebuild-memory-projection-runner.d.ts +8 -0
  207. package/dist/maintenance/rebuild-memory-projection-runner.d.ts.map +1 -0
  208. package/dist/maintenance/rebuild-memory-projection-runner.js +113 -0
  209. package/dist/maintenance/rebuild-memory-projection-runner.js.map +1 -0
  210. package/dist/maintenance/rebuild-memory-projection.d.ts +8 -0
  211. package/dist/maintenance/rebuild-memory-projection.d.ts.map +1 -1
  212. package/dist/maintenance/rebuild-memory-projection.js +26 -26
  213. package/dist/maintenance/rebuild-observations.js +3 -3
  214. package/dist/memory-browse-operations.js +63 -63
  215. package/dist/memory-browse.js +7 -7
  216. package/dist/memory-subject.js +1 -1
  217. package/dist/namespaces/migrate.js +26 -26
  218. package/dist/namespaces/search.js +10 -10
  219. package/dist/namespaces/storage.js +25 -25
  220. package/dist/offline-sync-impression-drain.js +3 -3
  221. package/dist/operator-doctor-corpus.js +28 -28
  222. package/dist/operator-toolkit.js +41 -40
  223. package/dist/orchestration/full-corpus-heap-guard.d.ts +25 -0
  224. package/dist/orchestration/full-corpus-heap-guard.d.ts.map +1 -0
  225. package/dist/orchestration/maintenance.d.ts.map +1 -1
  226. package/dist/orchestration/maintenance.js +38 -36
  227. package/dist/orchestration/projection-rebuild-schedule.d.ts +40 -0
  228. package/dist/orchestration/projection-rebuild-schedule.d.ts.map +1 -1
  229. package/dist/orchestration/session-context.d.ts.map +1 -1
  230. package/dist/orchestrator.js +142 -139
  231. package/dist/projection-rebuild-config.d.ts +52 -0
  232. package/dist/projection-rebuild-config.d.ts.map +1 -0
  233. package/dist/projection-rebuild-config.js +14 -0
  234. package/dist/projection-rebuild-config.js.map +1 -0
  235. package/dist/qmd.js +4 -4
  236. package/dist/recall-explain-renderer.js +3 -3
  237. package/dist/recall-navigate-access.js +2 -2
  238. package/dist/recall-navigate-disclosure.js +2 -2
  239. package/dist/recall-navigate.js +2 -2
  240. package/dist/recall-navigation-operations.js +63 -63
  241. package/dist/recall-navigation.js +62 -62
  242. package/dist/recall-state-view-admission.js +7 -7
  243. package/dist/recall-state.js +2 -2
  244. package/dist/recall-why-service.js +8 -8
  245. package/dist/recall-xray-cli.js +4 -4
  246. package/dist/recall-xray-renderer.js +3 -3
  247. package/dist/recall-xray.js +2 -2
  248. package/dist/reconcile/manifest.js +3 -3
  249. package/dist/relevance.js +2 -2
  250. package/dist/{resolution-RSG4HZTL.js → resolution-TV3F7ZTH.js} +7 -7
  251. package/dist/resolution-TV3F7ZTH.js.map +1 -0
  252. package/dist/resume-bundles.js +32 -31
  253. package/dist/salvage-envelope.js +1 -1
  254. package/dist/search/factory.js +9 -9
  255. package/dist/search/index.js +17 -17
  256. package/dist/semantic-consolidation.js +31 -31
  257. package/dist/semantic-rule-promotion.js +25 -25
  258. package/dist/semantic-rule-verifier.js +25 -25
  259. package/dist/source-agent-qualifier.js +29 -29
  260. package/dist/standup/build.js +25 -25
  261. package/dist/storage/memory-lifecycle-ledger-access.d.ts +5 -3
  262. package/dist/storage/memory-lifecycle-ledger-access.d.ts.map +1 -1
  263. package/dist/storage/memory-read-store.d.ts +2 -2
  264. package/dist/storage/memory-read-store.d.ts.map +1 -1
  265. package/dist/storage.d.ts +4 -2
  266. package/dist/storage.d.ts.map +1 -1
  267. package/dist/storage.js +24 -24
  268. package/dist/subject-config.js +1 -1
  269. package/dist/summarizer.js +3 -3
  270. package/dist/support-passport/index.js +78 -78
  271. package/dist/transfer/export-okf.js +26 -26
  272. package/dist/types.d.ts +8 -40
  273. package/dist/types.d.ts.map +1 -1
  274. package/dist/types.js +1 -1
  275. package/dist/verified-recall.js +25 -25
  276. package/dist/who-knows-cli.js +26 -26
  277. package/dist/who-knows.js +25 -25
  278. package/dist/write-envelope.js +1 -1
  279. package/package.json +2 -2
  280. package/src/access-cli.ts +4 -0
  281. package/src/access-http.ts +1 -0
  282. package/src/access-mcp-correction-tools.ts +56 -0
  283. package/src/access-mcp.ts +4 -49
  284. package/src/access-operations.ts +3 -0
  285. package/src/access-service.ts +12 -21
  286. package/src/config.test.ts +35 -0
  287. package/src/config.ts +6 -65
  288. package/src/corpus-read-cancellation.ts +7 -0
  289. package/src/correction/correction-access-wiring.ts +56 -16
  290. package/src/correction/correction-classify.test.ts +185 -0
  291. package/src/correction/correction-classify.ts +157 -0
  292. package/src/correction/correction-config.ts +93 -0
  293. package/src/correction/correction-contract.ts +61 -14
  294. package/src/correction/correction-executor.test.ts +102 -2
  295. package/src/correction/correction-executor.ts +19 -1
  296. package/src/correction/correction-planner.test.ts +141 -0
  297. package/src/correction/correction-planner.ts +117 -11
  298. package/src/correction/correction-surfaces.test.ts +22 -1
  299. package/src/local-llm-qos.test.ts +36 -0
  300. package/src/local-llm.ts +20 -3
  301. package/src/maintenance/atomic-file.ts +2 -0
  302. package/src/maintenance/projection-rebuild-abort.ts +40 -0
  303. package/src/maintenance/projection-timeline.ts +209 -0
  304. package/src/maintenance/rebuild-memory-projection-runner.ts +94 -0
  305. package/src/maintenance/rebuild-memory-projection.memory.test.ts +159 -0
  306. package/src/maintenance/rebuild-memory-projection.retain-raw.test.ts +202 -0
  307. package/src/maintenance/rebuild-memory-projection.ts +63 -91
  308. package/src/orchestration/full-corpus-heap-guard.ts +38 -0
  309. package/src/orchestration/maintenance.test.ts +2 -2
  310. package/src/orchestration/maintenance.ts +2 -0
  311. package/src/orchestration/projection-rebuild-schedule.test.ts +498 -0
  312. package/src/orchestration/projection-rebuild-schedule.ts +280 -9
  313. package/src/orchestration/session-context.ts +0 -15
  314. package/src/projection-rebuild-config.ts +82 -0
  315. package/src/recall-handles-wiring.test.ts +24 -0
  316. package/src/storage/memory-lifecycle-ledger-access.ts +61 -33
  317. package/src/storage/memory-read-store.ts +10 -4
  318. package/src/storage.ts +45 -33
  319. package/src/types.ts +8 -41
  320. package/dist/chunk-22DYJLFN.js.map +0 -1
  321. package/dist/chunk-6GU3TNJJ.js.map +0 -1
  322. package/dist/chunk-6JIHC5AS.js.map +0 -1
  323. package/dist/chunk-BLHPADUX.js.map +0 -1
  324. package/dist/chunk-CHI37EHP.js.map +0 -1
  325. package/dist/chunk-GBA4N2SG.js.map +0 -1
  326. package/dist/chunk-NGOZH7R5.js.map +0 -1
  327. package/dist/chunk-QLK3SL7Q.js.map +0 -1
  328. package/dist/chunk-RKDSELA3.js.map +0 -1
  329. package/dist/chunk-X6X4UASX.js.map +0 -1
  330. package/dist/chunk-ZS7RK6H6.js.map +0 -1
  331. /package/dist/{resolution-RSG4HZTL.js.map → access-mcp-correction-tools.js.map} +0 -0
  332. /package/dist/{auto-sync-PAWBFQWL.js.map → auto-sync-FN7E3OTO.js.map} +0 -0
  333. /package/dist/{chunk-YQSWNFZE.js.map → chunk-2CP577K6.js.map} +0 -0
  334. /package/dist/{chunk-345MEMRU.js.map → chunk-3OUGSOJP.js.map} +0 -0
  335. /package/dist/{chunk-G2STTRYB.js.map → chunk-4D2YH47Z.js.map} +0 -0
  336. /package/dist/{chunk-IXYIW3MZ.js.map → chunk-4K2H4KTR.js.map} +0 -0
  337. /package/dist/{chunk-UVVSOSHL.js.map → chunk-52CQKST6.js.map} +0 -0
  338. /package/dist/{chunk-H7BZBSP6.js.map → chunk-52II7PB6.js.map} +0 -0
  339. /package/dist/{chunk-2QZFZNXO.js.map → chunk-5CQO6LVM.js.map} +0 -0
  340. /package/dist/{chunk-NLDMTXWV.js.map → chunk-5LNPW7NE.js.map} +0 -0
  341. /package/dist/{chunk-WHTMVTU4.js.map → chunk-5X4ZJESN.js.map} +0 -0
  342. /package/dist/{chunk-WI37MWRZ.js.map → chunk-63UNVGZW.js.map} +0 -0
  343. /package/dist/{chunk-G3T6YMBO.js.map → chunk-64S6X4IU.js.map} +0 -0
  344. /package/dist/{chunk-CFCEJNOE.js.map → chunk-6KHF6PAB.js.map} +0 -0
  345. /package/dist/{chunk-35A7GCHG.js.map → chunk-6T42L5FI.js.map} +0 -0
  346. /package/dist/{chunk-JDOBIFBK.js.map → chunk-7CXL25AA.js.map} +0 -0
  347. /package/dist/{chunk-YLBE6PLR.js.map → chunk-7IGMKQWK.js.map} +0 -0
  348. /package/dist/{chunk-CSLK7BN6.js.map → chunk-7LYRFI5A.js.map} +0 -0
  349. /package/dist/{chunk-IB63OVUB.js.map → chunk-AJ6ORARY.js.map} +0 -0
  350. /package/dist/{chunk-CGOKNQDK.js.map → chunk-ANZXWOYD.js.map} +0 -0
  351. /package/dist/{chunk-5QXMESS7.js.map → chunk-AXC7UTLM.js.map} +0 -0
  352. /package/dist/{chunk-RFIJRZ4K.js.map → chunk-B657ZGDH.js.map} +0 -0
  353. /package/dist/{chunk-MY7B3HYC.js.map → chunk-CLCMDKTB.js.map} +0 -0
  354. /package/dist/{chunk-QPWUG2S3.js.map → chunk-CYEK6Z3P.js.map} +0 -0
  355. /package/dist/{chunk-IHCYCE6D.js.map → chunk-DJMVSUPK.js.map} +0 -0
  356. /package/dist/{chunk-HP4CU2ZA.js.map → chunk-DST7HRSG.js.map} +0 -0
  357. /package/dist/{chunk-IIFFC3RL.js.map → chunk-EDR6CVWZ.js.map} +0 -0
  358. /package/dist/{chunk-AUWMKDKL.js.map → chunk-EMVJU7WC.js.map} +0 -0
  359. /package/dist/{chunk-O6AYMAPJ.js.map → chunk-ENNC3RZ6.js.map} +0 -0
  360. /package/dist/{chunk-2JRB3Z5N.js.map → chunk-EP5WXE2G.js.map} +0 -0
  361. /package/dist/{chunk-I2KEH2LS.js.map → chunk-FC6RCOIU.js.map} +0 -0
  362. /package/dist/{chunk-OZVHLBH6.js.map → chunk-FLV7M7CT.js.map} +0 -0
  363. /package/dist/{chunk-Q6GI7HEC.js.map → chunk-FRH2UC7Q.js.map} +0 -0
  364. /package/dist/{chunk-3SL52JKD.js.map → chunk-FZVROZKC.js.map} +0 -0
  365. /package/dist/{chunk-2EZB2DL5.js.map → chunk-GE6HETIF.js.map} +0 -0
  366. /package/dist/{chunk-2DHJTJIY.js.map → chunk-GEGUBU3D.js.map} +0 -0
  367. /package/dist/{chunk-6AA7XFMO.js.map → chunk-GF4H3B3R.js.map} +0 -0
  368. /package/dist/{chunk-ME2LMFHF.js.map → chunk-HPAOMYKM.js.map} +0 -0
  369. /package/dist/{chunk-4NRZ7BRS.js.map → chunk-I25RINZK.js.map} +0 -0
  370. /package/dist/{chunk-XG4XXI66.js.map → chunk-I25RQA65.js.map} +0 -0
  371. /package/dist/{chunk-EJJMDLVB.js.map → chunk-IJ4FEGZR.js.map} +0 -0
  372. /package/dist/{chunk-EUL3S3LE.js.map → chunk-J5WI4QRP.js.map} +0 -0
  373. /package/dist/{chunk-LMMC55WM.js.map → chunk-JA54KIYD.js.map} +0 -0
  374. /package/dist/{chunk-5FLFPTDK.js.map → chunk-JA72X46J.js.map} +0 -0
  375. /package/dist/{chunk-NLYOOGZ7.js.map → chunk-KE5PWLI6.js.map} +0 -0
  376. /package/dist/{chunk-4DDIWRT7.js.map → chunk-KQI2PKYP.js.map} +0 -0
  377. /package/dist/{chunk-Y2ADLCB3.js.map → chunk-KSUOM3YU.js.map} +0 -0
  378. /package/dist/{chunk-BTOHGPHC.js.map → chunk-KZEMFD6E.js.map} +0 -0
  379. /package/dist/{chunk-NXVD46HT.js.map → chunk-L2VS27PZ.js.map} +0 -0
  380. /package/dist/{chunk-MYYHACOW.js.map → chunk-L34W4W4I.js.map} +0 -0
  381. /package/dist/{chunk-KRTIHXWC.js.map → chunk-L3PWXD73.js.map} +0 -0
  382. /package/dist/{chunk-NQHSXHJP.js.map → chunk-L6GNS5GB.js.map} +0 -0
  383. /package/dist/{chunk-44PBRUXB.js.map → chunk-M47Q6566.js.map} +0 -0
  384. /package/dist/{chunk-E7FN5N5E.js.map → chunk-MQNOIH2C.js.map} +0 -0
  385. /package/dist/{chunk-IDSZNHIP.js.map → chunk-MULNBSPP.js.map} +0 -0
  386. /package/dist/{chunk-RKSGUMVL.js.map → chunk-NBQIFCJE.js.map} +0 -0
  387. /package/dist/{chunk-I6WFBNA4.js.map → chunk-NHSVW7UT.js.map} +0 -0
  388. /package/dist/{chunk-32HO62I4.js.map → chunk-OFEHITAZ.js.map} +0 -0
  389. /package/dist/{chunk-PCO3P7WJ.js.map → chunk-OGVJXIHX.js.map} +0 -0
  390. /package/dist/{chunk-RYN4AIRM.js.map → chunk-OZABSIOV.js.map} +0 -0
  391. /package/dist/{chunk-HO7IQP7G.js.map → chunk-QMFKW4OU.js.map} +0 -0
  392. /package/dist/{chunk-34G4U6HS.js.map → chunk-QRZMJ2WN.js.map} +0 -0
  393. /package/dist/{chunk-GUD6FYQG.js.map → chunk-RSMIRLKM.js.map} +0 -0
  394. /package/dist/{chunk-TNVZCLJU.js.map → chunk-S5TGRZWY.js.map} +0 -0
  395. /package/dist/{chunk-O24EWFPQ.js.map → chunk-SNXBXYUE.js.map} +0 -0
  396. /package/dist/{chunk-KW77LGDX.js.map → chunk-SUAX5JWY.js.map} +0 -0
  397. /package/dist/{chunk-PNFWIB4X.js.map → chunk-SWRN6U7Q.js.map} +0 -0
  398. /package/dist/{chunk-R4NGLTUS.js.map → chunk-TDMNADPI.js.map} +0 -0
  399. /package/dist/{chunk-QCP75JGO.js.map → chunk-TFA6HPOA.js.map} +0 -0
  400. /package/dist/{chunk-LI76ESJL.js.map → chunk-TZQOCQXB.js.map} +0 -0
  401. /package/dist/{chunk-OV7EYM6M.js.map → chunk-U6OXJQEH.js.map} +0 -0
  402. /package/dist/{chunk-OWHPA4JS.js.map → chunk-V32NHP2R.js.map} +0 -0
  403. /package/dist/{chunk-JSUA4YFE.js.map → chunk-WG4TMZJ2.js.map} +0 -0
  404. /package/dist/{chunk-DQTCGVFK.js.map → chunk-WHCD4Z3D.js.map} +0 -0
  405. /package/dist/{chunk-KATEWTQJ.js.map → chunk-X7J56GPG.js.map} +0 -0
  406. /package/dist/{chunk-7KICGR5W.js.map → chunk-XPRAOTJY.js.map} +0 -0
  407. /package/dist/{chunk-2NV7MMMM.js.map → chunk-XQSJXNWC.js.map} +0 -0
  408. /package/dist/{chunk-RXT6UB25.js.map → chunk-YJAOYEWI.js.map} +0 -0
  409. /package/dist/{chunk-L6DJJEFW.js.map → chunk-YJEUHGZZ.js.map} +0 -0
  410. /package/dist/{chunk-AXFWMRKI.js.map → chunk-YQJCWR4R.js.map} +0 -0
@@ -21,7 +21,7 @@
21
21
  */
22
22
 
23
23
  import path from "node:path";
24
- import { throwIfAborted } from "../abort-error.js";
24
+ import { abortError, isAbortError, throwIfAborted } from "../abort-error.js";
25
25
  import { composeMemoryEnvelope, isMemoryCategory } from "../write-envelope.js";
26
26
  import { log } from "../logger.js";
27
27
  import { createHash } from "node:crypto";
@@ -48,6 +48,7 @@ import {
48
48
  } from "./correction-planner.js";
49
49
  import { type ExecutorDeps, type ExecutorMemory } from "./correction-executor.js";
50
50
  import { CorrectionService, type CorrectionServiceDeps } from "./correction-service.js";
51
+ import { completeCorrectionClassify } from "./correction-classify.js";
51
52
  import { resolveRecallAuxiliaryCapabilities } from "../capabilities.js";
52
53
  import { isSupportPassportPrivateMemory } from "../support-passport/card-projection.js";
53
54
 
@@ -81,15 +82,15 @@ export interface CorrectionAccessWiring {
81
82
  principal?: string;
82
83
  }): Promise<boolean>;
83
84
  /**
84
- * Optional LLM-complete callback (Responses API only — gotcha 1). When
85
- * absent, the planner's classify+draft step falls back to deterministic
86
- * mode (rule 13). The access-service wires the orchestrator's extraction
87
- * LLM here once it exposes a public accessor; until then the contract
88
- * ships with the safe fallback.
85
+ * Optional test override for classify+draft. Production leaves this unset
86
+ * and uses completeCorrectionClassify: a short per-attempt timeout, caller
87
+ * abort, then the gateway/fallback model. A caller abort propagates; every
88
+ * other failure becomes the deterministic empty-action plan.
89
89
  */
90
90
  llmComplete?(request: {
91
91
  system: string;
92
92
  user: string;
93
+ abortSignal?: AbortSignal;
93
94
  }): Promise<string>;
94
95
  }
95
96
 
@@ -144,10 +145,12 @@ function makePlannerDeps(
144
145
  searchMemories(wiring, text, namespaces, limit),
145
146
  resolveTargets: async ({ targetIds, namespaces }) =>
146
147
  resolveTargetMemories(wiring, targetIds, namespaces),
148
+ inspectMemory: async ({ memoryId, namespaces }) =>
149
+ inspectCorrectionMemory(wiring, memoryId, namespaces),
147
150
  expandNeighbors: async ({ seedIds, namespaces, limit }) =>
148
151
  expandEntityNeighbors(wiring, seedIds, namespaces, limit),
149
- classifyAndDraft: async ({ text, candidates }) =>
150
- classifyAndDraft(wiring, text, candidates),
152
+ classifyAndDraft: async ({ text, candidates, abortSignal }) =>
153
+ classifyAndDraft(wiring, text, candidates, abortSignal),
151
154
  renderDiff: async ({ candidates, actions }) =>
152
155
  renderCorrectionDiff(wiring, candidates, actions),
153
156
  storageDir: async (namespace) => (await wiring.orchestrator.getStorage(namespace)).dir,
@@ -294,22 +297,54 @@ async function classifyAndDraft(
294
297
  wiring: CorrectionAccessWiring,
295
298
  text: string,
296
299
  candidates: PlannerCandidate[],
300
+ abortSignal?: AbortSignal,
297
301
  ): Promise<LlmClassificationResult> {
298
- // Route through the injected LLM callback (Responses API only — gotcha 1).
299
- // On any failure (or when no callback is wired), return the deterministic
300
- // fallback (rule 13) — the planner never throws on an LLM outage.
302
+ // Injected llmComplete stays as a test override. Production uses
303
+ // completeCorrectionClassify: short timeout, caller abort, gateway fallback.
304
+ // Abort throws. Every other failure is the deterministic empty-action plan.
301
305
  try {
302
- if (!wiring.llmComplete) {
303
- return fallbackClassification(candidates, "no LLM client available");
304
- }
306
+ throwIfAborted(abortSignal, "correction classify aborted");
305
307
  const user = buildClassifyPrompt(text, candidates);
306
- const raw = await wiring.llmComplete({ system: CLASSIFY_SYSTEM_PROMPT, user });
308
+ const raw = wiring.llmComplete
309
+ ? await wiring.llmComplete({ system: CLASSIFY_SYSTEM_PROMPT, user, abortSignal })
310
+ : await completeCorrectionClassify(
311
+ [
312
+ { role: "system", content: CLASSIFY_SYSTEM_PROMPT },
313
+ { role: "user", content: user },
314
+ ],
315
+ { config: wiring.orchestrator.config, localLlm: wiring.orchestrator.localLlm },
316
+ abortSignal,
317
+ );
318
+ if (raw == null) return fallbackClassification(candidates, "correction classify unavailable");
319
+ throwIfAborted(abortSignal, "correction classify aborted");
307
320
  return parseClassifyResponse(raw, candidates);
308
321
  } catch (err) {
322
+ if (isAbortError(err) || abortSignal?.aborted) {
323
+ throw isAbortError(err) ? err : abortError("correction classify aborted");
324
+ }
309
325
  return fallbackClassification(candidates, `LLM unavailable: ${errMsg(err)}`);
310
326
  }
311
327
  }
312
328
 
329
+ async function inspectCorrectionMemory(
330
+ wiring: CorrectionAccessWiring,
331
+ memoryId: string,
332
+ namespaces: readonly string[],
333
+ ): Promise<{ candidate: PlannerCandidate; status: string } | null> {
334
+ for (const ns of namespaces) {
335
+ const storage = await wiring.orchestrator.getStorage(ns);
336
+ const memory = await storage.getMemoryByIdIncludingArchived(memoryId);
337
+ if (!memory || memory.frontmatter.id !== memoryId) continue;
338
+ const candidate = toCandidate(memory, ns, 1);
339
+ if (isSupportPassportPrivateMemory(memory)) return { candidate, status: "ineligible" };
340
+ if (typeof memory.frontmatter.archivedAt === "string" && memory.frontmatter.archivedAt.length > 0) {
341
+ return { candidate, status: "archived" };
342
+ }
343
+ return { candidate, status: memory.frontmatter.status ?? "active" };
344
+ }
345
+ return null;
346
+ }
347
+
313
348
  async function renderCorrectionDiff(
314
349
  _wiring: CorrectionAccessWiring,
315
350
  candidates: PlannerCandidate[],
@@ -339,7 +374,10 @@ async function renderCorrectionDiff(
339
374
  function describeAfterState(action: CorrectionAction): string {
340
375
  switch (action.kind) {
341
376
  case "supersede":
342
- return action.replacement?.content ?? "(superseded without replacement)";
377
+ return action.replacement?.content
378
+ ?? (action.winnerId
379
+ ? `(superseded by ${action.winnerId}; recoverable)`
380
+ : "(superseded without replacement)");
343
381
  case "edit":
344
382
  return action.patch;
345
383
  case "retract":
@@ -899,6 +937,7 @@ function toExecutorMemory(m: MemoryFile): ExecutorMemory {
899
937
  ...(typeof fm.contentHash === "string" && fm.contentHash.length > 0
900
938
  ? { contentHash: fm.contentHash }
901
939
  : {}),
940
+ status: fm.status ?? "active",
902
941
  } satisfies ExecutorMemory;
903
942
  }
904
943
 
@@ -987,6 +1026,7 @@ Given a correction statement and candidate memories, respond with a JSON object:
987
1026
 
988
1027
  Action shapes:
989
1028
  - {"kind":"supersede","loserId":"<id>","replacement":{"content":"<new fact>"}}
1029
+ - {"kind":"supersede","loserId":"<id>","winnerId":"<existing id>"}
990
1030
  - {"kind":"edit","memoryId":"<id>","patch":"<new full content>"}
991
1031
  - {"kind":"retract","memoryId":"<id>"}
992
1032
  - {"kind":"rescope","memoryId":"<id>","toNamespace":"<ns>"}
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Correction classify completion: short timeout, caller abort, gateway fallback.
3
+ */
4
+ import { strict as assert } from "node:assert";
5
+ import { test } from "node:test";
6
+
7
+ import { isAbortError } from "../abort-error.js";
8
+ import type { PluginConfig } from "../types.js";
9
+ import { type CorrectionClassifyDeps, completeCorrectionClassify } from "./correction-classify.js";
10
+
11
+ const MESSAGES = [{ role: "user" as const, content: "the old fact is a duplicate" }];
12
+
13
+ function config(over: Partial<PluginConfig> = {}): PluginConfig {
14
+ return {
15
+ localLlmEnabled: true,
16
+ localLlmFallback: true,
17
+ modelSource: "plugin",
18
+ correctionClassifyTimeoutMs: 40,
19
+ ...over,
20
+ } as PluginConfig;
21
+ }
22
+
23
+ function hangUntilAbort(): CorrectionClassifyDeps["localLlm"] {
24
+ return {
25
+ chatCompletion: (_messages, options) =>
26
+ new Promise((resolve) => {
27
+ const finish = () => resolve(null);
28
+ if (options?.signal?.aborted) {
29
+ finish();
30
+ return;
31
+ }
32
+ options?.signal?.addEventListener("abort", finish, { once: true });
33
+ }),
34
+ };
35
+ }
36
+
37
+ test("classify falls back after the local attempt times out", async () => {
38
+ let fallbackCalls = 0;
39
+ let localSignal: AbortSignal | undefined;
40
+ const deps: CorrectionClassifyDeps = {
41
+ config: config(),
42
+ localLlm: {
43
+ chatCompletion: (_messages, options) =>
44
+ new Promise((resolve) => {
45
+ localSignal = options?.signal;
46
+ const finish = () => resolve(null);
47
+ if (options?.signal?.aborted) {
48
+ finish();
49
+ return;
50
+ }
51
+ options?.signal?.addEventListener("abort", finish, { once: true });
52
+ }),
53
+ },
54
+ fallbackChat: async () => {
55
+ fallbackCalls += 1;
56
+ return { content: '{"classification":"outdated","confidence":0.8,"actions":[]}', modelUsed: "gateway" };
57
+ },
58
+ };
59
+ const content = await completeCorrectionClassify(MESSAGES, deps);
60
+ assert.equal(fallbackCalls, 1);
61
+ assert.equal(localSignal?.aborted, true);
62
+ assert.match(content ?? "", /outdated/);
63
+ });
64
+
65
+ test("classify cancels on caller abort and does not call the fallback", async () => {
66
+ let fallbackCalls = 0;
67
+ const caller = new AbortController();
68
+ const deps: CorrectionClassifyDeps = {
69
+ config: config({ correctionClassifyTimeoutMs: 30_000 }),
70
+ localLlm: hangUntilAbort(),
71
+ fallbackChat: async () => {
72
+ fallbackCalls += 1;
73
+ return { content: "should-not-run", modelUsed: "gateway" };
74
+ },
75
+ };
76
+ const pending = completeCorrectionClassify(MESSAGES, deps, caller.signal);
77
+ caller.abort();
78
+ await assert.rejects(pending, (err: unknown) => isAbortError(err));
79
+ assert.equal(fallbackCalls, 0);
80
+ });
81
+
82
+ test("classify uses the fallback when the local LLM returns null", async () => {
83
+ let localCalls = 0;
84
+ let fallbackCalls = 0;
85
+ const deps: CorrectionClassifyDeps = {
86
+ config: config(),
87
+ localLlm: {
88
+ chatCompletion: async () => {
89
+ localCalls += 1;
90
+ return null;
91
+ },
92
+ },
93
+ fallbackChat: async () => {
94
+ fallbackCalls += 1;
95
+ return { content: '{"ok":true}', modelUsed: "gateway" };
96
+ },
97
+ };
98
+ const content = await completeCorrectionClassify(MESSAGES, deps);
99
+ assert.equal(localCalls, 1);
100
+ assert.equal(fallbackCalls, 1);
101
+ assert.equal(content, '{"ok":true}');
102
+ });
103
+
104
+ test("classify does not call the fallback when the local LLM returns content", async () => {
105
+ let fallbackCalls = 0;
106
+ const deps: CorrectionClassifyDeps = {
107
+ config: config(),
108
+ localLlm: { chatCompletion: async () => ({ content: "local-json" }) },
109
+ fallbackChat: async () => {
110
+ fallbackCalls += 1;
111
+ return { content: "gateway", modelUsed: "gateway" };
112
+ },
113
+ };
114
+ assert.equal(await completeCorrectionClassify(MESSAGES, deps), "local-json");
115
+ assert.equal(fallbackCalls, 0);
116
+ });
117
+
118
+ test("gateway modelSource skips the local LLM", async () => {
119
+ let localCalls = 0;
120
+ const deps: CorrectionClassifyDeps = {
121
+ config: config({ modelSource: "gateway", localLlmEnabled: true }),
122
+ localLlm: {
123
+ chatCompletion: async () => {
124
+ localCalls += 1;
125
+ return { content: "local" };
126
+ },
127
+ },
128
+ fallbackChat: async () => ({ content: "gateway-primary", modelUsed: "gateway" }),
129
+ };
130
+ assert.equal(await completeCorrectionClassify(MESSAGES, deps), "gateway-primary");
131
+ assert.equal(localCalls, 0);
132
+ });
133
+
134
+ test("no configured gateway models return null without waiting out the classify timeout", async () => {
135
+ const started = Date.now();
136
+ const content = await completeCorrectionClassify(MESSAGES, {
137
+ config: config({ localLlmEnabled: false }),
138
+ localLlm: {
139
+ chatCompletion: async () => {
140
+ throw new Error("local must not run");
141
+ },
142
+ },
143
+ });
144
+ assert.equal(content, null);
145
+ assert.ok(Date.now() - started < 1000, `no-models path took ${Date.now() - started}ms`);
146
+ });
147
+
148
+ test("a fallback attempt timeout returns null instead of an abort", async () => {
149
+ const deps: CorrectionClassifyDeps = {
150
+ config: config({ localLlmEnabled: false, correctionClassifyTimeoutMs: 40 }),
151
+ localLlm: {
152
+ chatCompletion: async () => {
153
+ throw new Error("local must not run");
154
+ },
155
+ },
156
+ fallbackChat: (_messages, options) =>
157
+ new Promise((_resolve, reject) => {
158
+ const fail = () => {
159
+ const err = new Error("fallback attempt aborted");
160
+ err.name = "AbortError";
161
+ reject(err);
162
+ };
163
+ if (options.signal?.aborted) {
164
+ fail();
165
+ return;
166
+ }
167
+ options.signal?.addEventListener("abort", fail, { once: true });
168
+ }),
169
+ };
170
+ assert.equal(await completeCorrectionClassify(MESSAGES, deps), null);
171
+ });
172
+
173
+ test("localLlmFallback false does not call the gateway after a local miss", async () => {
174
+ let fallbackCalls = 0;
175
+ const deps: CorrectionClassifyDeps = {
176
+ config: config({ localLlmFallback: false }),
177
+ localLlm: { chatCompletion: async () => null },
178
+ fallbackChat: async () => {
179
+ fallbackCalls += 1;
180
+ return { content: "nope", modelUsed: "gateway" };
181
+ },
182
+ };
183
+ await assert.rejects(() => completeCorrectionClassify(MESSAGES, deps), /fallback disabled/);
184
+ assert.equal(fallbackCalls, 0);
185
+ });
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Correction classify completion: a short per-attempt budget, caller abort,
3
+ * and the same gateway/fallback client extraction uses when the local LLM
4
+ * is down or `modelSource` is `gateway`.
5
+ *
6
+ * Each attempt (local, then gateway) gets `correctionClassifyTimeoutMs`.
7
+ * A caller abort cancels the in-flight attempt and does not fall through.
8
+ * A local timeout, null, or error falls through when `localLlmFallback` is
9
+ * not false. Gateway mode skips the local client and uses the task chain
10
+ * as the primary path.
11
+ */
12
+ import { abortError, isAbortError, throwIfAborted } from "../abort-error.js";
13
+ import { resolveLocalLlmCapabilities } from "../capabilities.js";
14
+ import { fallbackLlmRuntimeContextFromConfig } from "../fallback-llm-codex-subscription.js";
15
+ import {
16
+ FallbackLlmClient,
17
+ type FallbackLlmOptions,
18
+ type FallbackLlmResponse,
19
+ gatewayTaskChainOptions,
20
+ } from "../fallback-llm.js";
21
+ import type { LocalLlmChatCompletionOptions } from "../local-llm-helpers.js";
22
+ import type { PluginConfig } from "../types.js";
23
+ import { DEFAULT_CORRECTION_CLASSIFY_TIMEOUT_MS, MAX_CORRECTION_CLASSIFY_TIMEOUT_MS } from "./correction-config.js";
24
+
25
+ export interface CorrectionClassifyMessage {
26
+ role: "system" | "user" | "assistant";
27
+ content: string;
28
+ }
29
+
30
+ export interface CorrectionClassifyDeps {
31
+ config: PluginConfig;
32
+ localLlm: {
33
+ chatCompletion(
34
+ messages: Array<{ role: string; content: string }>,
35
+ options?: LocalLlmChatCompletionOptions
36
+ ): Promise<{ content: string } | null>;
37
+ };
38
+ /**
39
+ * Test seam. Production builds a {@link FallbackLlmClient} from config.
40
+ * Returning null is the no-models fast path.
41
+ */
42
+ fallbackChat?(
43
+ messages: CorrectionClassifyMessage[],
44
+ options: FallbackLlmOptions
45
+ ): Promise<FallbackLlmResponse | null>;
46
+ }
47
+
48
+ export async function completeCorrectionClassify(
49
+ messages: CorrectionClassifyMessage[],
50
+ deps: CorrectionClassifyDeps,
51
+ abortSignal?: AbortSignal
52
+ ): Promise<string | null> {
53
+ throwIfAborted(abortSignal, "correction classify aborted");
54
+ const timeoutMs = classifyTimeoutMs(deps.config);
55
+ const gatewayPrimary = deps.config.modelSource === "gateway";
56
+ const localEnabled = resolveLocalLlmCapabilities(deps.config).localLlm === true && !gatewayPrimary;
57
+
58
+ if (localEnabled) {
59
+ const localAttempt = startAttempt(abortSignal, timeoutMs);
60
+ try {
61
+ const local = await deps.localLlm.chatCompletion(messages, {
62
+ operation: "correction-classify",
63
+ priority: "background",
64
+ timeoutMs,
65
+ temperature: 0.2,
66
+ signal: localAttempt.signal,
67
+ });
68
+ if (abortSignal?.aborted) throw abortError("correction classify aborted");
69
+ if (local?.content) return local.content;
70
+ if (deps.config.localLlmFallback === false) {
71
+ throw new Error("correction classify: local LLM unavailable and fallback disabled");
72
+ }
73
+ } catch (err) {
74
+ if (abortSignal?.aborted) {
75
+ throw isAbortError(err) ? err : abortError("correction classify aborted");
76
+ }
77
+ // A timeout abort belongs to this attempt. Fall through unless the
78
+ // operator turned the gateway path off.
79
+ if (deps.config.localLlmFallback === false) {
80
+ if (isAbortError(err)) {
81
+ throw new Error("correction classify: local LLM timed out and fallback disabled");
82
+ }
83
+ throw err;
84
+ }
85
+ } finally {
86
+ localAttempt.dispose();
87
+ }
88
+ } else if (!gatewayPrimary && deps.config.localLlmFallback === false) {
89
+ throw new Error("correction classify: local LLM disabled and fallback disabled");
90
+ }
91
+
92
+ throwIfAborted(abortSignal, "correction classify aborted");
93
+ const fallbackAttempt = startAttempt(abortSignal, timeoutMs);
94
+ try {
95
+ const fallbackOptions: FallbackLlmOptions = {
96
+ temperature: 0.2,
97
+ timeoutMs,
98
+ signal: fallbackAttempt.signal,
99
+ ...gatewayTaskChainOptions(deps.config),
100
+ };
101
+ const fallback = deps.fallbackChat
102
+ ? await deps.fallbackChat(messages, fallbackOptions)
103
+ : await new FallbackLlmClient(
104
+ deps.config.gatewayConfig,
105
+ fallbackLlmRuntimeContextFromConfig(deps.config)
106
+ ).chatCompletion(messages, fallbackOptions);
107
+ if (abortSignal?.aborted) throw abortError("correction classify aborted");
108
+ return fallback?.content ?? null;
109
+ } catch (err) {
110
+ // The attempt signal aborts on its own budget. That is a finished miss
111
+ // (empty plan), not a caller disconnect — only the caller signal skips
112
+ // the empty-plan path.
113
+ if (abortSignal?.aborted) {
114
+ throw isAbortError(err) ? err : abortError("correction classify aborted");
115
+ }
116
+ if (isAbortError(err)) return null;
117
+ throw err;
118
+ } finally {
119
+ fallbackAttempt.dispose();
120
+ }
121
+ }
122
+
123
+ function classifyTimeoutMs(config: PluginConfig): number {
124
+ const configured = config.correctionClassifyTimeoutMs;
125
+ if (
126
+ typeof configured === "number" &&
127
+ Number.isInteger(configured) &&
128
+ configured >= 1 &&
129
+ configured <= MAX_CORRECTION_CLASSIFY_TIMEOUT_MS
130
+ ) {
131
+ return configured;
132
+ }
133
+ return DEFAULT_CORRECTION_CLASSIFY_TIMEOUT_MS;
134
+ }
135
+
136
+ function startAttempt(
137
+ caller: AbortSignal | undefined,
138
+ timeoutMs: number
139
+ ): { signal: AbortSignal; dispose: () => void } {
140
+ // A ref'd timer, not AbortSignal.timeout: that helper's timer is unref'd,
141
+ // so a classify wait would not keep a short-lived caller alive until the
142
+ // budget elapses.
143
+ const controller = new AbortController();
144
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
145
+ const onCaller = (): void => controller.abort();
146
+ if (caller?.aborted) controller.abort();
147
+ else caller?.addEventListener("abort", onCaller, { once: true });
148
+ let disposed = false;
149
+ const dispose = (): void => {
150
+ if (disposed) return;
151
+ disposed = true;
152
+ clearTimeout(timer);
153
+ caller?.removeEventListener("abort", onCaller);
154
+ };
155
+ controller.signal.addEventListener("abort", dispose, { once: true });
156
+ return { signal: controller.signal, dispose };
157
+ }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Correction-contract config (issue #1580 plus classify timeout).
3
+ *
4
+ * Extracted from config.ts so the grandfathered parser file does not grow.
5
+ * Nested `correction.<key>` wins over the flat `correction<Key>` legacy key.
6
+ * Invalid values throw — they are never clamped or silently defaulted.
7
+ *
8
+ * Reads stay inline on `cfg` so the parsed-key extractor records the same
9
+ * paths the plugin schema declares.
10
+ */
11
+ import { coerceBool, coerceNumber } from "../connectors/coerce.js";
12
+
13
+ export const DEFAULT_CORRECTION_CLASSIFY_TIMEOUT_MS = 45_000;
14
+ export const MAX_CORRECTION_CLASSIFY_TIMEOUT_MS = 300_000;
15
+
16
+ export interface ParsedCorrectionConfig {
17
+ correctionEnabled: boolean;
18
+ correctionApplyRequiresConfirm: boolean;
19
+ correctionMaxAffected: number;
20
+ correctionPlanTtlHours: number;
21
+ correctionClassifyTimeoutMs: number;
22
+ }
23
+
24
+ export function parseCorrectionConfig(cfg: Record<string, unknown>): ParsedCorrectionConfig {
25
+ return {
26
+ correctionEnabled: (() => {
27
+ const nested = (cfg.correction as Record<string, unknown> | undefined)?.enabled;
28
+ const raw = nested !== undefined ? nested : cfg.correctionEnabled;
29
+ if (raw === undefined || raw === null) return true;
30
+ const value = coerceBool(raw);
31
+ if (value === undefined) {
32
+ throw new Error(`Invalid correction.enabled: expected a boolean, got ${JSON.stringify(raw)}`);
33
+ }
34
+ return value;
35
+ })(),
36
+ correctionApplyRequiresConfirm: (() => {
37
+ const nested = (cfg.correction as Record<string, unknown> | undefined)?.applyRequiresConfirm;
38
+ const raw = nested !== undefined ? nested : cfg.correctionApplyRequiresConfirm;
39
+ if (raw === undefined || raw === null) return true;
40
+ const value = coerceBool(raw);
41
+ if (value === undefined) {
42
+ throw new Error(`Invalid correction.applyRequiresConfirm: expected a boolean, got ${JSON.stringify(raw)}`);
43
+ }
44
+ return value;
45
+ })(),
46
+ correctionMaxAffected: (() => {
47
+ const rawNested = (cfg.correction as Record<string, unknown> | undefined)?.maxAffected;
48
+ const raw = rawNested !== undefined ? rawNested : cfg.correctionMaxAffected;
49
+ if (raw === undefined || raw === null) return 10;
50
+ const n = coerceNumber(raw);
51
+ if (n === undefined || !Number.isFinite(n) || n < 1 || !Number.isInteger(n)) {
52
+ throw new Error(`Invalid correction.maxAffected: expected an integer >= 1, got ${JSON.stringify(raw)}`);
53
+ }
54
+ return n;
55
+ })(),
56
+ correctionPlanTtlHours: (() => {
57
+ const nested = (cfg.correction as Record<string, unknown> | undefined)?.planTtlHours;
58
+ const fromNested = nested !== undefined ? coerceNumber(nested) : undefined;
59
+ if (fromNested !== undefined && fromNested >= 1) return fromNested;
60
+ if (nested !== undefined && fromNested === undefined) {
61
+ throw new Error(`Invalid correction.planTtlHours: expected a number >= 1, got ${JSON.stringify(nested)}`);
62
+ }
63
+ if (fromNested !== undefined && fromNested < 1) {
64
+ throw new Error(`Invalid correction.planTtlHours: expected a number >= 1, got ${JSON.stringify(nested)}`);
65
+ }
66
+ const fromFlat = coerceNumber(cfg.correctionPlanTtlHours);
67
+ if (fromFlat !== undefined && fromFlat >= 1) return fromFlat;
68
+ if (cfg.correctionPlanTtlHours !== undefined && fromFlat === undefined) {
69
+ throw new Error(
70
+ `Invalid correction.planTtlHours: expected a number >= 1, got ${JSON.stringify(cfg.correctionPlanTtlHours)}`
71
+ );
72
+ }
73
+ if (fromFlat !== undefined && fromFlat < 1) {
74
+ throw new Error(
75
+ `Invalid correction.planTtlHours: expected a number >= 1, got ${JSON.stringify(cfg.correctionPlanTtlHours)}`
76
+ );
77
+ }
78
+ return 24;
79
+ })(),
80
+ correctionClassifyTimeoutMs: (() => {
81
+ const nested = (cfg.correction as Record<string, unknown> | undefined)?.classifyTimeoutMs;
82
+ const raw = nested !== undefined ? nested : cfg.correctionClassifyTimeoutMs;
83
+ if (raw === undefined || raw === null) return DEFAULT_CORRECTION_CLASSIFY_TIMEOUT_MS;
84
+ const n = coerceNumber(raw);
85
+ if (n === undefined || !Number.isInteger(n) || n < 1 || n > MAX_CORRECTION_CLASSIFY_TIMEOUT_MS) {
86
+ throw new Error(
87
+ `Invalid correction.classifyTimeoutMs: expected an integer from 1 to ${MAX_CORRECTION_CLASSIFY_TIMEOUT_MS}, got ${JSON.stringify(raw)}`
88
+ );
89
+ }
90
+ return n;
91
+ })(),
92
+ };
93
+ }