agentic-qe 3.13.11 → 3.14.0

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 (438) hide show
  1. package/.claude/helpers/.helpers-version +1 -1
  2. package/.claude/helpers/helpers.manifest.json +2 -2
  3. package/.claude/helpers/statusline.cjs +89 -40
  4. package/.claude/skills/qe-court/SKILL.md +4 -4
  5. package/.claude/skills/qe-court/config-schema.json +38 -0
  6. package/.claude/skills/qe-court/evals/qe-court.yaml +8 -8
  7. package/.claude/skills/skills-manifest.json +1 -1
  8. package/CHANGELOG.md +93 -0
  9. package/assets/skills/qe-court/SKILL.md +4 -4
  10. package/assets/skills/qe-court/config-schema.json +38 -0
  11. package/assets/skills/qe-court/evals/qe-court.yaml +8 -8
  12. package/assets/skills/skills-manifest.json +1 -1
  13. package/dist/cli/brain-commands.d.ts +1 -0
  14. package/dist/cli/brain-commands.js +1 -0
  15. package/dist/cli/bundle.js +5 -5
  16. package/dist/cli/chunks/adapter-3VBXL7DO.js +2 -0
  17. package/dist/cli/chunks/{agent-booster-wasm-5ARDZYNO.js → agent-booster-wasm-JZO6VUW4.js} +2 -2
  18. package/dist/cli/chunks/{agent-handler-SVKEPF7B.js → agent-handler-534KFGEJ.js} +2 -2
  19. package/dist/cli/chunks/{agent-memory-branch-QDL7OVHC.js → agent-memory-branch-B7KWLWY4.js} +2 -2
  20. package/dist/cli/chunks/aqe-learning-engine-NXRNKJNA.js +2 -0
  21. package/dist/cli/chunks/{arena-7OCKBN6O.js → arena-7CCD3S3I.js} +2 -2
  22. package/dist/cli/chunks/{arena-HQLSNQZ3.js → arena-OW5MELJX.js} +4 -4
  23. package/dist/cli/chunks/{audit-2EQJW5UE.js → audit-BHVKN4E2.js} +3 -3
  24. package/dist/cli/chunks/base-F6GMOKU2.js +2 -0
  25. package/dist/cli/chunks/{better-sqlite3-OBFNNE4T.js → better-sqlite3-P5FA6WFK.js} +2 -2
  26. package/dist/cli/chunks/brain-handler-WNW5LOV7.js +90 -0
  27. package/dist/cli/chunks/{branch-enumerator-6B4L5O3D.js → branch-enumerator-TVZ2ODKH.js} +2 -2
  28. package/dist/cli/chunks/{browser-VDMGBWNK.js → browser-TCEB2W2F.js} +2 -2
  29. package/dist/cli/chunks/browser-workflow-QRDC46LR.js +2 -0
  30. package/dist/cli/chunks/{chunk-M3MRFZKD.js → chunk-2D7UBX5S.js} +1 -1
  31. package/dist/cli/chunks/{chunk-IZF3VLNW.js → chunk-2HYVOX6N.js} +1 -1
  32. package/dist/cli/chunks/{chunk-U6G6RUFI.js → chunk-2NO4H35K.js} +1 -1
  33. package/dist/cli/chunks/{chunk-4TJJU5Y2.js → chunk-2O4DDPEA.js} +19 -19
  34. package/dist/cli/chunks/{chunk-24XW7KUX.js → chunk-3EM2MK3Z.js} +2 -2
  35. package/dist/cli/chunks/chunk-3GUO4ERR.js +24 -0
  36. package/dist/cli/chunks/{chunk-YRCNWQBO.js → chunk-3JIJBR4V.js} +2 -2
  37. package/dist/cli/chunks/{chunk-2E2QCIGR.js → chunk-3QT6S3A3.js} +3 -3
  38. package/dist/cli/chunks/{chunk-W2T36CS5.js → chunk-3RYTAYR6.js} +1 -1
  39. package/dist/cli/chunks/chunk-43GPSYAL.js +14 -0
  40. package/dist/cli/chunks/{chunk-YYAJEPRD.js → chunk-4FEJEVG7.js} +1 -1
  41. package/dist/cli/chunks/{chunk-4DTBGQWA.js → chunk-4MK2G6TK.js} +2 -2
  42. package/dist/cli/chunks/{chunk-CK7ZLMD2.js → chunk-4XTHRAUV.js} +1 -1
  43. package/dist/cli/chunks/{chunk-LM727BVL.js → chunk-4ZVGSVHV.js} +4 -4
  44. package/dist/cli/chunks/{chunk-DPZ4AU6W.js → chunk-52DFCV57.js} +2 -2
  45. package/dist/cli/chunks/{chunk-PADCR4LY.js → chunk-5LEAGKX5.js} +2 -2
  46. package/dist/cli/chunks/{chunk-P736OKQN.js → chunk-5MZF4N4Q.js} +1 -1
  47. package/dist/cli/chunks/{chunk-XZI4QXJJ.js → chunk-5VDVYTO5.js} +2 -2
  48. package/dist/cli/chunks/chunk-5XGVSQS2.js +11 -0
  49. package/dist/cli/chunks/{chunk-AAPJG3WI.js → chunk-6AOZU3UN.js} +2 -2
  50. package/dist/cli/chunks/{chunk-NC7HTDH5.js → chunk-6AYO3IR2.js} +2 -2
  51. package/dist/cli/chunks/{chunk-TJ67B7S7.js → chunk-6DPAMYLU.js} +2 -2
  52. package/dist/cli/chunks/{chunk-PZHAPI47.js → chunk-6IJMD2ZL.js} +3 -3
  53. package/dist/cli/chunks/{chunk-RGUKQXKL.js → chunk-6IZQSLYZ.js} +2 -2
  54. package/dist/cli/chunks/{chunk-2E3PRDEX.js → chunk-6W42XWLL.js} +2 -2
  55. package/dist/cli/chunks/{chunk-ATRUE6CT.js → chunk-7BOPFN3Y.js} +2 -2
  56. package/dist/cli/chunks/{chunk-LIGDJP6H.js → chunk-7OUMJCJT.js} +1 -1
  57. package/dist/cli/chunks/{chunk-2Y3PW5EJ.js → chunk-7RAB2BZY.js} +160 -26
  58. package/dist/cli/chunks/{chunk-XEUUWOSS.js → chunk-7RVAHJQZ.js} +3 -3
  59. package/dist/cli/chunks/{chunk-HHEWINDL.js → chunk-A2WZEMWT.js} +1 -1
  60. package/dist/cli/chunks/{chunk-36ONUM43.js → chunk-A7SKM6JL.js} +1 -1
  61. package/dist/cli/chunks/chunk-AFGT26NR.js +2 -0
  62. package/dist/cli/chunks/{chunk-23E37MDA.js → chunk-AR6SATU3.js} +2 -2
  63. package/dist/cli/chunks/{chunk-IYMV4CU5.js → chunk-AVLA5PBJ.js} +2 -2
  64. package/dist/cli/chunks/{chunk-OLPJGYO5.js → chunk-B3H4K7CY.js} +2 -2
  65. package/dist/cli/chunks/{chunk-JWT675RM.js → chunk-BQCTO7UB.js} +2 -2
  66. package/dist/cli/chunks/{chunk-JKP2BUF5.js → chunk-BZ4QVFP2.js} +2 -2
  67. package/dist/cli/chunks/{chunk-WINYWMQP.js → chunk-CIUOC5F3.js} +1 -1
  68. package/dist/cli/chunks/{chunk-SJJFU7KK.js → chunk-CIZ6LSNV.js} +2 -2
  69. package/dist/cli/chunks/{chunk-2D7SBOZ4.js → chunk-DDPJUAXQ.js} +2 -2
  70. package/dist/cli/chunks/{chunk-H7QWOJF2.js → chunk-DJITEQYS.js} +2 -2
  71. package/dist/cli/chunks/{chunk-DMHQ4ZUZ.js → chunk-DOWCOZDO.js} +2 -2
  72. package/dist/cli/chunks/{chunk-PBJ5I2NS.js → chunk-E4NU4BJA.js} +2 -2
  73. package/dist/cli/chunks/{chunk-E54QUKAD.js → chunk-E722WNWC.js} +2 -2
  74. package/dist/cli/chunks/chunk-ECTPFCON.js +2 -0
  75. package/dist/cli/chunks/{chunk-MUAW4AJ6.js → chunk-EJ6IP7DE.js} +5 -5
  76. package/dist/cli/chunks/{chunk-NHL37QAZ.js → chunk-ENGKLFLY.js} +2 -2
  77. package/dist/cli/chunks/{chunk-VU3ARH4S.js → chunk-FF43SRP6.js} +1 -1
  78. package/dist/cli/chunks/{chunk-PJNIDDER.js → chunk-FOHYBR3O.js} +1 -1
  79. package/dist/cli/chunks/{chunk-LREIHR2A.js → chunk-FRL3X2QA.js} +2 -2
  80. package/dist/cli/chunks/{chunk-WYQM7GXW.js → chunk-GKNDP5VK.js} +2 -2
  81. package/dist/cli/chunks/{chunk-JK6ENS3J.js → chunk-GVEJP5T7.js} +2 -2
  82. package/dist/cli/chunks/{chunk-H3BHOIT6.js → chunk-H3YDDKBW.js} +3 -3
  83. package/dist/cli/chunks/{chunk-3SPYI7TS.js → chunk-H6NFDOMK.js} +1 -1
  84. package/dist/cli/chunks/{chunk-AZVV7POX.js → chunk-H76TV4UU.js} +3 -3
  85. package/dist/cli/chunks/{chunk-2HT5HRAA.js → chunk-HJIAVR6P.js} +2 -2
  86. package/dist/cli/chunks/{chunk-UZ7OWJ75.js → chunk-HLNQDRHA.js} +1 -1
  87. package/dist/cli/chunks/{chunk-IGW5L7YM.js → chunk-HNKPLVYS.js} +1 -1
  88. package/dist/cli/chunks/{chunk-Y2S6SS7M.js → chunk-HQ7243G3.js} +2 -2
  89. package/dist/cli/chunks/{chunk-TZA45FI7.js → chunk-HQHLHSJO.js} +2 -2
  90. package/dist/cli/chunks/{chunk-YEPJS4AM.js → chunk-I3VHXA2W.js} +2 -2
  91. package/dist/cli/chunks/{chunk-W2NQDAJZ.js → chunk-IE5LC7MB.js} +2 -2
  92. package/dist/cli/chunks/{chunk-TU4BYSXR.js → chunk-IL4AEB3J.js} +27 -25
  93. package/dist/cli/chunks/{chunk-JTQSXU6V.js → chunk-IL6QRXRP.js} +6 -6
  94. package/dist/cli/chunks/{chunk-UX5SM2PW.js → chunk-IQ3JFHZN.js} +1 -1
  95. package/dist/cli/chunks/{chunk-RAV2NJNX.js → chunk-IQLRA4FV.js} +1 -1
  96. package/dist/cli/chunks/{chunk-S3FNAHNC.js → chunk-J3XSHJGK.js} +2 -2
  97. package/dist/cli/chunks/{chunk-AHG2O5HT.js → chunk-JAK5UUWH.js} +2 -2
  98. package/dist/cli/chunks/{chunk-XDK2TPI5.js → chunk-JGROLJ33.js} +1 -1
  99. package/dist/cli/chunks/{chunk-MWTFFGU3.js → chunk-JMKMDSXG.js} +2 -2
  100. package/dist/cli/chunks/{chunk-BVKFBJX6.js → chunk-JRKEBESE.js} +2 -2
  101. package/dist/cli/chunks/{chunk-76NSSNB3.js → chunk-JTWJR2KY.js} +2 -2
  102. package/dist/cli/chunks/{chunk-QYHRDBKI.js → chunk-JY4VZ6BN.js} +2 -2
  103. package/dist/cli/chunks/{chunk-DLL3AJH7.js → chunk-K6CGQR5R.js} +1 -1
  104. package/dist/cli/chunks/{chunk-7HTYM6XY.js → chunk-KDIZQHG5.js} +2 -2
  105. package/dist/cli/chunks/{chunk-B646CE3C.js → chunk-KTSCZ7AW.js} +2 -2
  106. package/dist/cli/chunks/{chunk-LLP3UW62.js → chunk-L4C42WYI.js} +2 -2
  107. package/dist/cli/chunks/{chunk-H3AKF2HS.js → chunk-LCSVROU7.js} +10 -10
  108. package/dist/cli/chunks/{chunk-HZR4G6I3.js → chunk-LKR7T3OZ.js} +1 -1
  109. package/dist/cli/chunks/{chunk-Z5ARI4XT.js → chunk-LZHTCUCO.js} +2 -2
  110. package/dist/cli/chunks/{chunk-YDEYB6Z2.js → chunk-M3HUZD2I.js} +1 -1
  111. package/dist/cli/chunks/{chunk-GWTGBQ6X.js → chunk-MO7EITJK.js} +2 -2
  112. package/dist/cli/chunks/{chunk-5364DBZG.js → chunk-N2YAUDAV.js} +2 -2
  113. package/dist/cli/chunks/{chunk-XVFSZLRS.js → chunk-NDPQ2WPU.js} +1 -1
  114. package/dist/cli/chunks/{chunk-CTS545BF.js → chunk-NKWO2X3T.js} +1 -1
  115. package/dist/cli/chunks/{chunk-GPJDZFFV.js → chunk-NOPVFBQM.js} +2 -2
  116. package/dist/cli/chunks/{chunk-VNVGCJCS.js → chunk-NYARYONH.js} +1 -1
  117. package/dist/cli/chunks/{chunk-WRZ5WZG3.js → chunk-O2MZZL54.js} +2 -2
  118. package/dist/cli/chunks/{chunk-EKFKQGIT.js → chunk-OIYUSWHX.js} +1 -1
  119. package/dist/cli/chunks/{chunk-ZNGFSR5M.js → chunk-OONCRNFA.js} +2 -2
  120. package/dist/cli/chunks/{chunk-LUAXQB57.js → chunk-PEW6FZ47.js} +1 -1
  121. package/dist/cli/chunks/{chunk-W7BB4XLP.js → chunk-Q73RRTLE.js} +2 -2
  122. package/dist/cli/chunks/{chunk-JKPMV5VB.js → chunk-QKQZ4IJF.js} +5 -5
  123. package/dist/cli/chunks/{chunk-EHZQIXH6.js → chunk-QMMDN4WX.js} +3 -3
  124. package/dist/cli/chunks/chunk-QQCENKCX.js +2 -0
  125. package/dist/cli/chunks/{chunk-7YL7P5XG.js → chunk-QSPRQPEL.js} +2 -2
  126. package/dist/cli/chunks/{chunk-DXQMVATY.js → chunk-QZPFUFZG.js} +2 -2
  127. package/dist/cli/chunks/chunk-R2B6EE45.js +2 -0
  128. package/dist/cli/chunks/{chunk-VLW63UA2.js → chunk-R6WJF4D5.js} +2 -2
  129. package/dist/cli/chunks/{chunk-EWH7PFV6.js → chunk-RGVD6ZUX.js} +1 -1
  130. package/dist/cli/chunks/{chunk-UGFHEVG4.js → chunk-RKUYVVGJ.js} +2 -2
  131. package/dist/cli/chunks/{chunk-V7QZMOG3.js → chunk-S5YVLPXU.js} +2 -2
  132. package/dist/cli/chunks/{chunk-UDFTDQ6C.js → chunk-SROZXIMT.js} +37 -37
  133. package/dist/cli/chunks/{chunk-AFXMTSO3.js → chunk-SSV6KSFT.js} +1 -1
  134. package/dist/cli/chunks/{chunk-HGPMS6JL.js → chunk-SUSMHSXT.js} +2 -2
  135. package/dist/cli/chunks/{chunk-BL5BHBXR.js → chunk-T3BZAFLP.js} +1 -1
  136. package/dist/cli/chunks/{chunk-K652SCBA.js → chunk-TGOGCA74.js} +2 -2
  137. package/dist/cli/chunks/{chunk-VHNAN7E2.js → chunk-TKYEP7OR.js} +1 -1
  138. package/dist/cli/chunks/{chunk-OJUI65TR.js → chunk-TQJE7DNL.js} +2 -2
  139. package/dist/cli/chunks/{chunk-MS5CAEPA.js → chunk-TRDEULS7.js} +2 -2
  140. package/dist/cli/chunks/{chunk-YRV45WDT.js → chunk-U4GZWPKM.js} +2 -2
  141. package/dist/cli/chunks/{chunk-2BDTZYQN.js → chunk-UI6WSPEK.js} +3 -3
  142. package/dist/cli/chunks/{chunk-TMY2Y7SC.js → chunk-UUJ5VQJM.js} +1 -1
  143. package/dist/cli/chunks/{chunk-4CTKRLRX.js → chunk-VESOYP6A.js} +2 -2
  144. package/dist/cli/chunks/{chunk-UPMQHCC2.js → chunk-VOSISOFD.js} +1 -1
  145. package/dist/cli/chunks/{chunk-IZOWGVTU.js → chunk-VPVZHJSU.js} +2 -2
  146. package/dist/cli/chunks/{chunk-TBO64O56.js → chunk-VRRG66XB.js} +1 -1
  147. package/dist/cli/chunks/{chunk-WJB6YYMJ.js → chunk-VXU53F7Z.js} +2 -2
  148. package/dist/cli/chunks/{chunk-4TUGQESL.js → chunk-VYENTZ45.js} +2 -2
  149. package/dist/cli/chunks/chunk-WJXHE52V.js +65 -0
  150. package/dist/cli/chunks/{chunk-EN36AFRJ.js → chunk-WKNWRDA5.js} +1 -1
  151. package/dist/cli/chunks/{chunk-4X4GTEQY.js → chunk-WTHAVFIC.js} +1 -1
  152. package/dist/cli/chunks/{chunk-LPDT27EC.js → chunk-WWWX76QB.js} +2 -2
  153. package/dist/cli/chunks/{chunk-TCVEXQLL.js → chunk-XITTURP2.js} +2 -2
  154. package/dist/cli/chunks/{chunk-HV6LJ57R.js → chunk-XXFKN7LB.js} +1 -1
  155. package/dist/cli/chunks/{chunk-UJ2NRIAS.js → chunk-YEAC3DC6.js} +2 -2
  156. package/dist/cli/chunks/{chunk-FBASWZ7G.js → chunk-YKWMKRYB.js} +1 -1
  157. package/dist/cli/chunks/chunk-YKZV7O65.js +2 -0
  158. package/dist/cli/chunks/{chunk-PT6U3IIK.js → chunk-YW4RNZWT.js} +2 -2
  159. package/dist/cli/chunks/{chunk-H5DS7S4B.js → chunk-ZAAYOENS.js} +2 -2
  160. package/dist/cli/chunks/chunk-ZBKGX72B.js +13 -0
  161. package/dist/cli/chunks/{chunk-WVNLNXXI.js → chunk-ZBOWR4XT.js} +2 -2
  162. package/dist/cli/chunks/{chunk-FPX4JSFH.js → chunk-ZCVJCCQQ.js} +2 -2
  163. package/dist/cli/chunks/{chunk-OQWG7GOD.js → chunk-ZI3UO7QR.js} +8 -7
  164. package/dist/cli/chunks/{chunk-BPPEN7SS.js → chunk-ZNSMVQCG.js} +2 -2
  165. package/dist/cli/chunks/{chunk-EI4KYBNI.js → chunk-ZQSXG7FA.js} +2 -2
  166. package/dist/cli/chunks/{ci-M6WYL2XY.js → ci-B432UH6N.js} +2 -2
  167. package/dist/cli/chunks/{ci-output-JFC3WOHX.js → ci-output-GTHH7WYG.js} +2 -2
  168. package/dist/cli/chunks/{circuit-breaker-IMVBU6WP.js → circuit-breaker-VW75DLME.js} +2 -2
  169. package/dist/cli/chunks/{claude-flow-setup-M5T5ZUXX.js → claude-flow-setup-RE3IVT7U.js} +2 -2
  170. package/dist/cli/chunks/client-HNBZUEVV.js +2 -0
  171. package/dist/cli/chunks/{cline-installer-LSFAUHXC.js → cline-installer-NUBZZ55N.js} +2 -2
  172. package/dist/cli/chunks/{code-N3SWJ3XL.js → code-K6JCWKNW.js} +2 -2
  173. package/dist/cli/chunks/{code-index-extractor-WPGRG36B.js → code-index-extractor-I4ILB5JN.js} +2 -2
  174. package/dist/cli/chunks/{codex-installer-LKRCSDU5.js → codex-installer-AKQ5BCHU.js} +2 -2
  175. package/dist/cli/chunks/{completions-E5TBZ5AB.js → completions-V3KJVJF4.js} +2 -2
  176. package/dist/cli/chunks/{complexity-analyzer-3FOSOELK.js → complexity-analyzer-HRG7OOY3.js} +2 -2
  177. package/dist/cli/chunks/{continuedev-installer-ZSVFWMQB.js → continuedev-installer-CNTDONWG.js} +2 -2
  178. package/dist/cli/chunks/{copilot-installer-POSKHPZM.js → copilot-installer-XPWWIWYA.js} +2 -2
  179. package/dist/cli/chunks/core-handlers-TB7NLYXL.js +2 -0
  180. package/dist/cli/chunks/{cost-tracker-3UWDCNGC.js → cost-tracker-MFNLQ56K.js} +2 -2
  181. package/dist/cli/chunks/{coverage-HPBO67JF.js → coverage-KOIK654V.js} +3 -3
  182. package/dist/cli/chunks/cross-domain-router-MDSI553U.js +2 -0
  183. package/dist/cli/chunks/{cursor-installer-DSW3L62X.js → cursor-installer-S5MKLRQQ.js} +2 -2
  184. package/dist/cli/chunks/{daemon-2622ZCMJ.js → daemon-DUS6JYW6.js} +3 -3
  185. package/dist/cli/chunks/{daemon-33KTKDUM.js → daemon-Z7VPGWHY.js} +4 -4
  186. package/dist/cli/chunks/{dag-attention-scheduler-YFT5JMM6.js → dag-attention-scheduler-7CQ3ARNI.js} +2 -2
  187. package/dist/cli/chunks/{detect-COR3Q6BE.js → detect-WORBCAYF.js} +2 -2
  188. package/dist/cli/chunks/{dist-node-5TRNTNWI.js → dist-node-KRNI6OYW.js} +2 -2
  189. package/dist/cli/chunks/{domain-handler-TZC4GCF6.js → domain-handler-KRUR2VQG.js} +2 -2
  190. package/dist/cli/chunks/{domain-transfer-D2TENM3Y.js → domain-transfer-VEOVLV54.js} +2 -2
  191. package/dist/cli/chunks/dream-X7ISHFEE.js +2 -0
  192. package/dist/cli/chunks/embed-and-insert-pattern-QU3NQVA5.js +3 -0
  193. package/dist/cli/chunks/{eval-5ID5D77H.js → eval-AYQ7BSUP.js} +2 -2
  194. package/dist/cli/chunks/{experience-capture-middleware-MT4Z74TD.js → experience-capture-middleware-AWQBL6LT.js} +3 -3
  195. package/dist/cli/chunks/{fast-paths-MACRZGSE.js → fast-paths-V5EL4BYJ.js} +2 -2
  196. package/dist/cli/chunks/{feature-flags-LFNL6BHS.js → feature-flags-BXX7BP22.js} +2 -2
  197. package/dist/cli/chunks/{feature-flags-JZX6ZBUX.js → feature-flags-P34BPRWU.js} +2 -2
  198. package/dist/cli/chunks/{file-discovery-DA5BEAO2.js → file-discovery-MS6BKQ2U.js} +2 -2
  199. package/dist/cli/chunks/{fleet-35I4F7D2.js → fleet-7WVVPA4W.js} +3 -3
  200. package/dist/cli/chunks/{gnn-wrapper-2W3HM3ZI.js → gnn-wrapper-7WQ3LEPG.js} +2 -2
  201. package/dist/cli/chunks/{heartbeat-handler-6O2D2FU2.js → heartbeat-handler-P7QD3BZH.js} +4 -4
  202. package/dist/cli/chunks/heartbeat-scheduler-2C5XGFQT.js +2 -0
  203. package/dist/cli/chunks/hnsw-adapter-KCVMXLUL.js +2 -0
  204. package/dist/cli/chunks/hnsw-index-OGWZS6MP.js +2 -0
  205. package/dist/cli/chunks/{hnsw-legacy-bridge-GWBXEMQR.js → hnsw-legacy-bridge-Y3GAR3UP.js} +2 -2
  206. package/dist/cli/chunks/{hnswlib-node-TZ4VLARU.js → hnswlib-node-5QUXZQWQ.js} +2 -2
  207. package/dist/cli/chunks/{hooks-723GPTQ4.js → hooks-2D64KPRY.js} +10 -10
  208. package/dist/cli/chunks/{hypergraph-engine-ILRYFJM5.js → hypergraph-engine-5INXEZDP.js} +2 -2
  209. package/dist/cli/chunks/{hypergraph-handler-523TSK4P.js → hypergraph-handler-YJJJ3ESZ.js} +3 -3
  210. package/dist/cli/chunks/impact-analyzer-IBJ43GQL.js +2 -0
  211. package/dist/cli/chunks/{init-handler-RBDYIXNV.js → init-handler-QSFYJN5Q.js} +6 -6
  212. package/dist/cli/chunks/init-wizard-6XMFD5LP.js +2 -0
  213. package/dist/cli/chunks/kernel-UDUMUPB3.js +2 -0
  214. package/dist/cli/chunks/{kilocode-installer-E2QSX7Z7.js → kilocode-installer-3N3QGTJU.js} +2 -2
  215. package/dist/cli/chunks/{kiro-installer-EZZLT2R5.js → kiro-installer-GIWVTFZQ.js} +2 -2
  216. package/dist/cli/chunks/knowledge-graph-JRYTUG3W.js +2 -0
  217. package/dist/cli/chunks/learning-SGO2DQ5Q.js +122 -0
  218. package/dist/cli/chunks/llm-router-HBBULO65.js +37 -0
  219. package/dist/cli/chunks/llm-router-service-KF5ZALUQ.js +2 -0
  220. package/dist/cli/chunks/{load-QFQ3YOBQ.js → load-B5OFYB4H.js} +2 -2
  221. package/dist/cli/chunks/load-test-LHMBCNCH.js +2 -0
  222. package/dist/cli/chunks/{mcp-FPBBJJFY.js → mcp-FG3LNQR4.js} +2 -2
  223. package/dist/cli/chunks/{memory-RKN6KV75.js → memory-SPS7ZWLC.js} +5 -5
  224. package/dist/cli/chunks/memory-backend-WZHKHBZK.js +2 -0
  225. package/dist/cli/chunks/memory-handlers-P7FTPWP3.js +2 -0
  226. package/dist/cli/chunks/{multi-model-executor-OUNW3JM5.js → multi-model-executor-PUB7E6KH.js} +2 -2
  227. package/dist/cli/chunks/{opencode-installer-FXETYGGY.js → opencode-installer-TGCSGVG2.js} +2 -2
  228. package/dist/cli/chunks/{orchestrator-35UTVXSP.js → orchestrator-S5FTJFGB.js} +10 -13
  229. package/dist/cli/chunks/pattern-null-store-UZSHCRL2.js +2 -0
  230. package/dist/cli/chunks/{pattern-usage-recorder-YEQDS7DN.js → pattern-usage-recorder-LCXXGRAB.js} +2 -2
  231. package/dist/cli/chunks/{pipeline-YDJMWCAR.js → pipeline-ENOSRHKW.js} +2 -2
  232. package/dist/cli/chunks/{platform-L3LPNVKT.js → platform-WCRBO3Z7.js} +2 -2
  233. package/dist/cli/chunks/{plugin-55EYFJH5.js → plugin-JZ5ODJSU.js} +2 -2
  234. package/dist/cli/chunks/{prime-radiant-advanced-wasm-SUZT6RTM.js → prime-radiant-advanced-wasm-DPJUPNCG.js} +2 -2
  235. package/dist/cli/chunks/protocol-executor-42NLVDTC.js +2 -0
  236. package/dist/cli/chunks/{protocol-handler-QNBFRJNY.js → protocol-handler-IP4NLVY3.js} +2 -2
  237. package/dist/cli/chunks/{prove-3X5NQP4L.js → prove-P6FM4RU7.js} +2 -2
  238. package/dist/cli/chunks/qe-reasoning-bank-W74RSMMU.js +2 -0
  239. package/dist/cli/chunks/{quality-5ZONTNA6.js → quality-UHP4XO26.js} +2 -2
  240. package/dist/cli/chunks/{quality-gate-JPNREEVR.js → quality-gate-63DDBSGM.js} +3 -3
  241. package/dist/cli/chunks/queen-coordinator-XELKSRMS.js +2 -0
  242. package/dist/cli/chunks/real-embeddings-6SO5MFRQ.js +2 -0
  243. package/dist/cli/chunks/{roocode-installer-ZK73O2GD.js → roocode-installer-WTWUOFCH.js} +2 -2
  244. package/dist/cli/chunks/router-GTW2EQZV.js +2 -0
  245. package/dist/cli/chunks/routing-feedback-NVOBWEMK.js +2 -0
  246. package/dist/cli/chunks/{routing-handler-2UN5GRHJ.js → routing-handler-IK7HYERQ.js} +2 -2
  247. package/dist/cli/chunks/{ruvector-commands-EQQUEVWN.js → ruvector-commands-THLB4NIM.js} +2 -2
  248. package/dist/cli/chunks/rvf-dual-writer-5OPVMXNL.js +2 -0
  249. package/dist/cli/chunks/rvf-migration-adapter-5S4YFTX4.js +2 -0
  250. package/dist/cli/chunks/rvf-migration-coordinator-BUKCSHBD.js +2 -0
  251. package/dist/cli/chunks/rvf-native-adapter-LYSJHEVX.js +2 -0
  252. package/dist/cli/chunks/{rvf-store-integrity-66FEC3MD.js → rvf-store-integrity-ECTADZIN.js} +2 -2
  253. package/dist/cli/chunks/safe-db-AEB3JMLD.js +2 -0
  254. package/dist/cli/chunks/schedule-4QNPCQNT.js +2 -0
  255. package/dist/cli/chunks/scheduler-S3DFHX6A.js +2 -0
  256. package/dist/cli/chunks/{security-H7PEUUCV.js → security-HN4UXFXX.js} +3 -3
  257. package/dist/cli/chunks/{shared-rvf-adapter-XETNK4ID.js → shared-rvf-adapter-XJCPX5SN.js} +2 -2
  258. package/dist/cli/chunks/{shared-rvf-dual-writer-PXD54BWJ.js → shared-rvf-dual-writer-23X5PNY5.js} +2 -2
  259. package/dist/cli/chunks/sqlite-persistence-L2KISU2H.js +2 -0
  260. package/dist/cli/chunks/status-handler-ZXANTHKT.js +47 -0
  261. package/dist/cli/chunks/{structural-health-EIFBDCRI.js → structural-health-LR6WI3AH.js} +2 -2
  262. package/dist/cli/chunks/{sync-PECWQ64K.js → sync-4OEI3BC2.js} +2 -2
  263. package/dist/cli/chunks/{sync-3SU4H6R3.js → sync-I5IUJF33.js} +2 -2
  264. package/dist/cli/chunks/{task-handler-ZTUAT6YF.js → task-handler-R3ATJKU5.js} +2 -2
  265. package/dist/cli/chunks/{task-handlers-4RIO7LJJ.js → task-handlers-4X357AEW.js} +3 -3
  266. package/dist/cli/chunks/{test-EV7BSUTI.js → test-PR2B5H2F.js} +4 -4
  267. package/dist/cli/chunks/{test-scheduling-4WVI5DLE.js → test-scheduling-ZNOSQVLD.js} +3 -3
  268. package/dist/cli/chunks/token-bootstrap-LYXOI72S.js +2 -0
  269. package/dist/cli/chunks/{token-usage-FR3JOWYB.js → token-usage-R6EQZBX2.js} +2 -2
  270. package/dist/cli/chunks/{transformers-FENO33LA.js → transformers-COQWK27J.js} +2 -2
  271. package/dist/cli/chunks/{tree-sitter-wasm-parser-YFGJLSMB.js → tree-sitter-wasm-parser-LLIW2X3M.js} +2 -2
  272. package/dist/cli/chunks/{types-N3Y565RZ.js → types-MEYTZITK.js} +2 -2
  273. package/dist/cli/chunks/unified-memory-5KQMA7FV.js +2 -0
  274. package/dist/cli/chunks/unified-memory-hnsw-PQRPTYOD.js +2 -0
  275. package/dist/cli/chunks/unified-persistence-YDONVPJB.js +2 -0
  276. package/dist/cli/chunks/{upgrade-33ZVVZFW.js → upgrade-DVIJ456A.js} +2 -2
  277. package/dist/cli/chunks/{validate-CS5PXGDF.js → validate-UYTSMJ5X.js} +2 -2
  278. package/dist/cli/chunks/{validate-swarm-GRZPWYNM.js → validate-swarm-P32WCJR6.js} +2 -2
  279. package/dist/cli/chunks/{vibium-IO7N4VEE.js → vibium-JGVOLM6X.js} +2 -2
  280. package/dist/cli/chunks/visual-security-RJ7RTNGJ.js +2 -0
  281. package/dist/cli/chunks/{web-tree-sitter-UQXR5HHR.js → web-tree-sitter-5ZGNCTPN.js} +2 -2
  282. package/dist/cli/chunks/{windsurf-installer-BCKLSLZT.js → windsurf-installer-IILSSQAE.js} +2 -2
  283. package/dist/cli/chunks/{witness-chain-R3V26QWL.js → witness-chain-JZMXCG3B.js} +2 -2
  284. package/dist/cli/chunks/witness-chain-YXXXNGAM.js +2 -0
  285. package/dist/cli/chunks/{witness-key-manager-Y36HMPEM.js → witness-key-manager-UUTLNUIH.js} +2 -2
  286. package/dist/cli/chunks/{workflow-4TCIUUFQ.js → workflow-NS7HXVHZ.js} +4 -4
  287. package/dist/cli/chunks/workflow-orchestrator-5KUAVSTH.js +2 -0
  288. package/dist/cli/chunks/{wrappers-MD4JRHJH.js → wrappers-XKEDSV5W.js} +2 -2
  289. package/dist/cli/commands/learning.js +44 -0
  290. package/dist/cli/commands/llm-router.js +62 -27
  291. package/dist/cli/handlers/brain-handler.js +2 -0
  292. package/dist/cli/handlers/status-handler.js +18 -2
  293. package/dist/context/compaction/index.d.ts +1 -0
  294. package/dist/context/compaction/index.js +3 -0
  295. package/dist/context/compaction/instruction-provenance.d.ts +20 -0
  296. package/dist/context/compaction/instruction-provenance.js +18 -0
  297. package/dist/context/compaction/tier2-session-summary.d.ts +8 -0
  298. package/dist/context/compaction/tier2-session-summary.js +20 -0
  299. package/dist/context/compaction/tier3-llm-compact.d.ts +4 -0
  300. package/dist/context/compaction/tier3-llm-compact.js +32 -4
  301. package/dist/init/phases/07-hooks.js +0 -3
  302. package/dist/integrations/embeddings/base/EmbeddingGenerator.js +4 -2
  303. package/dist/integrations/ruvector/brain-exporter.d.ts +1 -1
  304. package/dist/integrations/ruvector/brain-exporter.js +15 -3
  305. package/dist/integrations/ruvector/brain-rvf-exporter.d.ts +4 -0
  306. package/dist/integrations/ruvector/brain-rvf-exporter.js +118 -37
  307. package/dist/integrations/ruvector/brain-search.d.ts +1 -1
  308. package/dist/integrations/ruvector/brain-search.js +1 -1
  309. package/dist/integrations/ruvector/brain-shared.d.ts +4 -2
  310. package/dist/integrations/ruvector/brain-shared.js +11 -4
  311. package/dist/integrations/ruvector/brain-table-ddl.d.ts +2 -2
  312. package/dist/integrations/ruvector/brain-table-ddl.js +20 -8
  313. package/dist/integrations/ruvector/cognitive-container.js +1 -1
  314. package/dist/integrations/ruvector/rvf-dual-writer.d.ts +4 -0
  315. package/dist/integrations/ruvector/rvf-dual-writer.js +57 -9
  316. package/dist/integrations/ruvector/rvf-store-integrity.js +1 -1
  317. package/dist/kernel/unified-memory-schemas.d.ts +4 -3
  318. package/dist/kernel/unified-memory-schemas.js +19 -9
  319. package/dist/kernel/unified-memory.d.ts +2 -0
  320. package/dist/kernel/unified-memory.js +19 -0
  321. package/dist/learning/embed-and-insert-pattern.js +6 -3
  322. package/dist/learning/embedding-space.d.ts +50 -0
  323. package/dist/learning/embedding-space.js +128 -0
  324. package/dist/learning/evidence-admission-store.d.ts +15 -0
  325. package/dist/learning/evidence-admission-store.js +68 -0
  326. package/dist/learning/index.d.ts +4 -0
  327. package/dist/learning/index.js +2 -0
  328. package/dist/learning/learning-evidence-admission.d.ts +44 -0
  329. package/dist/learning/learning-evidence-admission.js +97 -0
  330. package/dist/learning/pattern-store.d.ts +5 -0
  331. package/dist/learning/pattern-store.js +36 -7
  332. package/dist/learning/qe-flywheel/coupled-anchor.d.ts +8 -0
  333. package/dist/learning/qe-flywheel/coupled-anchor.js +46 -3
  334. package/dist/learning/qe-reasoning-bank.js +4 -0
  335. package/dist/learning/real-embeddings.d.ts +3 -0
  336. package/dist/learning/real-embeddings.js +40 -3
  337. package/dist/learning/real-qe-reasoning-bank.js +7 -3
  338. package/dist/learning/rvf-pattern-migration.d.ts +2 -0
  339. package/dist/learning/rvf-pattern-migration.js +5 -3
  340. package/dist/learning/rvf-pattern-store.d.ts +3 -0
  341. package/dist/learning/rvf-pattern-store.js +21 -1
  342. package/dist/learning/sqlite-persistence.d.ts +7 -2
  343. package/dist/learning/sqlite-persistence.js +58 -21
  344. package/dist/mcp/bundle.js +607 -443
  345. package/dist/mcp/handlers/memory-handlers.js +6 -2
  346. package/dist/migrations/20260830_add_learning_evidence_tables.d.ts +7 -0
  347. package/dist/migrations/20260830_add_learning_evidence_tables.js +173 -0
  348. package/dist/persistence/rvf-migration-adapter.d.ts +2 -0
  349. package/dist/persistence/rvf-migration-adapter.js +56 -12
  350. package/dist/shared/llm/billing-modes.d.ts +5 -0
  351. package/dist/shared/llm/billing-modes.js +42 -3
  352. package/dist/shared/llm/index.d.ts +4 -1
  353. package/dist/shared/llm/index.js +5 -1
  354. package/dist/shared/llm/interfaces.d.ts +34 -3
  355. package/dist/shared/llm/interfaces.js +17 -0
  356. package/dist/shared/llm/llm-router-service.d.ts +8 -0
  357. package/dist/shared/llm/llm-router-service.js +21 -3
  358. package/dist/shared/llm/provider-manager.js +8 -1
  359. package/dist/shared/llm/provider-registry.d.ts +135 -0
  360. package/dist/shared/llm/provider-registry.js +227 -0
  361. package/dist/shared/llm/providers/external-cli.d.ts +129 -0
  362. package/dist/shared/llm/providers/external-cli.js +385 -0
  363. package/dist/shared/llm/router/config-store.d.ts +14 -0
  364. package/dist/shared/llm/router/config-store.js +102 -14
  365. package/dist/shared/llm/router/external-provider-config.d.ts +47 -0
  366. package/dist/shared/llm/router/external-provider-config.js +105 -0
  367. package/dist/shared/llm/router/types.d.ts +50 -4
  368. package/dist/shared/llm/router/types.js +10 -0
  369. package/dist/shared/llm/translation/message-formatter.js +7 -1
  370. package/dist/shared/sql-safety.js +4 -0
  371. package/dist/skills/qe-court/cli.d.ts +3 -0
  372. package/dist/skills/qe-court/cli.js +54 -0
  373. package/dist/verification/adversarial-verify/calibrate.d.ts +46 -2
  374. package/dist/verification/adversarial-verify/calibrate.js +104 -2
  375. package/dist/verification/adversarial-verify/index.d.ts +1 -1
  376. package/dist/verification/adversarial-verify/index.js +1 -1
  377. package/package.json +12 -3
  378. package/dist/cli/chunks/adapter-YBSKQFQ4.js +0 -2
  379. package/dist/cli/chunks/aqe-learning-engine-3H7DRDD4.js +0 -2
  380. package/dist/cli/chunks/base-ZTD6PFD7.js +0 -2
  381. package/dist/cli/chunks/brain-handler-FV2AJZ4A.js +0 -89
  382. package/dist/cli/chunks/browser-workflow-GKWDM25H.js +0 -2
  383. package/dist/cli/chunks/chunk-2CBEQV6R.js +0 -2
  384. package/dist/cli/chunks/chunk-4ZQOJ7Q2.js +0 -2
  385. package/dist/cli/chunks/chunk-CYQRZ645.js +0 -2
  386. package/dist/cli/chunks/chunk-FXKOHZBN.js +0 -2
  387. package/dist/cli/chunks/chunk-GJTGHVAX.js +0 -65
  388. package/dist/cli/chunks/chunk-GP5QBJVO.js +0 -3
  389. package/dist/cli/chunks/chunk-JGXP67BP.js +0 -2
  390. package/dist/cli/chunks/chunk-SW56PB22.js +0 -2
  391. package/dist/cli/chunks/chunk-T5M4TZRB.js +0 -12
  392. package/dist/cli/chunks/chunk-UDSNWQQI.js +0 -2
  393. package/dist/cli/chunks/chunk-VC77SDNH.js +0 -9
  394. package/dist/cli/chunks/chunk-WRMJWQUI.js +0 -12
  395. package/dist/cli/chunks/chunk-YXUFGDGS.js +0 -2
  396. package/dist/cli/chunks/client-4Q5JE7RY.js +0 -2
  397. package/dist/cli/chunks/core-handlers-4GKEYGPS.js +0 -2
  398. package/dist/cli/chunks/cross-domain-router-PBJ2ZGPJ.js +0 -2
  399. package/dist/cli/chunks/dream-4EXVFHUJ.js +0 -2
  400. package/dist/cli/chunks/embed-and-insert-pattern-COAZZGS6.js +0 -3
  401. package/dist/cli/chunks/heartbeat-scheduler-UNEYR3WR.js +0 -2
  402. package/dist/cli/chunks/hnsw-adapter-JQG66H4L.js +0 -2
  403. package/dist/cli/chunks/hnsw-index-2NOROILM.js +0 -2
  404. package/dist/cli/chunks/hybrid-router-FXTAJATE.js +0 -2
  405. package/dist/cli/chunks/impact-analyzer-VHZSBWMT.js +0 -2
  406. package/dist/cli/chunks/init-wizard-EML4OET2.js +0 -2
  407. package/dist/cli/chunks/kernel-TMRJOEO2.js +0 -2
  408. package/dist/cli/chunks/knowledge-graph-FMKVCMJQ.js +0 -2
  409. package/dist/cli/chunks/learning-II2U3EV2.js +0 -119
  410. package/dist/cli/chunks/llm-router-WC66OXZH.js +0 -36
  411. package/dist/cli/chunks/llm-router-service-GOSVGMKM.js +0 -2
  412. package/dist/cli/chunks/load-test-WIRGDZIA.js +0 -2
  413. package/dist/cli/chunks/memory-backend-XBBIXDZU.js +0 -2
  414. package/dist/cli/chunks/memory-handlers-SQHNK3DK.js +0 -2
  415. package/dist/cli/chunks/pattern-null-store-PFLKHRVU.js +0 -2
  416. package/dist/cli/chunks/protocol-executor-GAMNY2JB.js +0 -2
  417. package/dist/cli/chunks/provider-manager-VTIG5AKU.js +0 -2
  418. package/dist/cli/chunks/qe-reasoning-bank-KAX63PXV.js +0 -2
  419. package/dist/cli/chunks/queen-coordinator-UYW2MDKI.js +0 -2
  420. package/dist/cli/chunks/real-embeddings-HHR75AR5.js +0 -2
  421. package/dist/cli/chunks/router-NVDU7IIA.js +0 -2
  422. package/dist/cli/chunks/routing-feedback-KHR3J2J3.js +0 -2
  423. package/dist/cli/chunks/rvf-dual-writer-43L75WLF.js +0 -2
  424. package/dist/cli/chunks/rvf-migration-adapter-Y5OVCYTO.js +0 -2
  425. package/dist/cli/chunks/rvf-migration-coordinator-NEPDFB3N.js +0 -2
  426. package/dist/cli/chunks/rvf-native-adapter-PQ4IOTZU.js +0 -2
  427. package/dist/cli/chunks/safe-db-QHSFV63M.js +0 -2
  428. package/dist/cli/chunks/schedule-JGZFHJGM.js +0 -2
  429. package/dist/cli/chunks/scheduler-NGCDQIW5.js +0 -2
  430. package/dist/cli/chunks/sqlite-persistence-3JSNWL6I.js +0 -2
  431. package/dist/cli/chunks/status-handler-CTXOH3FX.js +0 -47
  432. package/dist/cli/chunks/token-bootstrap-3P22AYZ4.js +0 -2
  433. package/dist/cli/chunks/unified-memory-4VO5KSHP.js +0 -2
  434. package/dist/cli/chunks/unified-memory-hnsw-ZR2FZXOB.js +0 -2
  435. package/dist/cli/chunks/unified-persistence-VFPW5MFM.js +0 -2
  436. package/dist/cli/chunks/visual-security-SNADRUPR.js +0 -2
  437. package/dist/cli/chunks/witness-chain-2TXUTEJE.js +0 -2
  438. package/dist/cli/chunks/workflow-orchestrator-ON6UYRVE.js +0 -2
@@ -0,0 +1,385 @@
1
+ /**
2
+ * Agentic QE v3 — Generic external CLI provider (ADR-127 / issue #628)
3
+ *
4
+ * A provider built from a *declaration* rather than from code. It generalizes
5
+ * the shape AQE has already shipped twice — `providers/codex.ts` and
6
+ * `providers/claude-code.ts` — into something a downstream integrator can
7
+ * obtain by writing JSON:
8
+ *
9
+ * spawn a host CLI → feed it a prompt on stdin → read the completion from
10
+ * stdout → attribute the usage honestly.
11
+ *
12
+ * WHY DECLARATIVE: the requester (agentic-kit) drives AQE as spawned
13
+ * subprocesses — the `aqe` CLI bundle and the MCP server bundle are separate
14
+ * esbuild artifacts — so an in-process `registerProvider()` call cannot reach
15
+ * them. A declaration in `.agentic-qe/llm-config.json` reaches both, and does
16
+ * so without AQE ever importing third-party code. See ADR-127 for why the
17
+ * dynamic-`import()` plugin design was rejected.
18
+ *
19
+ * LOAD-BEARING: `stripEnv` removes named variables from the child environment.
20
+ * A host that bills against a subscription must be able to guarantee an API
21
+ * key is not silently present to flip it back to metered billing — the same
22
+ * discipline `codex.ts` enforces for OPENAI_API_KEY / CODEX_API_KEY.
23
+ */
24
+ import { spawn } from 'node:child_process';
25
+ import { accessSync, constants as fsConstants, statSync } from 'node:fs';
26
+ import { delimiter, join } from 'node:path';
27
+ import { createLLMError, } from '../interfaces.js';
28
+ export const DEFAULT_EXTERNAL_CLI_TIMEOUT_MS = 180_000;
29
+ export const DEFAULT_EXTERNAL_CLI_CONCURRENCY = 2;
30
+ export const EXTERNAL_CLI_DEFAULT_MODEL = 'default';
31
+ /**
32
+ * Validate a declaration. Returns an error message, or `undefined` when the
33
+ * declaration is usable.
34
+ *
35
+ * Deliberately does NOT check that the binary exists — that is a runtime
36
+ * availability question answered by `isAvailable()`. A declaration for a host
37
+ * that is not installed yet is valid-but-unavailable, not malformed, so a
38
+ * config file stays portable across machines.
39
+ */
40
+ export function validateExternalCliConfig(type, config) {
41
+ if (!config || typeof config !== 'object') {
42
+ return `externalProviders["${type}"] must be an object`;
43
+ }
44
+ const c = config;
45
+ if (c.kind !== 'cli') {
46
+ return `externalProviders["${type}"].kind must be "cli" (got ${JSON.stringify(c.kind)})`;
47
+ }
48
+ if (!Array.isArray(c.command) || c.command.length === 0) {
49
+ return `externalProviders["${type}"].command must be a non-empty string array`;
50
+ }
51
+ if (!c.command.every((part) => typeof part === 'string' && part.length > 0)) {
52
+ return `externalProviders["${type}"].command must contain only non-empty strings`;
53
+ }
54
+ if (c.billingMode !== undefined &&
55
+ !['subscription', 'metered-api', 'metered-capped', 'local'].includes(c.billingMode)) {
56
+ return `externalProviders["${type}"].billingMode "${String(c.billingMode)}" is not a valid billing mode`;
57
+ }
58
+ if (c.models !== undefined && (!Array.isArray(c.models) || c.models.some((m) => typeof m !== 'string'))) {
59
+ return `externalProviders["${type}"].models must be a string array`;
60
+ }
61
+ if (c.timeoutMs !== undefined && (typeof c.timeoutMs !== 'number' || c.timeoutMs <= 0)) {
62
+ return `externalProviders["${type}"].timeoutMs must be a positive number`;
63
+ }
64
+ if (c.maxConcurrency !== undefined &&
65
+ (typeof c.maxConcurrency !== 'number' || c.maxConcurrency < 1)) {
66
+ return `externalProviders["${type}"].maxConcurrency must be >= 1`;
67
+ }
68
+ if (c.stripEnv !== undefined &&
69
+ (!Array.isArray(c.stripEnv) || c.stripEnv.some((k) => typeof k !== 'string'))) {
70
+ return `externalProviders["${type}"].stripEnv must be a string array`;
71
+ }
72
+ if ('apiKey' in c) {
73
+ // Matches saveRouterConfigFile's never-persist-keys discipline. Reported
74
+ // as an error so the key is noticed and removed rather than silently kept
75
+ // in a file that gets committed.
76
+ return `externalProviders["${type}"] must not contain an apiKey — keys belong in the environment, not in llm-config.json`;
77
+ }
78
+ return undefined;
79
+ }
80
+ // ============================================================================
81
+ // Provider
82
+ // ============================================================================
83
+ export class ExternalCliProvider {
84
+ type;
85
+ name;
86
+ billingMode;
87
+ config;
88
+ requestCounter = 0;
89
+ availabilityCache;
90
+ active = 0;
91
+ queue = [];
92
+ constructor(type, config) {
93
+ const problem = validateExternalCliConfig(type, config);
94
+ if (problem) {
95
+ throw createLLMError(problem, 'INVALID_REGISTRATION', { retryable: false });
96
+ }
97
+ this.type = type;
98
+ this.config = config;
99
+ this.name = config.displayName ?? type;
100
+ // Undeclared => metered-api: assume it costs money and requires a cap.
101
+ this.billingMode = config.billingMode ?? 'metered-api';
102
+ }
103
+ /**
104
+ * Is the host's binary present and executable?
105
+ *
106
+ * Deliberately does NOT run it. AQE cannot know what invoking an arbitrary
107
+ * host costs — a declared host has no `--version` contract it agreed to, and
108
+ * spawning it with an empty prompt could burn a real request against the
109
+ * user's plan just to answer "are you installed?". Resolving the binary on
110
+ * PATH answers the question for free.
111
+ */
112
+ async isAvailable() {
113
+ const now = Date.now();
114
+ if (this.availabilityCache && now - this.availabilityCache.at < 60_000) {
115
+ return this.availabilityCache.available;
116
+ }
117
+ const available = resolvesOnPath(this.config.command[0]);
118
+ this.availabilityCache = { at: now, available };
119
+ return available;
120
+ }
121
+ async healthCheck() {
122
+ const start = Date.now();
123
+ const available = await this.isAvailable();
124
+ const latencyMs = Date.now() - start;
125
+ if (!available) {
126
+ return {
127
+ healthy: false,
128
+ latencyMs,
129
+ error: `External provider "${this.type}" could not run "${this.config.command[0]}". ` +
130
+ `Check that the host is installed and on PATH.`,
131
+ };
132
+ }
133
+ return {
134
+ healthy: true,
135
+ latencyMs,
136
+ models: this.getSupportedModels(),
137
+ details: {
138
+ command: this.config.command[0],
139
+ billing: this.billingMode,
140
+ external: true,
141
+ },
142
+ };
143
+ }
144
+ async generate(input, options) {
145
+ const model = options?.model ?? this.defaultModel();
146
+ const requestId = `${this.type}-${++this.requestCounter}-${Date.now()}`;
147
+ const prompt = flattenPrompt(input, options?.systemPrompt);
148
+ const timeoutMs = options?.timeoutMs ?? this.config.timeoutMs ?? DEFAULT_EXTERNAL_CLI_TIMEOUT_MS;
149
+ const extraArgs = [];
150
+ if (this.config.modelFlag && model && model !== EXTERNAL_CLI_DEFAULT_MODEL) {
151
+ extraArgs.push(this.config.modelFlag, model);
152
+ }
153
+ const start = Date.now();
154
+ const { code, stdout, stderr } = await this.withConcurrency(() => this.spawnHost(extraArgs, prompt, timeoutMs));
155
+ const latencyMs = Date.now() - start;
156
+ const content = stdout.trim();
157
+ if (code !== 0 && !content) {
158
+ throw this.classifyError(stderr || `${this.type} exited with code ${code}`, model);
159
+ }
160
+ // No per-token receipt from a generic CLI. Estimate for visibility only
161
+ // (~4 chars/token) and mark the cost `local-estimate` so ADR-126's
162
+ // provenance rule holds: an estimate is never reported as a measurement.
163
+ const promptTokens = Math.ceil(prompt.length / 4);
164
+ const completionTokens = Math.ceil(content.length / 4);
165
+ const usage = {
166
+ promptTokens,
167
+ completionTokens,
168
+ totalTokens: promptTokens + completionTokens,
169
+ };
170
+ return {
171
+ content,
172
+ model,
173
+ provider: this.type,
174
+ usage,
175
+ cost: {
176
+ inputCost: 0,
177
+ outputCost: 0,
178
+ totalCost: 0,
179
+ currency: 'USD',
180
+ // Even for a subscription host this is an estimate, not a receipt:
181
+ // AQE did not observe the billing event and cannot verify the mode
182
+ // the declaration claimed.
183
+ source: 'local-estimate',
184
+ },
185
+ latencyMs,
186
+ finishReason: 'stop',
187
+ cached: false,
188
+ requestId,
189
+ };
190
+ }
191
+ /**
192
+ * ADR-127: a generic CLI contract cannot promise an embeddings endpoint.
193
+ * Throws `EMBEDDING_UNSUPPORTED` — a distinct, non-retryable signal so the
194
+ * caller falls back to a provider that has one, and never receives a
195
+ * fabricated vector.
196
+ */
197
+ async embed(_text, _options) {
198
+ throw createLLMError(`External provider "${this.type}" does not support embeddings. ` +
199
+ `Use a provider with an embeddings endpoint (openai, ollama, cognitum).`, 'EMBEDDING_UNSUPPORTED', { provider: this.type, retryable: false });
200
+ }
201
+ async complete(prompt, options) {
202
+ const response = await this.generate(prompt, {
203
+ model: options?.model,
204
+ temperature: options?.temperature,
205
+ maxTokens: options?.maxTokens,
206
+ });
207
+ return {
208
+ completion: response.content,
209
+ model: response.model,
210
+ provider: this.type,
211
+ usage: response.usage,
212
+ latencyMs: response.latencyMs,
213
+ cached: response.cached,
214
+ };
215
+ }
216
+ getConfig() {
217
+ return { ...this.config };
218
+ }
219
+ getSupportedModels() {
220
+ return [...(this.config.models ?? [EXTERNAL_CLI_DEFAULT_MODEL])];
221
+ }
222
+ /**
223
+ * Unknown. A declared host does not tell AQE its per-token price, and
224
+ * inventing one would feed fabricated numbers into cost reporting.
225
+ */
226
+ getCostPerToken() {
227
+ return { input: 0, output: 0 };
228
+ }
229
+ async dispose() {
230
+ // No persistent resources — each request is its own subprocess.
231
+ }
232
+ // -- internals -------------------------------------------------------------
233
+ defaultModel() {
234
+ return this.config.defaultModel ?? this.config.models?.[0] ?? EXTERNAL_CLI_DEFAULT_MODEL;
235
+ }
236
+ /**
237
+ * Child env with the declared keys removed (LOAD-BEARING — see file header).
238
+ * Static so tests can assert the strip without spawning anything.
239
+ */
240
+ static childEnv(stripEnv = [], base = process.env) {
241
+ const env = { ...base };
242
+ for (const key of stripEnv) {
243
+ delete env[key];
244
+ }
245
+ return env;
246
+ }
247
+ classifyError(message, model) {
248
+ const lower = message.toLowerCase();
249
+ if (lower.includes('rate limit') ||
250
+ lower.includes('usage limit') ||
251
+ lower.includes('429') ||
252
+ lower.includes('quota')) {
253
+ return createLLMError(message, 'RATE_LIMITED', {
254
+ provider: this.type,
255
+ model,
256
+ retryable: true,
257
+ retryAfterMs: 60_000,
258
+ });
259
+ }
260
+ return createLLMError(message, 'PROVIDER_UNAVAILABLE', {
261
+ provider: this.type,
262
+ model,
263
+ retryable: true,
264
+ });
265
+ }
266
+ spawnHost(extraArgs, stdin, timeoutMs) {
267
+ const [binary, ...baseArgs] = this.config.command;
268
+ const args = [...baseArgs, ...extraArgs];
269
+ return new Promise((resolve, reject) => {
270
+ const child = spawn(binary, args, {
271
+ env: ExternalCliProvider.childEnv(this.config.stripEnv),
272
+ stdio: ['pipe', 'pipe', 'pipe'],
273
+ });
274
+ let stdout = '';
275
+ let stderr = '';
276
+ let settled = false;
277
+ const timer = setTimeout(() => {
278
+ if (settled)
279
+ return;
280
+ settled = true;
281
+ child.kill('SIGKILL');
282
+ reject(createLLMError(`${this.type} timed out after ${timeoutMs}ms`, 'TIMEOUT', {
283
+ provider: this.type,
284
+ retryable: true,
285
+ }));
286
+ }, timeoutMs);
287
+ child.stdout.on('data', (d) => (stdout += d.toString()));
288
+ child.stderr.on('data', (d) => (stderr += d.toString()));
289
+ child.on('error', (err) => {
290
+ if (settled)
291
+ return;
292
+ settled = true;
293
+ clearTimeout(timer);
294
+ reject(createLLMError(`Failed to spawn "${binary}" for provider "${this.type}": ${err.message}`, 'PROVIDER_UNAVAILABLE', { provider: this.type, retryable: false, cause: err }));
295
+ });
296
+ child.on('close', (code) => {
297
+ if (settled)
298
+ return;
299
+ settled = true;
300
+ clearTimeout(timer);
301
+ resolve({ code: code ?? 0, stdout, stderr });
302
+ });
303
+ // EPIPE guard: a host that exits before reading stdin (e.g. not logged
304
+ // in) makes the write emit 'error' on the stdin stream. Without a
305
+ // listener Node throws an uncaught exception that kills the calling
306
+ // process. The close/error handlers above carry the real outcome.
307
+ child.stdin.on('error', () => {
308
+ /* swallow EPIPE — outcome reported via close/error */
309
+ });
310
+ try {
311
+ child.stdin.write(stdin);
312
+ child.stdin.end();
313
+ }
314
+ catch {
315
+ /* stream already torn down — process handlers report the failure */
316
+ }
317
+ });
318
+ }
319
+ /** Semaphore so we never spawn unbounded host subprocesses. */
320
+ async withConcurrency(fn) {
321
+ const limit = this.config.maxConcurrency ?? DEFAULT_EXTERNAL_CLI_CONCURRENCY;
322
+ if (this.active >= limit) {
323
+ await new Promise((resolve) => this.queue.push(resolve));
324
+ }
325
+ this.active++;
326
+ try {
327
+ return await fn();
328
+ }
329
+ finally {
330
+ this.active--;
331
+ const next = this.queue.shift();
332
+ if (next)
333
+ next();
334
+ }
335
+ }
336
+ }
337
+ // ============================================================================
338
+ // Helpers
339
+ // ============================================================================
340
+ /**
341
+ * Does this binary resolve to something executable, without running it?
342
+ * An absolute or explicitly-relative path is checked directly; a bare name is
343
+ * looked up across PATH the way the shell would.
344
+ */
345
+ export function resolvesOnPath(binary, env = process.env) {
346
+ const isExecutable = (candidate) => {
347
+ try {
348
+ accessSync(candidate, fsConstants.X_OK);
349
+ return statSync(candidate).isFile();
350
+ }
351
+ catch {
352
+ return false;
353
+ }
354
+ };
355
+ if (binary.includes('/') || binary.includes('\\')) {
356
+ return isExecutable(binary);
357
+ }
358
+ const entries = (env.PATH ?? '').split(delimiter).filter(Boolean);
359
+ // PATHEXT matters on Windows, where `my-host` is really `my-host.cmd`.
360
+ const extensions = process.platform === 'win32'
361
+ ? ['', ...(env.PATHEXT ?? '.COM;.EXE;.BAT;.CMD').split(';').filter(Boolean)]
362
+ : [''];
363
+ return entries.some((dir) => extensions.some((ext) => isExecutable(join(dir, binary + ext))));
364
+ }
365
+ /**
366
+ * Flatten messages into a single prompt. A generic CLI has no role protocol,
367
+ * so system content is hoisted into a preamble — the same strategy
368
+ * `codex.ts` uses and the reason `message-formatter` falls back to
369
+ * `'first-message'` for unknown providers.
370
+ */
371
+ export function flattenPrompt(input, systemPrompt) {
372
+ if (typeof input === 'string') {
373
+ return systemPrompt ? `${systemPrompt}\n\n${input}` : input;
374
+ }
375
+ const systemParts = input.filter((m) => m.role === 'system').map((m) => m.content);
376
+ if (systemPrompt)
377
+ systemParts.unshift(systemPrompt);
378
+ const convo = input
379
+ .filter((m) => m.role !== 'system')
380
+ .map((m) => `${m.role === 'assistant' ? 'Assistant' : 'User'}: ${m.content}`)
381
+ .join('\n\n');
382
+ const preamble = systemParts.length > 0 ? `${systemParts.join('\n\n')}\n\n` : '';
383
+ return `${preamble}${convo}`;
384
+ }
385
+ //# sourceMappingURL=external-cli.js.map
@@ -20,6 +20,20 @@
20
20
  import type { RouterConfig, ExtendedProviderType, AgentProviderOverride } from './types.js';
21
21
  /** Filename under .agentic-qe/ */
22
22
  export declare const ROUTER_CONFIG_FILENAME = "llm-config.json";
23
+ /**
24
+ * Can `ProviderManager.createProvider()` build this type? True for the
25
+ * built-in constructible set, and for any provider registered per ADR-127.
26
+ */
27
+ export declare function isRuntimeConstructible(provider: ExtendedProviderType): boolean;
28
+ /**
29
+ * Every provider type the router may legitimately be pointed at: the ones AQE
30
+ * ships plus the ones a downstream integrator registered (ADR-127).
31
+ *
32
+ * `ALL_PROVIDER_TYPES` deliberately stays frozen as "what AQE ships" — it is
33
+ * exported public API that consumers read — so validation paths call this
34
+ * instead of widening that constant at runtime.
35
+ */
36
+ export declare function allSelectableProviderTypes(): readonly ExtendedProviderType[];
23
37
  /**
24
38
  * Return the resolved config-file path for a project.
25
39
  * Caller may override projectRoot for tests; defaults to findProjectRoot().
@@ -20,6 +20,9 @@
20
20
  import * as fs from 'fs';
21
21
  import * as path from 'path';
22
22
  import { findProjectRoot } from '../../../kernel/unified-memory.js';
23
+ import { isBuiltinExtendedProviderType } from './types.js';
24
+ import { isRegisteredProvider, registeredProviderTypes, } from '../provider-registry.js';
25
+ import { registerExternalProviders, sanitizeExternalProviders, } from './external-provider-config.js';
23
26
  import { DEFAULT_ROUTER_CONFIG, ALL_PROVIDER_TYPES } from './types.js';
24
27
  import { createLogger } from '../../../logging/logger-factory.js';
25
28
  // `console.warn` is not usable here: in MCP stdio mode stdout is the protocol
@@ -35,7 +38,7 @@ export const ROUTER_CONFIG_FILENAME = 'llm-config.json';
35
38
  * Empty array means "local provider, no key required". The set of
36
39
  * runtime-constructible providers is narrower than ExtendedProviderType
37
40
  * (e.g. onnx is in the type system but ProviderManager doesn't yet
38
- * have a case for it) — see RUNTIME_CONSTRUCTIBLE_PROVIDERS below.
41
+ * have a case for it) — see isRuntimeConstructible() below.
39
42
  */
40
43
  const PROVIDER_ENV_KEYS = {
41
44
  claude: ['ANTHROPIC_API_KEY', 'CLAUDE_API_KEY'],
@@ -58,14 +61,16 @@ const PROVIDER_ENV_KEYS = {
58
61
  onnx: [], // local
59
62
  };
60
63
  /**
61
- * Providers that ProviderManager.createProvider() can actually
62
- * instantiate today. Anything not in this set is dropped from the
64
+ * Built-in providers that ProviderManager.createProvider() can actually
65
+ * instantiate today. Anything not constructible is dropped from the
63
66
  * "available" set even if its env keys are present, so we don't emit
64
67
  * a "Failed to create X provider" warning at every kernel boot.
65
68
  *
66
69
  * Keep this in sync with src/shared/llm/provider-manager.ts:createProvider().
70
+ * ADR-127: externally registered providers are constructible too — call
71
+ * `isRuntimeConstructible()` rather than reading this set directly.
67
72
  */
68
- const RUNTIME_CONSTRUCTIBLE_PROVIDERS = new Set([
73
+ const BUILTIN_CONSTRUCTIBLE_PROVIDERS = new Set([
69
74
  'claude',
70
75
  'claude-code',
71
76
  'codex',
@@ -77,6 +82,33 @@ const RUNTIME_CONSTRUCTIBLE_PROVIDERS = new Set([
77
82
  'bedrock',
78
83
  'cognitum',
79
84
  ]);
85
+ /**
86
+ * Can `ProviderManager.createProvider()` build this type? True for the
87
+ * built-in constructible set, and for any provider registered per ADR-127.
88
+ */
89
+ export function isRuntimeConstructible(provider) {
90
+ return BUILTIN_CONSTRUCTIBLE_PROVIDERS.has(provider) || isRegisteredProvider(provider);
91
+ }
92
+ /**
93
+ * Every provider type the router may legitimately be pointed at: the ones AQE
94
+ * ships plus the ones a downstream integrator registered (ADR-127).
95
+ *
96
+ * `ALL_PROVIDER_TYPES` deliberately stays frozen as "what AQE ships" — it is
97
+ * exported public API that consumers read — so validation paths call this
98
+ * instead of widening that constant at runtime.
99
+ */
100
+ export function allSelectableProviderTypes() {
101
+ return [...ALL_PROVIDER_TYPES, ...registeredProviderTypes()];
102
+ }
103
+ /**
104
+ * Env keys that indicate a provider is configured. Returns `[]` for a provider
105
+ * AQE does not ship — an externally registered provider (ADR-127) brings its
106
+ * own availability check (a binary on PATH, typically) rather than an env key,
107
+ * so "no keys" is the correct answer, not a crash.
108
+ */
109
+ function envKeysFor(provider) {
110
+ return isBuiltinExtendedProviderType(provider) ? PROVIDER_ENV_KEYS[provider] : [];
111
+ }
80
112
  /**
81
113
  * Return the resolved config-file path for a project.
82
114
  * Caller may override projectRoot for tests; defaults to findProjectRoot().
@@ -91,13 +123,15 @@ export function getRouterConfigPath(projectRoot) {
91
123
  */
92
124
  export function detectAvailableProvidersFromEnv(env = process.env) {
93
125
  const available = new Set();
94
- for (const provider of ALL_PROVIDER_TYPES) {
95
- if (!RUNTIME_CONSTRUCTIBLE_PROVIDERS.has(provider)) {
126
+ // ADR-127: iterate registered providers alongside built-ins so an external
127
+ // host is detectable/enable-able without the user hand-editing `providers`.
128
+ for (const provider of allSelectableProviderTypes()) {
129
+ if (!isRuntimeConstructible(provider)) {
96
130
  // ProviderManager has no case for this provider yet (e.g. onnx).
97
131
  // Pretending it's available leads to "Failed to create" noise.
98
132
  continue;
99
133
  }
100
- const keys = PROVIDER_ENV_KEYS[provider];
134
+ const keys = envKeysFor(provider);
101
135
  if (keys.length === 0) {
102
136
  // local providers — always available if their binary is, but at
103
137
  // config-merge time we treat them as "potentially available"
@@ -207,6 +241,19 @@ export function mergeRouterConfig(base, override) {
207
241
  };
208
242
  }
209
243
  }
244
+ // ADR-127: `externalProviders` gets the same keyed-object merge semantics —
245
+ // declaring one host must not wipe the others.
246
+ if (base.externalProviders || override.externalProviders) {
247
+ merged.externalProviders = { ...(base.externalProviders ?? {}) };
248
+ for (const [type, cfg] of Object.entries(override.externalProviders ?? {})) {
249
+ if (!cfg)
250
+ continue;
251
+ merged.externalProviders[type] = {
252
+ ...(base.externalProviders?.[type] ?? {}),
253
+ ...cfg,
254
+ };
255
+ }
256
+ }
210
257
  // Issue #568: `agentOverrides` gets keyed-object merge semantics (like
211
258
  // `providers`), NOT the shallow-replace treatment `fallbackChain` gets.
212
259
  // Overriding one agent type must not wipe the rest.
@@ -255,12 +302,14 @@ export function sanitizeAgentOverrides(raw) {
255
302
  const cleaned = {};
256
303
  if (entry.provider !== undefined) {
257
304
  const provider = entry.provider;
258
- if (!ALL_PROVIDER_TYPES.includes(provider)) {
305
+ // ADR-127: registered external providers are valid override targets too.
306
+ const selectable = allSelectableProviderTypes();
307
+ if (!selectable.includes(provider)) {
259
308
  warnings.push(`agentOverrides["${agentType}"].provider "${String(entry.provider)}" is not a known ` +
260
- `provider (${ALL_PROVIDER_TYPES.join(', ')}); ignoring this entry.`);
309
+ `provider (${selectable.join(', ')}); ignoring this entry.`);
261
310
  continue;
262
311
  }
263
- if (!RUNTIME_CONSTRUCTIBLE_PROVIDERS.has(provider)) {
312
+ if (!isRuntimeConstructible(provider)) {
264
313
  warnings.push(`agentOverrides["${agentType}"].provider "${provider}" cannot be instantiated at ` +
265
314
  `runtime yet; ignoring this entry.`);
266
315
  continue;
@@ -325,7 +374,7 @@ export function applyEnvProviderDetection(config, env = process.env, explicitlyD
325
374
  providers: { ...(config.providers ?? {}) },
326
375
  };
327
376
  for (const provider of available) {
328
- const keys = PROVIDER_ENV_KEYS[provider];
377
+ const keys = envKeysFor(provider);
329
378
  if (keys.length === 0)
330
379
  continue; // local provider, leave default
331
380
  if (explicitlyDisabled.has(provider))
@@ -365,12 +414,17 @@ export function resolveProviderOverrideFromEnv(env = process.env) {
365
414
  if (!raw)
366
415
  return undefined;
367
416
  const normalized = raw === 'anthropic' ? 'claude' : raw;
368
- if (ALL_PROVIDER_TYPES.includes(normalized)) {
417
+ // ADR-127: a registered external provider is selectable here on exactly the
418
+ // same terms as a built-in — that is the whole point of issue #628.
419
+ const selectable = allSelectableProviderTypes();
420
+ if (selectable.includes(normalized)) {
369
421
  return normalized;
370
422
  }
423
+ // The warning enumerates built-ins AND registered types, so a typo is never
424
+ // mistaken for "some provider got registered and I can't see it".
371
425
  // eslint-disable-next-line no-console
372
426
  console.warn(`[router-config] Ignoring AQE_LLM_PROVIDER="${raw}": not a known provider. ` +
373
- `Valid values: ${ALL_PROVIDER_TYPES.join(', ')}.`);
427
+ `Valid values: ${selectable.join(', ')}.`);
374
428
  return undefined;
375
429
  }
376
430
  /**
@@ -381,6 +435,21 @@ export function resolveProviderOverrideFromEnv(env = process.env) {
381
435
  export function loadRouterConfig(options = {}) {
382
436
  const env = options.env ?? process.env;
383
437
  const onDisk = loadRouterConfigFile(options.projectRoot);
438
+ // ADR-127 (issue #628): register declared external providers FIRST. Every
439
+ // gate below — env detection, AQE_LLM_PROVIDER resolution, agentOverrides
440
+ // validation — asks the registry whether a type is selectable, so the
441
+ // declarations must be live before any of them run.
442
+ const { declarations, warnings: externalWarnings } = sanitizeExternalProviders(onDisk.externalProviders);
443
+ for (const warning of externalWarnings) {
444
+ logger.warn(`[llm-config] ${warning}`);
445
+ }
446
+ onDisk.externalProviders = declarations;
447
+ const registration = registerExternalProviders(declarations, {
448
+ sourceDetail: getRouterConfigPath(options.projectRoot),
449
+ });
450
+ for (const warning of registration.warnings) {
451
+ logger.warn(`[llm-config] ${warning}`);
452
+ }
384
453
  // Issue #568: validate the hand-editable per-agent map before it reaches the
385
454
  // router. Bad entries are dropped with a warning, never fatal.
386
455
  if (onDisk.agentOverrides !== undefined) {
@@ -394,6 +463,21 @@ export function loadRouterConfig(options = {}) {
394
463
  // ADR-123: an explicit `enabled: false` on disk is never resurrected by env.
395
464
  const explicitlyDisabled = collectExplicitlyDisabled(onDisk);
396
465
  let withEnv = applyEnvProviderDetection(merged, env, explicitlyDisabled);
466
+ // ADR-127: declaring an external host is enabling it. Without this the host
467
+ // would be selectable in principle but dropped by pickEnabledProviders,
468
+ // which is the "configured it and nothing happened" failure ADR-125 called
469
+ // out. An explicit `enabled: false` under `providers` still wins.
470
+ for (const type of registration.registered) {
471
+ if (explicitlyDisabled.has(type))
472
+ continue;
473
+ withEnv = {
474
+ ...withEnv,
475
+ providers: {
476
+ ...withEnv.providers,
477
+ [type]: { ...(withEnv.providers?.[type] ?? {}), enabled: true },
478
+ },
479
+ };
480
+ }
397
481
  // ADR-123: AQE_LLM_PROVIDER pins the default provider and enables it.
398
482
  const providerOverride = resolveProviderOverrideFromEnv(env);
399
483
  if (providerOverride) {
@@ -418,8 +502,12 @@ export function loadRouterConfig(options = {}) {
418
502
  */
419
503
  export function shouldEnableRouter(options = {}) {
420
504
  const env = options.env ?? process.env;
505
+ // ADR-127: a project whose only provider is an externally registered host
506
+ // still needs a router.
507
+ if (registeredProviderTypes().length > 0)
508
+ return true;
421
509
  for (const provider of ALL_PROVIDER_TYPES) {
422
- const keys = PROVIDER_ENV_KEYS[provider];
510
+ const keys = envKeysFor(provider);
423
511
  if (keys.length === 0)
424
512
  continue;
425
513
  if (keys.some((k) => (env[k] ?? '').trim().length > 0)) {
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Agentic QE v3 — Config-driven external provider registration
3
+ * ADR-127 (issue #628)
4
+ *
5
+ * Turns the `externalProviders` block of `.agentic-qe/llm-config.json` into
6
+ * live entries in the provider registry.
7
+ *
8
+ * This is the half of ADR-127 that actually unblocks the requester. An
9
+ * in-process `registerProvider()` call cannot reach the `aqe` CLI bundle or the
10
+ * MCP server bundle — they are separate esbuild artifacts spawned as
11
+ * subprocesses — but both call `loadRouterConfig()`, so a declaration on disk
12
+ * reaches both. No third-party code is imported: a declaration is data, and the
13
+ * provider it produces is AQE's own `ExternalCliProvider`.
14
+ *
15
+ * Untrusted-input discipline (inherited from ADR-125's `sanitizeAgentOverrides`):
16
+ * entries are validated and dropped **individually** with a warning. One
17
+ * malformed declaration must never fail router construction for the project.
18
+ */
19
+ import { type ExternalCliProviderConfig } from '../providers/external-cli.js';
20
+ /** Result of processing an `externalProviders` block. */
21
+ export interface ExternalProviderRegistrationResult {
22
+ /** Types successfully registered. */
23
+ readonly registered: string[];
24
+ /** Human-readable reasons entries were dropped. */
25
+ readonly warnings: string[];
26
+ }
27
+ /**
28
+ * Register every valid declaration in an `externalProviders` block.
29
+ *
30
+ * Idempotent: re-registering the same type replaces the previous entry, and
31
+ * config-sourced registrations that are no longer declared are removed. API
32
+ * registrations (`registerProvider()`) are never touched — a config reload must
33
+ * not silently unregister a provider an embedder installed in-process.
34
+ */
35
+ export declare function registerExternalProviders(declarations: Record<string, ExternalCliProviderConfig> | undefined, options?: {
36
+ sourceDetail?: string;
37
+ }): ExternalProviderRegistrationResult;
38
+ /**
39
+ * Extract and shallow-validate an `externalProviders` block read off disk.
40
+ * Returns only entries that are objects; deeper validation happens at
41
+ * registration so every rejection is reported through one path.
42
+ */
43
+ export declare function sanitizeExternalProviders(raw: unknown): {
44
+ declarations: Record<string, ExternalCliProviderConfig>;
45
+ warnings: string[];
46
+ };
47
+ //# sourceMappingURL=external-provider-config.d.ts.map