agentic-qe 3.12.3 → 3.13.1

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 (442) hide show
  1. package/.agents/skills/aqe-plan-quality/SKILL.md +25 -0
  2. package/.agents/skills/aqe-plan-quality/agents/openai.yaml +4 -0
  3. package/.agents/skills/aqe-plan-quality/references/qe-routing.md +22 -0
  4. package/.agents/skills/aqe-review-quality/SKILL.md +24 -0
  5. package/.agents/skills/aqe-review-quality/agents/openai.yaml +4 -0
  6. package/.agents/skills/aqe-review-quality/references/quality-gates.md +20 -0
  7. package/.agents/skills/aqe-test-change/SKILL.md +26 -0
  8. package/.agents/skills/aqe-test-change/agents/openai.yaml +4 -0
  9. package/.agents/skills/aqe-test-change/references/test-specialties.md +23 -0
  10. package/.claude/helpers/.helpers-version +1 -1
  11. package/.claude/helpers/auto-memory-hook.mjs +430 -430
  12. package/.claude/helpers/helpers.manifest.json +6 -5
  13. package/.claude/helpers/hook-handler.cjs +565 -311
  14. package/.claude/helpers/intelligence.cjs +1058 -1058
  15. package/.claude/helpers/ruflo-hook.cjs +5 -1
  16. package/.claude/helpers/statusline.cjs +995 -682
  17. package/.claude/skills/qe-court/SKILL.md +186 -0
  18. package/.claude/skills/qe-court/config.json +27 -0
  19. package/.claude/skills/qe-court/evals/fixtures/seeded-mutant-delivery/README.md +38 -0
  20. package/.claude/skills/qe-court/evals/fixtures/seeded-mutant-delivery/budget.test.ts +21 -0
  21. package/.claude/skills/qe-court/evals/fixtures/seeded-mutant-delivery/budget.ts +25 -0
  22. package/.claude/skills/qe-court/evals/qe-court.yaml +70 -0
  23. package/.claude/skills/qe-court/schemas/output.json +92 -0
  24. package/.claude/skills/qe-court/scripts/validate-config.json +30 -0
  25. package/.claude/skills/skills-manifest.json +9 -8
  26. package/.codex/hooks/aqe-codex-hook.cjs +69 -0
  27. package/.codex/hooks/ruflo-codex-hook.cjs +94 -0
  28. package/.codex/hooks.json +169 -0
  29. package/CHANGELOG.md +159 -0
  30. package/README.md +3 -3
  31. package/assets/skills/qe-court/SKILL.md +186 -0
  32. package/assets/skills/qe-court/config.json +27 -0
  33. package/assets/skills/qe-court/evals/fixtures/seeded-mutant-delivery/README.md +38 -0
  34. package/assets/skills/qe-court/evals/fixtures/seeded-mutant-delivery/budget.test.ts +21 -0
  35. package/assets/skills/qe-court/evals/fixtures/seeded-mutant-delivery/budget.ts +25 -0
  36. package/assets/skills/qe-court/evals/qe-court.yaml +70 -0
  37. package/assets/skills/qe-court/schemas/output.json +92 -0
  38. package/assets/skills/qe-court/scripts/validate-config.json +30 -0
  39. package/assets/skills/skills-manifest.json +9 -8
  40. package/dist/cli/bundle.js +5 -5
  41. package/dist/cli/chunks/adapter-VRZNL53W.js +2 -0
  42. package/dist/cli/chunks/{agent-booster-wasm-XFAQATTH.js → agent-booster-wasm-4VM3DE42.js} +2 -2
  43. package/dist/cli/chunks/{agent-handler-2ZV4CA3U.js → agent-handler-ZDLMZQQX.js} +2 -2
  44. package/dist/cli/chunks/{agent-memory-branch-6WGP7F5T.js → agent-memory-branch-P72MDHHV.js} +2 -2
  45. package/dist/cli/chunks/aqe-learning-engine-XCNMNERX.js +2 -0
  46. package/dist/cli/chunks/{arena-FWX7XZAE.js → arena-55FZGKEV.js} +4 -4
  47. package/dist/cli/chunks/{arena-KJUNQK4N.js → arena-GCRBQ26R.js} +2 -2
  48. package/dist/cli/chunks/{audit-YFUDEOKN.js → audit-VYAYVLIV.js} +3 -3
  49. package/dist/cli/chunks/base-VZZNR4UR.js +2 -0
  50. package/dist/cli/chunks/{hnswlib-node-SKTT5KIH.js → better-sqlite3-JDDFYVCX.js} +2 -2
  51. package/dist/cli/chunks/{brain-handler-ZCK4WTPK.js → brain-handler-IUKMRGWO.js} +3 -3
  52. package/dist/cli/chunks/{branch-enumerator-HTVINRYG.js → branch-enumerator-TUI3RWLX.js} +2 -2
  53. package/dist/cli/chunks/{browser-WUL2B77M.js → browser-NBQTZYX2.js} +2 -2
  54. package/dist/cli/chunks/browser-workflow-5DZNIFUY.js +2 -0
  55. package/dist/cli/chunks/{chunk-IWQHQLT6.js → chunk-23MT7OBY.js} +2 -2
  56. package/dist/cli/chunks/{chunk-KE3RK35K.js → chunk-2CQAWYYE.js} +1 -1
  57. package/dist/cli/chunks/{chunk-ADQFQBFJ.js → chunk-2D2XFUKI.js} +2 -2
  58. package/dist/cli/chunks/{chunk-2YDZXFO2.js → chunk-2G2QWV7B.js} +2 -2
  59. package/dist/cli/chunks/{chunk-HGJQSTQD.js → chunk-2ISLQU3G.js} +1 -1
  60. package/dist/cli/chunks/{chunk-6E47R27C.js → chunk-2OM3TDC4.js} +1 -1
  61. package/dist/cli/chunks/{chunk-WQASUQ5P.js → chunk-2XZ4BHWS.js} +1 -1
  62. package/dist/cli/chunks/{chunk-E2U3LTGM.js → chunk-3F4XH4ZA.js} +1 -1
  63. package/dist/cli/chunks/{chunk-YUDGQZJN.js → chunk-3K4YC3GC.js} +2 -2
  64. package/dist/cli/chunks/chunk-3KGCBAUH.js +2 -0
  65. package/dist/cli/chunks/{chunk-PXCJDB7J.js → chunk-3Y6CHW67.js} +1 -1
  66. package/dist/cli/chunks/{chunk-MOX25RZQ.js → chunk-4QMWWYNU.js} +2 -2
  67. package/dist/cli/chunks/{chunk-XZGOXLZH.js → chunk-4WGSMODQ.js} +1 -1
  68. package/dist/cli/chunks/{chunk-2CPJCXV6.js → chunk-55WDVSCM.js} +2 -2
  69. package/dist/cli/chunks/{chunk-EHO52NKU.js → chunk-5BB5DUL4.js} +2 -2
  70. package/dist/cli/chunks/{chunk-BEPP57C2.js → chunk-6C3MZLYW.js} +2 -2
  71. package/dist/cli/chunks/{chunk-DTZFZ5M7.js → chunk-6QKQFHS7.js} +2 -2
  72. package/dist/cli/chunks/{chunk-KXKACV3E.js → chunk-6XZXMEK4.js} +3 -3
  73. package/dist/cli/chunks/{chunk-L6GZGEFQ.js → chunk-7D7XFZZI.js} +2 -2
  74. package/dist/cli/chunks/{chunk-NLDLUHOF.js → chunk-7WEHZCSK.js} +2 -2
  75. package/dist/cli/chunks/{chunk-TXLPOXAN.js → chunk-A2MG3YXO.js} +1 -1
  76. package/dist/cli/chunks/{chunk-HJ2LG5IH.js → chunk-AHTOWWSE.js} +1 -1
  77. package/dist/cli/chunks/{chunk-GV2BS5NC.js → chunk-AOMXMFDH.js} +2 -2
  78. package/dist/cli/chunks/{chunk-EH7TSJPS.js → chunk-ARTLSAXO.js} +2 -2
  79. package/dist/cli/chunks/{chunk-CXMOIE7D.js → chunk-AY7S7AWN.js} +2 -2
  80. package/dist/cli/chunks/{chunk-2LNDU7AW.js → chunk-AYYDO52G.js} +2 -2
  81. package/dist/cli/chunks/chunk-BBLWZT5I.js +2 -0
  82. package/dist/cli/chunks/{chunk-KRS4KGX5.js → chunk-BF5BKSDU.js} +2 -2
  83. package/dist/cli/chunks/{chunk-BQ2K4BPD.js → chunk-BOCEOLLJ.js} +2 -2
  84. package/dist/cli/chunks/{chunk-KKOFH26C.js → chunk-CE6LYEHT.js} +1 -1
  85. package/dist/cli/chunks/{chunk-GS6G7AVS.js → chunk-CWP4RMOM.js} +2 -2
  86. package/dist/cli/chunks/{chunk-DCYBV6HV.js → chunk-CWXDL7AH.js} +1 -1
  87. package/dist/cli/chunks/{chunk-KGHSKDC7.js → chunk-DTRFZD7N.js} +2 -2
  88. package/dist/cli/chunks/{chunk-CYSBM52D.js → chunk-DVPGQG4W.js} +2 -2
  89. package/dist/cli/chunks/{chunk-MSDIIRLM.js → chunk-E4D47QTF.js} +2 -2
  90. package/dist/cli/chunks/{chunk-MB7TZQW3.js → chunk-EAKHU5YX.js} +3 -3
  91. package/dist/cli/chunks/{chunk-TP3UAWMB.js → chunk-EGMGOWCE.js} +2 -2
  92. package/dist/cli/chunks/{chunk-4EHW4TNA.js → chunk-ELMUHNXN.js} +2 -2
  93. package/dist/cli/chunks/chunk-EVDYFGVH.js +2 -0
  94. package/dist/cli/chunks/{chunk-D4GMAUTP.js → chunk-FDUSWM6O.js} +98 -88
  95. package/dist/cli/chunks/{chunk-YEC55GO5.js → chunk-FM272B4O.js} +2 -2
  96. package/dist/cli/chunks/{chunk-DMD6WVJ2.js → chunk-FQUUMC57.js} +2 -2
  97. package/dist/cli/chunks/{chunk-Y625LP6K.js → chunk-FQXQJMSF.js} +2 -2
  98. package/dist/cli/chunks/{chunk-Z3SXMYVI.js → chunk-FRAGSKXQ.js} +3 -3
  99. package/dist/cli/chunks/{chunk-MOXKKY5Q.js → chunk-GASOBSNB.js} +4 -4
  100. package/dist/cli/chunks/{chunk-PTPN6HQ7.js → chunk-GJEEPKAT.js} +2 -2
  101. package/dist/cli/chunks/{chunk-WFPGIP4N.js → chunk-GOV2MHLV.js} +2 -2
  102. package/dist/cli/chunks/{chunk-NKTLKMUB.js → chunk-GTPZVLL6.js} +1 -1
  103. package/dist/cli/chunks/chunk-H7YZ2YF2.js +3 -0
  104. package/dist/cli/chunks/{chunk-WKE6WL5C.js → chunk-HFI5PHG5.js} +3 -3
  105. package/dist/cli/chunks/chunk-HUMQU4ES.js +153 -0
  106. package/dist/cli/chunks/{chunk-5FAWIIWF.js → chunk-HYIRFJDB.js} +2 -2
  107. package/dist/cli/chunks/{chunk-DZ775UGW.js → chunk-IBSBOHUX.js} +2 -2
  108. package/dist/cli/chunks/{chunk-NAMJGRGC.js → chunk-IJA4WS7B.js} +2 -2
  109. package/dist/cli/chunks/{chunk-XUUVAAHV.js → chunk-IJP42OGW.js} +2 -2
  110. package/dist/cli/chunks/{chunk-UZ7FXVH5.js → chunk-IMQKEFW6.js} +1 -1
  111. package/dist/cli/chunks/{chunk-MZ7SIO6R.js → chunk-INYS35DV.js} +2 -2
  112. package/dist/cli/chunks/{chunk-MS6VOSPY.js → chunk-IV2VCR75.js} +5 -5
  113. package/dist/cli/chunks/{chunk-UAPUA6OY.js → chunk-IZO6TPGX.js} +2 -2
  114. package/dist/cli/chunks/{chunk-XBOCRCC6.js → chunk-J45DX3IK.js} +1 -1
  115. package/dist/cli/chunks/{chunk-RE42RJYR.js → chunk-J4T6D2OQ.js} +2 -2
  116. package/dist/cli/chunks/{chunk-Y5L37K7X.js → chunk-JAPOMPFD.js} +2 -2
  117. package/dist/cli/chunks/{chunk-M7W6VJ65.js → chunk-JELNVHBF.js} +1 -1
  118. package/dist/cli/chunks/{chunk-YDYKFSD2.js → chunk-JOTNKZ7A.js} +87 -87
  119. package/dist/cli/chunks/{chunk-VUCVSKJC.js → chunk-KNWDXI3I.js} +2 -2
  120. package/dist/cli/chunks/{chunk-SPI4LUJ6.js → chunk-KW5NBORV.js} +2 -2
  121. package/dist/cli/chunks/{chunk-2OX74DOO.js → chunk-L7IOTWWY.js} +1 -1
  122. package/dist/cli/chunks/{chunk-VFYMWDSJ.js → chunk-LJQ27PUE.js} +1 -1
  123. package/dist/cli/chunks/{chunk-NT66CT73.js → chunk-LSKQUUUN.js} +2 -2
  124. package/dist/cli/chunks/{chunk-ROFCS47F.js → chunk-LSZZQ7H6.js} +1 -1
  125. package/dist/cli/chunks/{chunk-Y6XNISHP.js → chunk-LTKNCYMO.js} +2 -2
  126. package/dist/cli/chunks/{chunk-7WF3TW3G.js → chunk-LTWT4GZD.js} +4 -4
  127. package/dist/cli/chunks/chunk-LVHMXGDI.js +29 -0
  128. package/dist/cli/chunks/{chunk-3YC4YUGB.js → chunk-M3T6FB54.js} +2 -2
  129. package/dist/cli/chunks/{chunk-M32DTUZM.js → chunk-MAYZTGJ2.js} +3 -3
  130. package/dist/cli/chunks/{chunk-RLBNAB6Y.js → chunk-MCIO7BDK.js} +1 -1
  131. package/dist/cli/chunks/{chunk-SFS3UHNF.js → chunk-MPD7RR4P.js} +1 -1
  132. package/dist/cli/chunks/{chunk-DDYP6FBC.js → chunk-MUFYDKVC.js} +2 -2
  133. package/dist/cli/chunks/{chunk-ZFLE4D4X.js → chunk-MX5ZDKRR.js} +2 -2
  134. package/dist/cli/chunks/{chunk-WBZNLFTV.js → chunk-MXKHIZVM.js} +5 -5
  135. package/dist/cli/chunks/chunk-MY47CR27.js +2 -0
  136. package/dist/cli/chunks/chunk-N7WAZ2Y6.js +2 -0
  137. package/dist/cli/chunks/{chunk-2HCMRGMO.js → chunk-NHV4PTG6.js} +2 -2
  138. package/dist/cli/chunks/{chunk-HJ4U64BL.js → chunk-NP4CP3LR.js} +2 -2
  139. package/dist/cli/chunks/{chunk-UIHZ34SJ.js → chunk-OCKTPTQ7.js} +2 -2
  140. package/dist/cli/chunks/{chunk-SGMJ277D.js → chunk-OE4B6LDZ.js} +1 -1
  141. package/dist/cli/chunks/{chunk-SV7J5FDW.js → chunk-OPQY2333.js} +2 -2
  142. package/dist/cli/chunks/{chunk-FTM562PF.js → chunk-ORDH4VPL.js} +1 -1
  143. package/dist/cli/chunks/{chunk-YOG5KQZF.js → chunk-P4Y3G6OB.js} +2 -2
  144. package/dist/cli/chunks/{chunk-LHAUFY4Y.js → chunk-PIOO2M4W.js} +2 -2
  145. package/dist/cli/chunks/{chunk-UGW23T6S.js → chunk-PKMCZFET.js} +2 -2
  146. package/dist/cli/chunks/{chunk-OPG2HV2W.js → chunk-QES4QHQW.js} +1 -1
  147. package/dist/cli/chunks/{chunk-BDGKB2PT.js → chunk-QG6BXSNJ.js} +2 -2
  148. package/dist/cli/chunks/{chunk-BSNNTOHY.js → chunk-QLOOW23R.js} +3 -3
  149. package/dist/cli/chunks/{chunk-AIWRYWK2.js → chunk-RSCGBHLM.js} +2 -2
  150. package/dist/cli/chunks/{chunk-LCM7TY76.js → chunk-RZ6CO3RZ.js} +2 -2
  151. package/dist/cli/chunks/chunk-S6F3IJMK.js +2 -0
  152. package/dist/cli/chunks/{chunk-WUE53ZPH.js → chunk-S6VNPKHO.js} +1 -1
  153. package/dist/cli/chunks/{chunk-L2X3BWMN.js → chunk-SHWXADCI.js} +2 -2
  154. package/dist/cli/chunks/{chunk-GMFFMJD6.js → chunk-SPJQJWGK.js} +2 -2
  155. package/dist/cli/chunks/{chunk-KVHCWH4H.js → chunk-SS6K4FBH.js} +2 -2
  156. package/dist/cli/chunks/{chunk-ZGKQCUPB.js → chunk-SXADKQJN.js} +2 -2
  157. package/dist/cli/chunks/{chunk-LGYEOYQJ.js → chunk-TBQTGQ75.js} +2 -2
  158. package/dist/cli/chunks/{chunk-J2RJOJ5Q.js → chunk-TDGE52HJ.js} +1 -1
  159. package/dist/cli/chunks/chunk-THFLRJT6.js +54 -0
  160. package/dist/cli/chunks/{chunk-GGTCD4MA.js → chunk-TQ2MA3SR.js} +1 -1
  161. package/dist/cli/chunks/{chunk-NOSQI2OW.js → chunk-UCQ66N6M.js} +3 -3
  162. package/dist/cli/chunks/{chunk-CL6SNCFF.js → chunk-UHGMS2PJ.js} +1 -1
  163. package/dist/cli/chunks/{chunk-XDHGCBTS.js → chunk-UHKXKWMD.js} +2 -2
  164. package/dist/cli/chunks/{chunk-PEHEANAM.js → chunk-UJ6TMZC6.js} +2 -2
  165. package/dist/cli/chunks/{chunk-PSDH7CIL.js → chunk-UKBDH27A.js} +1 -1
  166. package/dist/cli/chunks/{chunk-ZTKCGEB6.js → chunk-UMCEEXQN.js} +1 -1
  167. package/dist/cli/chunks/{chunk-KA4EAMMQ.js → chunk-USUFTHHT.js} +3 -3
  168. package/dist/cli/chunks/{chunk-QILF6BZH.js → chunk-V32MDID3.js} +2 -2
  169. package/dist/cli/chunks/{chunk-6SUSRXXF.js → chunk-VAJT6QWX.js} +1 -1
  170. package/dist/cli/chunks/{chunk-7Q74K3HZ.js → chunk-VLUJWI56.js} +1 -1
  171. package/dist/cli/chunks/{chunk-DW7OI5IF.js → chunk-WINA3XUT.js} +2 -2
  172. package/dist/cli/chunks/{chunk-S47YGWXK.js → chunk-WWCH5SSF.js} +2 -2
  173. package/dist/cli/chunks/{chunk-FJACKKRL.js → chunk-WWP2VXYJ.js} +2 -2
  174. package/dist/cli/chunks/{chunk-N4ML23ZG.js → chunk-WZROSTQZ.js} +1 -1
  175. package/dist/cli/chunks/{chunk-FXOOOJQD.js → chunk-XFUF654L.js} +1 -1
  176. package/dist/cli/chunks/{chunk-PDX7IYTJ.js → chunk-XN2CD5S6.js} +2 -2
  177. package/dist/cli/chunks/{chunk-DHUZHT5D.js → chunk-XOQQGZFY.js} +1 -1
  178. package/dist/cli/chunks/{chunk-4HP7N6SP.js → chunk-XSCOEY4C.js} +2 -2
  179. package/dist/cli/chunks/{chunk-KNWBRMLL.js → chunk-Y734CWHJ.js} +2 -2
  180. package/dist/cli/chunks/{chunk-NJDLJ3KD.js → chunk-YBPCBFEE.js} +2 -2
  181. package/dist/cli/chunks/{chunk-67BCEHU5.js → chunk-YLAEIS6F.js} +1 -1
  182. package/dist/cli/chunks/{chunk-E53UR2EW.js → chunk-YMMVCOPF.js} +3 -3
  183. package/dist/cli/chunks/{chunk-VSD6JE2H.js → chunk-YQCAXYBF.js} +2 -2
  184. package/dist/cli/chunks/{chunk-32L7DKNQ.js → chunk-YRVUYM62.js} +2 -2
  185. package/dist/cli/chunks/{chunk-LNIHLNW4.js → chunk-YVKMHXQK.js} +3 -3
  186. package/dist/cli/chunks/{chunk-CHFTHSMM.js → chunk-ZCLFAOVT.js} +2 -2
  187. package/dist/cli/chunks/{chunk-AZ24ONMX.js → chunk-ZH6ZBDVV.js} +1 -1
  188. package/dist/cli/chunks/{chunk-R6WMGSEK.js → chunk-ZHEKGB4A.js} +2 -2
  189. package/dist/cli/chunks/{chunk-EOUJ6X5H.js → chunk-ZIBW7CB4.js} +1 -1
  190. package/dist/cli/chunks/{chunk-OBL6W4Z5.js → chunk-ZLCATRXI.js} +2 -2
  191. package/dist/cli/chunks/{chunk-UGXHMQ6P.js → chunk-ZXX2VFMA.js} +2 -2
  192. package/dist/cli/chunks/{chunk-72QQDR3Y.js → chunk-ZYPESQ4M.js} +2 -2
  193. package/dist/cli/chunks/{ci-MMLUV6L6.js → ci-HHL5WQXQ.js} +2 -2
  194. package/dist/cli/chunks/{ci-output-25GS6RXR.js → ci-output-HVOJC2KN.js} +2 -2
  195. package/dist/cli/chunks/{circuit-breaker-4OWAN6R6.js → circuit-breaker-HRXMIY2G.js} +2 -2
  196. package/dist/cli/chunks/{claude-flow-setup-LR3SRBX6.js → claude-flow-setup-3I7WVMZN.js} +2 -2
  197. package/dist/cli/chunks/client-EY2IYQDL.js +2 -0
  198. package/dist/cli/chunks/{cline-installer-YSZWRZUI.js → cline-installer-NBREL4QT.js} +2 -2
  199. package/dist/cli/chunks/{code-UUWUIXI7.js → code-TX4E2RWU.js} +2 -2
  200. package/dist/cli/chunks/{code-index-extractor-2LSPQORU.js → code-index-extractor-YELOFLSL.js} +2 -2
  201. package/dist/cli/chunks/codex-installer-HEX3KFRB.js +10 -0
  202. package/dist/cli/chunks/{completions-YKXBKZSO.js → completions-72GGFRGG.js} +2 -2
  203. package/dist/cli/chunks/{complexity-analyzer-HNT2QRWB.js → complexity-analyzer-WRAKDYUK.js} +2 -2
  204. package/dist/cli/chunks/{continuedev-installer-AW5VCB66.js → continuedev-installer-UGYKSKTI.js} +2 -2
  205. package/dist/cli/chunks/{copilot-installer-B6SVZXQT.js → copilot-installer-RPCRUKIJ.js} +2 -2
  206. package/dist/cli/chunks/core-handlers-G4GLA4DT.js +2 -0
  207. package/dist/cli/chunks/{cost-tracker-POZMBN2V.js → cost-tracker-CNHJ7G4M.js} +2 -2
  208. package/dist/cli/chunks/{coverage-V5BGPN5Q.js → coverage-RHIQ4XEX.js} +3 -3
  209. package/dist/cli/chunks/cross-domain-router-P3K4IH5H.js +2 -0
  210. package/dist/cli/chunks/{cursor-installer-EYBI4QVK.js → cursor-installer-KSRVA4V2.js} +2 -2
  211. package/dist/cli/chunks/{daemon-CF6ENQB2.js → daemon-IT4AAFBW.js} +3 -3
  212. package/dist/cli/chunks/{daemon-HKARXY2W.js → daemon-QBXSJABV.js} +4 -4
  213. package/dist/cli/chunks/{dag-attention-scheduler-XIL5IAP7.js → dag-attention-scheduler-F7WCQTLY.js} +2 -2
  214. package/dist/cli/chunks/{detect-RRX2U2KK.js → detect-2IYU332U.js} +2 -2
  215. package/dist/cli/chunks/{dist-node-DOANNRCS.js → dist-node-CIYNYCYN.js} +2 -2
  216. package/dist/cli/chunks/{domain-handler-QMTJV5OR.js → domain-handler-QRYQO2QK.js} +2 -2
  217. package/dist/cli/chunks/{domain-transfer-FSNH75XW.js → domain-transfer-2OQDOD3G.js} +2 -2
  218. package/dist/cli/chunks/dream-IT4ZQGEO.js +2 -0
  219. package/dist/cli/chunks/{embed-and-insert-pattern-NI7PGONV.js → embed-and-insert-pattern-P2IFC7JL.js} +2 -2
  220. package/dist/cli/chunks/{eval-6ZAF2RY3.js → eval-SBPK37IB.js} +2 -2
  221. package/dist/cli/chunks/{experience-capture-middleware-JMXR7N5G.js → experience-capture-middleware-3GMFND3F.js} +3 -3
  222. package/dist/cli/chunks/{fast-paths-YB7XZZ76.js → fast-paths-4OYYII2F.js} +2 -2
  223. package/dist/cli/chunks/{feature-flags-2RC4GFC4.js → feature-flags-BM6QTMMC.js} +2 -2
  224. package/dist/cli/chunks/{feature-flags-KJSP7KR6.js → feature-flags-NFCAYUWO.js} +2 -2
  225. package/dist/cli/chunks/{file-discovery-SUPBXFYI.js → file-discovery-2M3ZUAGC.js} +2 -2
  226. package/dist/cli/chunks/{fleet-KSXFOLFW.js → fleet-QMGGVIDV.js} +3 -3
  227. package/dist/cli/chunks/{gnn-wrapper-DFZ5KPPK.js → gnn-wrapper-WGMAGZMO.js} +2 -2
  228. package/dist/cli/chunks/{heartbeat-handler-G3EADK2H.js → heartbeat-handler-5RSMEZGG.js} +4 -4
  229. package/dist/cli/chunks/heartbeat-scheduler-UUWSPHF2.js +2 -0
  230. package/dist/cli/chunks/hnsw-adapter-YUJVSFPD.js +2 -0
  231. package/dist/cli/chunks/hnsw-index-FQZ47RSJ.js +2 -0
  232. package/dist/cli/chunks/{hnsw-legacy-bridge-MA676GER.js → hnsw-legacy-bridge-TYPQ2T6P.js} +2 -2
  233. package/dist/cli/chunks/{better-sqlite3-FE55OGYB.js → hnswlib-node-GMRPPMDM.js} +2 -2
  234. package/dist/cli/chunks/{hooks-HTCPMZNZ.js → hooks-NUC5P3OJ.js} +10 -10
  235. package/dist/cli/chunks/hybrid-router-T5MF73NF.js +2 -0
  236. package/dist/cli/chunks/{hypergraph-engine-QPB3IH4A.js → hypergraph-engine-OIB7WK54.js} +2 -2
  237. package/dist/cli/chunks/{hypergraph-handler-GBFSVD7N.js → hypergraph-handler-XX2N75QC.js} +3 -3
  238. package/dist/cli/chunks/impact-analyzer-AEGELYMD.js +2 -0
  239. package/dist/cli/chunks/{init-handler-XYBTBCPD.js → init-handler-AKSTJMZY.js} +6 -6
  240. package/dist/cli/chunks/init-wizard-WKX3OGWM.js +2 -0
  241. package/dist/cli/chunks/kernel-5CVWFIGR.js +2 -0
  242. package/dist/cli/chunks/{kilocode-installer-LY6JOBFY.js → kilocode-installer-375V5ZZB.js} +2 -2
  243. package/dist/cli/chunks/{kiro-installer-JF6NXTJL.js → kiro-installer-GKY5GB5Q.js} +2 -2
  244. package/dist/cli/chunks/knowledge-graph-EGPMNDMT.js +2 -0
  245. package/dist/cli/chunks/{learning-BIISN67N.js → learning-2CQE3QP6.js} +3 -3
  246. package/dist/cli/chunks/{llm-router-YZUNVZQE.js → llm-router-FTQNRF45.js} +5 -5
  247. package/dist/cli/chunks/llm-router-service-H4LXOARB.js +2 -0
  248. package/dist/cli/chunks/{load-MTWNBNLL.js → load-3DBAO5ZG.js} +2 -2
  249. package/dist/cli/chunks/load-test-OOOGITSN.js +2 -0
  250. package/dist/cli/chunks/{mcp-37PYMNHQ.js → mcp-AWR6TDXM.js} +2 -2
  251. package/dist/cli/chunks/{memory-IXUUDCI3.js → memory-GI4LCNXY.js} +5 -5
  252. package/dist/cli/chunks/memory-backend-O2YOT4LW.js +2 -0
  253. package/dist/cli/chunks/{memory-handlers-RM2VOFJX.js → memory-handlers-MALKG7GP.js} +2 -2
  254. package/dist/cli/chunks/{multi-model-executor-RKKS5XYG.js → multi-model-executor-SWFT5U72.js} +2 -2
  255. package/dist/cli/chunks/{opencode-installer-3VXI5MTR.js → opencode-installer-EYXGYJX5.js} +2 -2
  256. package/dist/cli/chunks/{orchestrator-UO3ZOGFZ.js → orchestrator-MP3EAZVC.js} +5 -5
  257. package/dist/cli/chunks/pattern-null-store-WBOHE2XL.js +2 -0
  258. package/dist/cli/chunks/{pattern-usage-recorder-YJKRL2GI.js → pattern-usage-recorder-25P3R3W7.js} +2 -2
  259. package/dist/cli/chunks/{pipeline-GJQJKXWX.js → pipeline-LSAFL25D.js} +2 -2
  260. package/dist/cli/chunks/{platform-RHVCZK6C.js → platform-IJ6DJ2PG.js} +2 -2
  261. package/dist/cli/chunks/{plugin-ZKAIW7O6.js → plugin-OXOZRZW2.js} +2 -2
  262. package/dist/cli/chunks/{prime-radiant-advanced-wasm-4X3DSKQV.js → prime-radiant-advanced-wasm-HJNJQQ3R.js} +2 -2
  263. package/dist/cli/chunks/protocol-executor-JCNTPW5J.js +2 -0
  264. package/dist/cli/chunks/{protocol-handler-KKYQZMB2.js → protocol-handler-TCMUH2HL.js} +2 -2
  265. package/dist/cli/chunks/{prove-OD6RPKWX.js → prove-ADXQ2GOX.js} +2 -2
  266. package/dist/cli/chunks/provider-manager-LPGQ5GOO.js +2 -0
  267. package/dist/cli/chunks/qe-reasoning-bank-FWVYVSPP.js +2 -0
  268. package/dist/cli/chunks/{quality-ZRWBXCKO.js → quality-EWP36ORX.js} +2 -2
  269. package/dist/cli/chunks/{quality-gate-Y6BWQV7J.js → quality-gate-3BIG7PVU.js} +3 -3
  270. package/dist/cli/chunks/queen-coordinator-UXNALRVD.js +2 -0
  271. package/dist/cli/chunks/{real-embeddings-YYEUO4P2.js → real-embeddings-D4NBT7W5.js} +2 -2
  272. package/dist/cli/chunks/{roocode-installer-JBCPS3FK.js → roocode-installer-DOPWH6YV.js} +2 -2
  273. package/dist/cli/chunks/router-C6LUIQYY.js +2 -0
  274. package/dist/cli/chunks/routing-feedback-KMSGGDVD.js +2 -0
  275. package/dist/cli/chunks/{routing-handler-KTPJOSBJ.js → routing-handler-5ADEFOHS.js} +2 -2
  276. package/dist/cli/chunks/{ruvector-commands-SCZZVRE7.js → ruvector-commands-PLMEIHSN.js} +2 -2
  277. package/dist/cli/chunks/{rvf-dual-writer-IP7GELQJ.js → rvf-dual-writer-EQWKVNQB.js} +2 -2
  278. package/dist/cli/chunks/{rvf-migration-adapter-WLFTDARU.js → rvf-migration-adapter-2OWFUUCG.js} +2 -2
  279. package/dist/cli/chunks/{rvf-migration-coordinator-MGFDQ5WD.js → rvf-migration-coordinator-URAETAPU.js} +2 -2
  280. package/dist/cli/chunks/rvf-native-adapter-K54BELUE.js +2 -0
  281. package/dist/cli/chunks/{rvf-store-integrity-OTOAGT5W.js → rvf-store-integrity-4AWPW4TF.js} +2 -2
  282. package/dist/cli/chunks/safe-db-NGOZWF2D.js +2 -0
  283. package/dist/cli/chunks/schedule-B3Y5P64E.js +2 -0
  284. package/dist/cli/chunks/scheduler-Z6QVRMTB.js +2 -0
  285. package/dist/cli/chunks/{security-B2CLZKRB.js → security-HDLZVM62.js} +3 -3
  286. package/dist/cli/chunks/{shared-rvf-adapter-2PMFAQMN.js → shared-rvf-adapter-YQT7PF7X.js} +2 -2
  287. package/dist/cli/chunks/{shared-rvf-dual-writer-B6P732AV.js → shared-rvf-dual-writer-N5D3NSJ7.js} +2 -2
  288. package/dist/cli/chunks/sqlite-persistence-PLOUGATS.js +2 -0
  289. package/dist/cli/chunks/{status-handler-M6SN2W2D.js → status-handler-KLYRJ2MR.js} +2 -2
  290. package/dist/cli/chunks/{structural-health-YCCTGI3K.js → structural-health-6LKVRP6Y.js} +2 -2
  291. package/dist/cli/chunks/sync-7VMFPBVB.js +2 -0
  292. package/dist/cli/chunks/{sync-KSRF77ID.js → sync-YYOW6L6W.js} +2 -2
  293. package/dist/cli/chunks/{task-handler-MIKRB2SL.js → task-handler-RKQDGFN2.js} +2 -2
  294. package/dist/cli/chunks/{task-handlers-YXKRUBQ4.js → task-handlers-6YJ4QTEP.js} +3 -3
  295. package/dist/cli/chunks/{test-MLOLQCB7.js → test-ASPUIUJH.js} +4 -4
  296. package/dist/cli/chunks/{test-scheduling-3KXESUZS.js → test-scheduling-I7ABQBBU.js} +3 -3
  297. package/dist/cli/chunks/{token-bootstrap-6V2PMDBJ.js → token-bootstrap-2XZBTODN.js} +2 -2
  298. package/dist/cli/chunks/{token-usage-DP6THJ3I.js → token-usage-TMUE6PTF.js} +2 -2
  299. package/dist/cli/chunks/transformers-FDHHH4SL.js +2 -0
  300. package/dist/cli/chunks/{tree-sitter-wasm-parser-COK2XERS.js → tree-sitter-wasm-parser-G7PGZA5C.js} +2 -2
  301. package/dist/cli/chunks/{types-PYHUCVOZ.js → types-JGT57UFP.js} +2 -2
  302. package/dist/cli/chunks/unified-memory-P5YVLMFI.js +2 -0
  303. package/dist/cli/chunks/unified-memory-hnsw-7HI43A2O.js +2 -0
  304. package/dist/cli/chunks/unified-persistence-U26YP3N3.js +2 -0
  305. package/dist/cli/chunks/{upgrade-ONP2HNGL.js → upgrade-V6UQODJF.js} +2 -2
  306. package/dist/cli/chunks/{validate-NWSICDAY.js → validate-UBZAE5RW.js} +2 -2
  307. package/dist/cli/chunks/{validate-swarm-KXS5P3U7.js → validate-swarm-TSO2MI5X.js} +2 -2
  308. package/dist/cli/chunks/{vibium-TZSGZRCQ.js → vibium-7G3VYSQA.js} +2 -2
  309. package/dist/cli/chunks/visual-security-6FCDY5FF.js +2 -0
  310. package/dist/cli/chunks/{web-tree-sitter-V2FADNRA.js → web-tree-sitter-MIYJSF2L.js} +2 -2
  311. package/dist/cli/chunks/{windsurf-installer-GQM7KWDC.js → windsurf-installer-5TLZSQIJ.js} +2 -2
  312. package/dist/cli/chunks/witness-chain-I3K3WQID.js +2 -0
  313. package/dist/cli/chunks/{witness-chain-GTITDIA2.js → witness-chain-LTV7H2HM.js} +2 -2
  314. package/dist/cli/chunks/{witness-key-manager-AHRM2M52.js → witness-key-manager-PAJ2J4AL.js} +2 -2
  315. package/dist/cli/chunks/{workflow-XCGHEL3I.js → workflow-DL3ESR2H.js} +4 -4
  316. package/dist/cli/chunks/workflow-orchestrator-MAALP4WA.js +2 -0
  317. package/dist/cli/chunks/{wrappers-VGO2GO4J.js → wrappers-5HSU665E.js} +2 -2
  318. package/dist/cli/commands/llm-router.js +1 -0
  319. package/dist/cli/handlers/init-handler.js +1 -1
  320. package/dist/coordination/handlers/coverage-collection.d.ts +161 -0
  321. package/dist/coordination/handlers/coverage-collection.js +639 -0
  322. package/dist/coordination/handlers/coverage-handlers.js +199 -146
  323. package/dist/coordination/handlers/handler-types.d.ts +6 -1
  324. package/dist/coordination/handlers/handler-utils.js +10 -3
  325. package/dist/coordination/handlers/security-handlers.js +17 -2
  326. package/dist/coordination/handlers/test-execution-handlers.js +16 -1
  327. package/dist/coordination/result-saver.js +119 -97
  328. package/dist/coordination/task-executor.d.ts +26 -1
  329. package/dist/coordination/task-executor.js +52 -4
  330. package/dist/domains/code-intelligence/services/knowledge-graph.js +8 -0
  331. package/dist/domains/coverage-analysis/services/coverage-analyzer.js +4 -0
  332. package/dist/domains/coverage-analysis/services/gap-detector.js +4 -0
  333. package/dist/domains/defect-intelligence/services/defect-predictor.js +4 -0
  334. package/dist/domains/defect-intelligence/services/root-cause-analyzer.js +4 -0
  335. package/dist/domains/quality-assessment/services/deployment-advisor.js +4 -0
  336. package/dist/domains/quality-assessment/services/quality-analyzer.js +4 -0
  337. package/dist/domains/security-compliance/services/scanners/sast-scanner.js +4 -0
  338. package/dist/domains/test-generation/index.js +17 -2
  339. package/dist/domains/test-generation/services/test-generator.js +31 -9
  340. package/dist/init/codex-installer.d.ts +13 -0
  341. package/dist/init/codex-installer.js +122 -1
  342. package/dist/init/phases/09-assets.js +4 -0
  343. package/dist/init/phases/phase-interface.d.ts +1 -1
  344. package/dist/init/skills-installer.js +1 -0
  345. package/dist/integrations/embeddings/base/EmbeddingGenerator.js +13 -1
  346. package/dist/kernel/unified-memory.js +4 -0
  347. package/dist/learning/qe-flywheel/corpus-scorer.js +10 -7
  348. package/dist/learning/qe-flywheel/generation.d.ts +7 -0
  349. package/dist/learning/qe-flywheel/generation.js +5 -0
  350. package/dist/learning/qe-flywheel/witness-verify.d.ts +86 -0
  351. package/dist/learning/qe-flywheel/witness-verify.js +156 -0
  352. package/dist/learning/real-embeddings.js +15 -1
  353. package/dist/mcp/bundle.js +455 -434
  354. package/dist/mcp/handlers/domain-handler-configs.js +79 -14
  355. package/dist/mcp/types.d.ts +42 -4
  356. package/dist/optimization/session-cache.d.ts +13 -1
  357. package/dist/optimization/session-cache.js +27 -2
  358. package/dist/shared/db-recovery.d.ts +55 -0
  359. package/dist/shared/db-recovery.js +184 -0
  360. package/dist/shared/llm/billing-modes.js +5 -2
  361. package/dist/shared/llm/cost-tracker.js +1 -0
  362. package/dist/shared/llm/interfaces.d.ts +14 -1
  363. package/dist/shared/llm/llm-router-service.js +59 -4
  364. package/dist/shared/llm/provider-manager.d.ts +1 -1
  365. package/dist/shared/llm/provider-manager.js +7 -3
  366. package/dist/shared/llm/providers/claude-code.js +12 -3
  367. package/dist/shared/llm/providers/codex.d.ts +58 -0
  368. package/dist/shared/llm/providers/codex.js +305 -0
  369. package/dist/shared/llm/providers/index.d.ts +1 -0
  370. package/dist/shared/llm/providers/index.js +1 -0
  371. package/dist/shared/llm/router/agent-router-config.d.ts +46 -2
  372. package/dist/shared/llm/router/agent-router-config.js +124 -2
  373. package/dist/shared/llm/router/config-store.d.ts +17 -1
  374. package/dist/shared/llm/router/config-store.js +108 -0
  375. package/dist/shared/llm/router/hybrid-router.js +2 -2
  376. package/dist/shared/llm/router/index.d.ts +2 -2
  377. package/dist/shared/llm/router/index.js +2 -0
  378. package/dist/shared/llm/router/types.d.ts +44 -0
  379. package/dist/shared/llm/router/types.js +1 -0
  380. package/dist/shared/llm/spend-ledger.d.ts +24 -0
  381. package/dist/shared/llm/spend-ledger.js +86 -15
  382. package/dist/shared/llm/translation/message-formatter.js +1 -0
  383. package/dist/shared/safe-db.d.ts +10 -0
  384. package/dist/shared/safe-db.js +17 -0
  385. package/dist/skills/qe-court/referee.d.ts +62 -0
  386. package/dist/skills/qe-court/referee.js +78 -0
  387. package/dist/validation/gate-reexecute.d.ts +9 -0
  388. package/dist/validation/gate-reexecute.js +61 -0
  389. package/dist/validation/paired-bootstrap.d.ts +36 -0
  390. package/dist/validation/paired-bootstrap.js +58 -0
  391. package/package.json +9 -2
  392. package/dist/cli/chunks/adapter-D657MKZY.js +0 -2
  393. package/dist/cli/chunks/aqe-learning-engine-AFR6OZJK.js +0 -2
  394. package/dist/cli/chunks/base-K5QRGZWE.js +0 -2
  395. package/dist/cli/chunks/browser-workflow-ZMMKCGYW.js +0 -2
  396. package/dist/cli/chunks/chunk-6HQDPDN5.js +0 -2
  397. package/dist/cli/chunks/chunk-6SQ72FG7.js +0 -2
  398. package/dist/cli/chunks/chunk-7XUM4LBC.js +0 -2
  399. package/dist/cli/chunks/chunk-FHHCH7WL.js +0 -2
  400. package/dist/cli/chunks/chunk-G5OGKVLO.js +0 -2
  401. package/dist/cli/chunks/chunk-ILRKO6A5.js +0 -43
  402. package/dist/cli/chunks/chunk-NX522PRH.js +0 -153
  403. package/dist/cli/chunks/chunk-PPUDKQ4X.js +0 -29
  404. package/dist/cli/chunks/chunk-RSZOCQOL.js +0 -2
  405. package/dist/cli/chunks/chunk-ULQCEM7K.js +0 -2
  406. package/dist/cli/chunks/chunk-XQ5JT7KM.js +0 -3
  407. package/dist/cli/chunks/client-WI7RRUZO.js +0 -2
  408. package/dist/cli/chunks/codex-installer-E5BVXZVA.js +0 -8
  409. package/dist/cli/chunks/core-handlers-T6URR57V.js +0 -2
  410. package/dist/cli/chunks/cross-domain-router-WHTTKHWM.js +0 -2
  411. package/dist/cli/chunks/dream-QUL6SURC.js +0 -2
  412. package/dist/cli/chunks/heartbeat-scheduler-VZNKE33K.js +0 -2
  413. package/dist/cli/chunks/hnsw-adapter-LGWLDZLC.js +0 -2
  414. package/dist/cli/chunks/hnsw-index-66FOOI7U.js +0 -2
  415. package/dist/cli/chunks/hybrid-router-I5MEVQDF.js +0 -2
  416. package/dist/cli/chunks/impact-analyzer-K6ANYIY3.js +0 -2
  417. package/dist/cli/chunks/init-wizard-URHGXJ32.js +0 -2
  418. package/dist/cli/chunks/kernel-OKI5ZY5P.js +0 -2
  419. package/dist/cli/chunks/knowledge-graph-VQQSK7T5.js +0 -2
  420. package/dist/cli/chunks/llm-router-service-TISH33SY.js +0 -2
  421. package/dist/cli/chunks/load-test-KWKWC4Q7.js +0 -2
  422. package/dist/cli/chunks/memory-backend-JSQQMXVS.js +0 -2
  423. package/dist/cli/chunks/pattern-null-store-SXBIUKE5.js +0 -2
  424. package/dist/cli/chunks/protocol-executor-D3Q5P3WY.js +0 -2
  425. package/dist/cli/chunks/provider-manager-2EIX7E5Q.js +0 -2
  426. package/dist/cli/chunks/qe-reasoning-bank-ITOW2ZHC.js +0 -2
  427. package/dist/cli/chunks/queen-coordinator-TO5X2EXL.js +0 -2
  428. package/dist/cli/chunks/router-GZJQEJU3.js +0 -2
  429. package/dist/cli/chunks/routing-feedback-CRXSKFNS.js +0 -2
  430. package/dist/cli/chunks/rvf-native-adapter-WHADT5HX.js +0 -2
  431. package/dist/cli/chunks/safe-db-MIZ2OFWC.js +0 -2
  432. package/dist/cli/chunks/schedule-XFILYTYC.js +0 -2
  433. package/dist/cli/chunks/scheduler-EIPJ777A.js +0 -2
  434. package/dist/cli/chunks/sqlite-persistence-PFU3GSRD.js +0 -2
  435. package/dist/cli/chunks/sync-3SDMVAIU.js +0 -2
  436. package/dist/cli/chunks/transformers-QFOBOP2Y.js +0 -2
  437. package/dist/cli/chunks/unified-memory-OUVCMFGK.js +0 -2
  438. package/dist/cli/chunks/unified-memory-hnsw-YGQPOHXB.js +0 -2
  439. package/dist/cli/chunks/unified-persistence-GFRZVDOW.js +0 -2
  440. package/dist/cli/chunks/visual-security-LVOOYQ64.js +0 -2
  441. package/dist/cli/chunks/witness-chain-QNFPV4UL.js +0 -2
  442. package/dist/cli/chunks/workflow-orchestrator-MIVBCALG.js +0 -2
@@ -324,14 +324,97 @@ export function getAgentRoutingCategory(agentType) {
324
324
  }
325
325
  return 'general';
326
326
  }
327
+ // ============================================================================
328
+ // On-disk per-agent overrides (issue #568)
329
+ // ============================================================================
330
+ /**
331
+ * Per-agent-type overrides sourced from `.agentic-qe/llm-config.json`'s
332
+ * `agentOverrides` key.
333
+ *
334
+ * Module-level state, deliberately: `getPreferredModelForAgent` is a synchronous
335
+ * pure-lookup function called from many places (rule construction, capability
336
+ * resolution, the router's own dispatch), and threading a config object through
337
+ * all of them would be a far larger change than this feature warrants. The
338
+ * router installs the loaded map once at construction via
339
+ * `setAgentProviderOverrides`.
340
+ *
341
+ * Empty by default, so behavior is unchanged for anyone who never writes the
342
+ * key — the category defaults below remain authoritative.
343
+ */
344
+ let agentProviderOverrides = {};
345
+ /**
346
+ * Per-provider default model, from the router config's `providers` map.
347
+ *
348
+ * Needed because the category defaults in `DEFAULT_CATEGORY_MODELS` pair a
349
+ * provider with a provider-specific model id. If a user writes
350
+ * `{ "provider": "ollama" }` and we inherited the category *model* too, the
351
+ * agent would be routed to ollama asking for `claude-sonnet-4-6` — a model that
352
+ * provider cannot serve. Changing provider without naming a model must fall
353
+ * back to that provider's own default, not the previous provider's model.
354
+ */
355
+ let providerDefaultModels = {};
356
+ /**
357
+ * Install the on-disk per-agent overrides (issue #568). Called by the router
358
+ * service after `loadRouterConfig()` has validated them.
359
+ *
360
+ * Replaces the whole map rather than merging, so reloading a config that
361
+ * removed an entry actually removes it.
362
+ *
363
+ * @param overrides validated `agentOverrides` map
364
+ * @param defaultModels per-provider default model ids, so a provider-only
365
+ * override resolves to a model that provider can actually serve
366
+ */
367
+ export function setAgentProviderOverrides(overrides, defaultModels) {
368
+ agentProviderOverrides = { ...(overrides ?? {}) };
369
+ providerDefaultModels = { ...(defaultModels ?? {}) };
370
+ }
371
+ /** Current on-disk per-agent overrides. Primarily for diagnostics and tests. */
372
+ export function getAgentProviderOverrides() {
373
+ return agentProviderOverrides;
374
+ }
375
+ /** Clear installed overrides. For tests. */
376
+ export function resetAgentProviderOverrides() {
377
+ agentProviderOverrides = {};
378
+ providerDefaultModels = {};
379
+ }
327
380
  /**
328
- * Get the preferred model for an agent type
381
+ * Get the preferred model for an agent type.
382
+ *
383
+ * Resolution order (issue #568):
384
+ * 1. `agentOverrides[agentType]` from `.agentic-qe/llm-config.json` — the
385
+ * user's explicit, hand-written intent, so it wins.
386
+ * 2. the agent's category default (`AGENT_CATEGORY_MAP` ->
387
+ * `DEFAULT_CATEGORY_MODELS`).
388
+ *
389
+ * An override is a *partial*: fields it does not set keep their category
390
+ * default, so `{ "provider": "ollama" }` re-provisions the provider without
391
+ * forcing the user to also restate temperature, token budget, and priority.
392
+ *
329
393
  * @param agentType - The agent type identifier
330
394
  * @returns The model preference configuration
331
395
  */
332
396
  export function getPreferredModelForAgent(agentType) {
333
397
  const category = getAgentRoutingCategory(agentType);
334
- return DEFAULT_CATEGORY_MODELS[category];
398
+ const base = DEFAULT_CATEGORY_MODELS[category];
399
+ const override = agentProviderOverrides[agentType];
400
+ if (!override)
401
+ return base;
402
+ // Changing provider without naming a model must NOT inherit the previous
403
+ // provider's model id (see `providerDefaultModels`). Prefer that provider's
404
+ // own default; only keep the category model when the provider is unchanged.
405
+ const switchingProvider = override.provider !== undefined && override.provider !== base.provider;
406
+ const resolvedModel = override.model ??
407
+ (switchingProvider ? providerDefaultModels[override.provider] : undefined);
408
+ return {
409
+ ...base,
410
+ ...(override.provider !== undefined ? { provider: override.provider } : {}),
411
+ ...(resolvedModel !== undefined ? { model: resolvedModel } : {}),
412
+ ...(override.temperature !== undefined ? { temperature: override.temperature } : {}),
413
+ ...(override.maxTokens !== undefined ? { maxTokens: override.maxTokens } : {}),
414
+ // An explicit on-disk entry outranks the built-in category rule by default,
415
+ // so it isn't quietly out-prioritized by DEFAULT_ROUTING_OVERRIDES.
416
+ priority: override.priority ?? base.priority + 100,
417
+ };
335
418
  }
336
419
  /**
337
420
  * Get capability requirements for an agent type
@@ -410,6 +493,45 @@ export function createAgentRoutingRule(agentType) {
410
493
  priority: preference.priority,
411
494
  };
412
495
  }
496
+ /**
497
+ * Build routing rules for exactly the agent types the user overrode on disk
498
+ * (issue #568).
499
+ *
500
+ * Why a separate builder rather than `createAgentRoutingRule`: that one also
501
+ * constrains on `requiresReasoning`, which is right for the generated
502
+ * whole-roster ruleset but wrong here — a user who wrote
503
+ * `"qe-mutation-tester": { "provider": "ollama" }` means *always*, not "only when
504
+ * the request happens to be flagged as needing reasoning". These rules match on
505
+ * agent type alone.
506
+ *
507
+ * Returns `[]` when nothing is overridden, so a project without the key gets the
508
+ * exact rule set it got before.
509
+ *
510
+ * @returns Routing rules, highest priority first
511
+ */
512
+ export function createOverrideRoutingRules() {
513
+ return Object.keys(agentProviderOverrides)
514
+ .map((agentType) => {
515
+ const preference = getPreferredModelForAgent(agentType);
516
+ return {
517
+ id: `agent-override-${agentType}`,
518
+ name: `User override: ${agentType} -> ${preference.provider}/${preference.model}`,
519
+ description: `Per-agent routing override from .agentic-qe/llm-config.json ` +
520
+ `(agentOverrides["${agentType}"])`,
521
+ condition: { agentType: [agentType] },
522
+ action: {
523
+ provider: preference.provider,
524
+ model: preference.model,
525
+ temperature: preference.temperature,
526
+ maxTokens: preference.maxTokens,
527
+ priority: preference.priority,
528
+ },
529
+ enabled: true,
530
+ priority: preference.priority,
531
+ };
532
+ })
533
+ .sort((a, b) => b.priority - a.priority);
534
+ }
413
535
  /**
414
536
  * Create routing rules for all agents in a category
415
537
  * @param category - The agent category
@@ -17,7 +17,7 @@
17
17
  * state — we don't disable it implicitly, because users may set keys
18
18
  * later via `aqe llm config --set`.
19
19
  */
20
- import type { RouterConfig, ExtendedProviderType } from './types.js';
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
23
  /**
@@ -52,6 +52,22 @@ export declare function saveRouterConfigFile(config: Partial<RouterConfig>, proj
52
52
  * top-level scalars use last-wins.
53
53
  */
54
54
  export declare function mergeRouterConfig(base: RouterConfig, override: Partial<RouterConfig>): RouterConfig;
55
+ /**
56
+ * Validate and normalize an on-disk `agentOverrides` map (issue #568).
57
+ *
58
+ * User-hand-edited config is untrusted input: drop entries that name a provider
59
+ * we cannot construct, and strip any `apiKey`-shaped field that leaked in — the
60
+ * same "never persist API keys" discipline `saveRouterConfigFile` already
61
+ * enforces for `providers.*`.
62
+ *
63
+ * Returns the cleaned map plus human-readable warnings for the caller to log.
64
+ * Invalid entries are dropped, never thrown on: a typo in one agent key must not
65
+ * take down router initialization for the whole project.
66
+ */
67
+ export declare function sanitizeAgentOverrides(raw: unknown): {
68
+ overrides: Record<string, AgentProviderOverride>;
69
+ warnings: string[];
70
+ };
55
71
  /**
56
72
  * Apply env-detection on top of a merged config: providers whose API
57
73
  * keys are present in env are force-enabled (so a user who sets
@@ -21,6 +21,11 @@ import * as fs from 'fs';
21
21
  import * as path from 'path';
22
22
  import { findProjectRoot } from '../../../kernel/unified-memory.js';
23
23
  import { DEFAULT_ROUTER_CONFIG, ALL_PROVIDER_TYPES } from './types.js';
24
+ import { createLogger } from '../../../logging/logger-factory.js';
25
+ // `console.warn` is not usable here: in MCP stdio mode stdout is the protocol
26
+ // channel and console output is suppressed, so a config warning written that
27
+ // way is invisible in the most common way AQE runs (issue #568).
28
+ const logger = createLogger('llm/router/config-store');
24
29
  /** Filename under .agentic-qe/ */
25
30
  export const ROUTER_CONFIG_FILENAME = 'llm-config.json';
26
31
  /**
@@ -39,6 +44,10 @@ const PROVIDER_ENV_KEYS = {
39
44
  // availability is gated on the `claude` binary being on PATH (see the
40
45
  // provider's isAvailable()).
41
46
  'claude-code': [],
47
+ // ADR-124 M3.5: codex authenticates via the user's ChatGPT login
48
+ // (subscription), NOT an env key. Availability is gated on the `codex`
49
+ // binary being on PATH (see the provider's isAvailable()).
50
+ codex: [],
42
51
  openai: ['OPENAI_API_KEY'],
43
52
  ollama: [], // ollama is local; no key required
44
53
  openrouter: ['OPENROUTER_API_KEY'],
@@ -59,6 +68,7 @@ const PROVIDER_ENV_KEYS = {
59
68
  const RUNTIME_CONSTRUCTIBLE_PROVIDERS = new Set([
60
69
  'claude',
61
70
  'claude-code',
71
+ 'codex',
62
72
  'openai',
63
73
  'ollama',
64
74
  'openrouter',
@@ -197,8 +207,97 @@ export function mergeRouterConfig(base, override) {
197
207
  };
198
208
  }
199
209
  }
210
+ // Issue #568: `agentOverrides` gets keyed-object merge semantics (like
211
+ // `providers`), NOT the shallow-replace treatment `fallbackChain` gets.
212
+ // Overriding one agent type must not wipe the rest.
213
+ if (base.agentOverrides || override.agentOverrides) {
214
+ merged.agentOverrides = { ...(base.agentOverrides ?? {}) };
215
+ for (const [agentType, cfg] of Object.entries(override.agentOverrides ?? {})) {
216
+ if (!cfg)
217
+ continue;
218
+ merged.agentOverrides[agentType] = {
219
+ ...(base.agentOverrides?.[agentType] ?? {}),
220
+ ...cfg,
221
+ };
222
+ }
223
+ }
200
224
  return merged;
201
225
  }
226
+ /**
227
+ * Validate and normalize an on-disk `agentOverrides` map (issue #568).
228
+ *
229
+ * User-hand-edited config is untrusted input: drop entries that name a provider
230
+ * we cannot construct, and strip any `apiKey`-shaped field that leaked in — the
231
+ * same "never persist API keys" discipline `saveRouterConfigFile` already
232
+ * enforces for `providers.*`.
233
+ *
234
+ * Returns the cleaned map plus human-readable warnings for the caller to log.
235
+ * Invalid entries are dropped, never thrown on: a typo in one agent key must not
236
+ * take down router initialization for the whole project.
237
+ */
238
+ export function sanitizeAgentOverrides(raw) {
239
+ const overrides = {};
240
+ const warnings = [];
241
+ if (raw === undefined || raw === null)
242
+ return { overrides, warnings };
243
+ if (typeof raw !== 'object' || Array.isArray(raw)) {
244
+ warnings.push('`agentOverrides` must be an object keyed by agent type; ignoring it.');
245
+ return { overrides, warnings };
246
+ }
247
+ for (const [agentType, value] of Object.entries(raw)) {
248
+ if (!agentType.trim())
249
+ continue;
250
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
251
+ warnings.push(`agentOverrides["${agentType}"] must be an object; ignoring it.`);
252
+ continue;
253
+ }
254
+ const entry = value;
255
+ const cleaned = {};
256
+ if (entry.provider !== undefined) {
257
+ const provider = entry.provider;
258
+ if (!ALL_PROVIDER_TYPES.includes(provider)) {
259
+ warnings.push(`agentOverrides["${agentType}"].provider "${String(entry.provider)}" is not a known ` +
260
+ `provider (${ALL_PROVIDER_TYPES.join(', ')}); ignoring this entry.`);
261
+ continue;
262
+ }
263
+ if (!RUNTIME_CONSTRUCTIBLE_PROVIDERS.has(provider)) {
264
+ warnings.push(`agentOverrides["${agentType}"].provider "${provider}" cannot be instantiated at ` +
265
+ `runtime yet; ignoring this entry.`);
266
+ continue;
267
+ }
268
+ cleaned.provider = provider;
269
+ }
270
+ if (typeof entry.model === 'string' && entry.model.trim()) {
271
+ cleaned.model = entry.model.trim();
272
+ }
273
+ if (typeof entry.temperature === 'number' && Number.isFinite(entry.temperature)) {
274
+ cleaned.temperature = entry.temperature;
275
+ }
276
+ if (typeof entry.maxTokens === 'number' && Number.isFinite(entry.maxTokens)) {
277
+ cleaned.maxTokens = entry.maxTokens;
278
+ }
279
+ if (typeof entry.priority === 'number' && Number.isFinite(entry.priority)) {
280
+ cleaned.priority = entry.priority;
281
+ }
282
+ // Secrets are already dropped by construction: `cleaned` is an ALLOW-LIST,
283
+ // so anything not explicitly copied above never survives. This check exists
284
+ // only so a user who pasted a credential in here is TOLD it was discarded
285
+ // rather than silently wondering why their key isn't being used.
286
+ // Deliberately does not echo the value.
287
+ const secretish = Object.keys(entry).filter(k => /^(api[-_]?key|key|token|secret|password|passwd|auth|authorization|bearer|credential)s?$/i.test(k));
288
+ if (secretish.length > 0) {
289
+ warnings.push(`agentOverrides["${agentType}"] contained credential-shaped field(s) ` +
290
+ `[${secretish.join(', ')}] — discarded, not persisted, not logged. Credentials belong ` +
291
+ `in environment variables, never in .agentic-qe/llm-config.json.`);
292
+ }
293
+ if (Object.keys(cleaned).length === 0) {
294
+ warnings.push(`agentOverrides["${agentType}"] had no usable fields; ignoring it.`);
295
+ continue;
296
+ }
297
+ overrides[agentType] = cleaned;
298
+ }
299
+ return { overrides, warnings };
300
+ }
202
301
  /**
203
302
  * Apply env-detection on top of a merged config: providers whose API
204
303
  * keys are present in env are force-enabled (so a user who sets
@@ -282,6 +381,15 @@ export function resolveProviderOverrideFromEnv(env = process.env) {
282
381
  export function loadRouterConfig(options = {}) {
283
382
  const env = options.env ?? process.env;
284
383
  const onDisk = loadRouterConfigFile(options.projectRoot);
384
+ // Issue #568: validate the hand-editable per-agent map before it reaches the
385
+ // router. Bad entries are dropped with a warning, never fatal.
386
+ if (onDisk.agentOverrides !== undefined) {
387
+ const { overrides, warnings } = sanitizeAgentOverrides(onDisk.agentOverrides);
388
+ for (const warning of warnings) {
389
+ logger.warn(`[llm-config] ${warning}`);
390
+ }
391
+ onDisk.agentOverrides = overrides;
392
+ }
285
393
  const merged = mergeRouterConfig(DEFAULT_ROUTER_CONFIG, onDisk);
286
394
  // ADR-123: an explicit `enabled: false` on disk is never resurrected by env.
287
395
  const explicitlyDisabled = collectExplicitlyDisabled(onDisk);
@@ -518,8 +518,8 @@ export class HybridRouter {
518
518
  metadata: params.metadata,
519
519
  });
520
520
  // ADR-123: persist the charge to the cross-process ledger so budgets
521
- // hold across a fleet of processes.
522
- this.providerManager.recordResponseSpend(response);
521
+ // hold across a fleet of processes. ADR-124 M2.3: attribute to the agent.
522
+ this.providerManager.recordResponseSpend(response, params.agentType);
523
523
  const callLatency = Date.now() - callStartTime;
524
524
  // Record success in legacy tracker
525
525
  this.metrics.recordSuccess(providerType);
@@ -21,11 +21,11 @@
21
21
  * - Routing decision audit log
22
22
  * - Cost breakdown by agent type
23
23
  */
24
- export type { ExtendedProviderType, ProviderCapabilities, ExtendedLLMProvider, RequiredFeature, ModelFamily, ModelTier, ModelMapping, ModelRegistry, MessageFormat, SystemPromptStrategy, ToolSchemaFormat, TranslatedMessages, ToolDefinition, TranslatedTools, TranslationOptions, PromptTranslator, RoutingMode, TaskComplexity, RuleCondition, RuleAction, RoutingRule, SelectionReason, RoutingDecision, FallbackBehavior, FallbackChainEntry, FallbackChain, RouterConfig, ChatParams, ChatResponse, StreamChunk, ProviderRoutingMetrics, RouterMetrics, } from './types';
24
+ export type { ExtendedProviderType, ProviderCapabilities, ExtendedLLMProvider, RequiredFeature, ModelFamily, ModelTier, ModelMapping, ModelRegistry, MessageFormat, SystemPromptStrategy, ToolSchemaFormat, TranslatedMessages, ToolDefinition, TranslatedTools, TranslationOptions, PromptTranslator, RoutingMode, TaskComplexity, RuleCondition, RuleAction, RoutingRule, SelectionReason, RoutingDecision, FallbackBehavior, FallbackChainEntry, FallbackChain, RouterConfig, AgentProviderOverride, ChatParams, ChatResponse, StreamChunk, ProviderRoutingMetrics, RouterMetrics, } from './types';
25
25
  export { ALL_PROVIDER_TYPES, DEFAULT_PROVIDER_CAPABILITIES, DEFAULT_FALLBACK_BEHAVIOR, DEFAULT_ROUTER_CONFIG, } from './types';
26
26
  export { RoutingRuleEngine, DEFAULT_QE_ROUTING_RULES, createRoutingRule, mergeWithDefaultRules, inferComplexity, getExtendedAgentCategory, getModelPreferenceForAgent, agentRequiresAdvancedReasoning, agentIsCostSensitive, agentIsLatencySensitive, getAgentTypesInCategory, getAllAgentCategories, getCategoryModelPreference, createAgentBasedRoutingRule, createCategoryBasedRoutingRules, generateComprehensiveAgentRules, mergeAgentAwareRules, } from './routing-rules';
27
27
  export type { ExtendedAgentCategory } from './routing-rules';
28
28
  export type { AgentCategory, ModelPreference, AgentCapabilityRequirements, AgentRoutingConfig, AgentRouterConfig, AgentRoutingOverride, } from './agent-router-config';
29
- export { DEFAULT_CATEGORY_MODELS, AGENT_CATEGORY_MAP, DEFAULT_CATEGORY_CAPABILITIES, DEFAULT_ROUTING_OVERRIDES, ALTERNATIVE_MODELS, getAgentRoutingCategory, getPreferredModelForAgent, getAgentCapabilityRequirements, getAgentsByCategory, requiresAdvancedReasoning, isCostSensitive, isLatencySensitive, createAgentRoutingRule, createCategoryRoutingRules, createAgentAwareRules, createAllAgentRoutingRules, getAlternativeModelsForAgent, buildAgentRouterConfig, } from './agent-router-config';
29
+ export { DEFAULT_CATEGORY_MODELS, AGENT_CATEGORY_MAP, DEFAULT_CATEGORY_CAPABILITIES, DEFAULT_ROUTING_OVERRIDES, ALTERNATIVE_MODELS, getAgentRoutingCategory, getPreferredModelForAgent, getAgentCapabilityRequirements, getAgentsByCategory, requiresAdvancedReasoning, isCostSensitive, isLatencySensitive, createAgentRoutingRule, createCategoryRoutingRules, createAgentAwareRules, createAllAgentRoutingRules, setAgentProviderOverrides, getAgentProviderOverrides, resetAgentProviderOverrides, createOverrideRoutingRules, getAlternativeModelsForAgent, buildAgentRouterConfig, } from './agent-router-config';
30
30
  export { HybridRouter, createHybridRouter, createQERouter, } from './hybrid-router';
31
31
  //# sourceMappingURL=index.d.ts.map
@@ -38,6 +38,8 @@ DEFAULT_CATEGORY_MODELS, AGENT_CATEGORY_MAP, DEFAULT_CATEGORY_CAPABILITIES, DEFA
38
38
  getAgentRoutingCategory, getPreferredModelForAgent, getAgentCapabilityRequirements, getAgentsByCategory, requiresAdvancedReasoning, isCostSensitive, isLatencySensitive,
39
39
  // Rule generation
40
40
  createAgentRoutingRule, createCategoryRoutingRules, createAgentAwareRules, createAllAgentRoutingRules,
41
+ // On-disk per-agent overrides (issue #568)
42
+ setAgentProviderOverrides, getAgentProviderOverrides, resetAgentProviderOverrides, createOverrideRoutingRules,
41
43
  // Alternative models
42
44
  getAlternativeModelsForAgent,
43
45
  // Configuration builder
@@ -562,6 +562,50 @@ export interface RouterConfig {
562
562
  cacheConfig?: RouterCacheConfig;
563
563
  /** Audit logging configuration (ADR-043) */
564
564
  auditConfig?: AuditConfig;
565
+ /**
566
+ * Per-agent-type provider/model overrides (issue #568).
567
+ *
568
+ * Keyed by agent type (`qe-security-scanner`, `qe-test-architect`, ...).
569
+ * These take priority over the category defaults in
570
+ * `agent-router-config.ts` (`AGENT_CATEGORY_MAP` -> `DEFAULT_CATEGORY_MODELS`),
571
+ * letting a user route security-sensitive agents to a stronger model and
572
+ * mechanical ones to a cheap/local provider without touching
573
+ * `defaultProvider`.
574
+ *
575
+ * Set on disk in `.agentic-qe/llm-config.json`:
576
+ * ```jsonc
577
+ * {
578
+ * "defaultProvider": "claude-code",
579
+ * "agentOverrides": {
580
+ * "qe-security-scanner": { "provider": "cognitum", "model": "cognitum-high" },
581
+ * "qe-mutation-tester": { "provider": "ollama" }
582
+ * }
583
+ * }
584
+ * ```
585
+ * Unset fields keep their category default. Never put API keys here.
586
+ */
587
+ agentOverrides?: Record<string, AgentProviderOverride>;
588
+ }
589
+ /**
590
+ * On-disk, per-agent-type routing override (issue #568).
591
+ *
592
+ * A deliberately narrow, hand-editable subset of `ModelPreference` from
593
+ * `agent-router-config.ts`: `provider` and `model` are the only fields a user
594
+ * realistically wants to set by hand, but `temperature`/`maxTokens`/`priority`
595
+ * are accepted for parity with the in-process `AgentRoutingOverride` mechanism.
596
+ * Every field is optional and falls back to the agent's category default.
597
+ */
598
+ export interface AgentProviderOverride {
599
+ /** Provider to route this agent type through. */
600
+ provider?: ExtendedProviderType;
601
+ /** Model identifier within that provider. */
602
+ model?: string;
603
+ /** Sampling temperature. */
604
+ temperature?: number;
605
+ /** Max tokens for typical requests. */
606
+ maxTokens?: number;
607
+ /** Rule priority; higher wins. Defaults to above the category rule. */
608
+ priority?: number;
565
609
  }
566
610
  /**
567
611
  * Routing context provided with chat requests
@@ -24,6 +24,7 @@
24
24
  export const ALL_PROVIDER_TYPES = [
25
25
  'claude',
26
26
  'claude-code', // ADR-123: subscription-billed via `claude -p`
27
+ 'codex', // ADR-124 M3.5: subscription-billed via `codex exec` (issue #568)
27
28
  'openai',
28
29
  'ollama',
29
30
  'openrouter',
@@ -28,6 +28,12 @@ export interface SpendEntry {
28
28
  promptTokens: number;
29
29
  completionTokens: number;
30
30
  requestId: string;
31
+ /**
32
+ * ADR-124 M2.3: the QE agent type/name this charge is attributed to (e.g.
33
+ * 'qe-security-scanner'). Optional — null when the call site has no agent
34
+ * context. Enables per-agent cost attribution across the fleet.
35
+ */
36
+ agent?: string;
31
37
  }
32
38
  /** Cross-process spend ledger. */
33
39
  export interface SpendLedger {
@@ -35,16 +41,33 @@ export interface SpendLedger {
35
41
  record(entry: SpendEntry): void;
36
42
  /** Total USD charged within the last `windowMs` (rolling window ending now). */
37
43
  spentSince(windowMs: number, nowMs?: number): number;
44
+ /**
45
+ * ADR-124 M2.3: USD charged within the last `windowMs`, broken down by agent
46
+ * type. Charges with no agent are grouped under '(unattributed)'.
47
+ */
48
+ spentByAgentSince(windowMs: number, nowMs?: number): Record<string, number>;
38
49
  }
50
+ /** Bucket used for charges recorded without an agent context. */
51
+ export declare const UNATTRIBUTED_AGENT = "(unattributed)";
39
52
  /**
40
53
  * SQLite-backed ledger. Cross-process because every process opens its own
41
54
  * connection to the same WAL-mode `memory.db`.
42
55
  */
43
56
  export declare class SqliteSpendLedger implements SpendLedger {
44
57
  private readonly db;
58
+ /**
59
+ * Whether the `agent_type` column exists. If the additive migration couldn't
60
+ * run (e.g. transient lock), this stays false and record() INSERTs WITHOUT the
61
+ * column — so spend is still recorded and the ADR-123 budget cap still holds.
62
+ * A qe-court review (ADR-124) caught the original bug: a swallowed migration
63
+ * made every INSERT reference a missing column, throw, get caught, and silently
64
+ * drop ALL spend — bypassing the budget cap (the #557 class).
65
+ */
66
+ private hasAgentColumn;
45
67
  constructor(db: DatabaseType);
46
68
  record(entry: SpendEntry): void;
47
69
  spentSince(windowMs: number, nowMs?: number): number;
70
+ spentByAgentSince(windowMs: number, nowMs?: number): Record<string, number>;
48
71
  }
49
72
  /**
50
73
  * In-process fallback ledger. Used when no unified DB is available. Enforces
@@ -54,6 +77,7 @@ export declare class InMemorySpendLedger implements SpendLedger {
54
77
  private readonly entries;
55
78
  record(entry: SpendEntry): void;
56
79
  spentSince(windowMs: number, nowMs?: number): number;
80
+ spentByAgentSince(windowMs: number, nowMs?: number): Record<string, number>;
57
81
  }
58
82
  /**
59
83
  * Build the default ledger. Prefers the unified `memory.db` (cross-process);
@@ -17,6 +17,8 @@
17
17
  * connections (one per process) to a WAL-mode DB is exactly the supported
18
18
  * cross-process pattern.
19
19
  */
20
+ /** Bucket used for charges recorded without an agent context. */
21
+ export const UNATTRIBUTED_AGENT = '(unattributed)';
20
22
  const CREATE_TABLE_SQL = `
21
23
  CREATE TABLE IF NOT EXISTS llm_spend (
22
24
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -37,27 +39,72 @@ CREATE INDEX IF NOT EXISTS idx_llm_spend_ts ON llm_spend(ts);
37
39
  */
38
40
  export class SqliteSpendLedger {
39
41
  db;
42
+ /**
43
+ * Whether the `agent_type` column exists. If the additive migration couldn't
44
+ * run (e.g. transient lock), this stays false and record() INSERTs WITHOUT the
45
+ * column — so spend is still recorded and the ADR-123 budget cap still holds.
46
+ * A qe-court review (ADR-124) caught the original bug: a swallowed migration
47
+ * made every INSERT reference a missing column, throw, get caught, and silently
48
+ * drop ALL spend — bypassing the budget cap (the #557 class).
49
+ */
50
+ hasAgentColumn = false;
40
51
  constructor(db) {
41
52
  this.db = db;
42
53
  // Additive schema only — CREATE TABLE/INDEX IF NOT EXISTS never drops data.
43
54
  this.db.exec(CREATE_TABLE_SQL);
55
+ // ADR-124 M2.3: additive `agent_type` column for per-agent attribution.
56
+ // ALTER TABLE ADD COLUMN is non-destructive (existing rows get NULL); guarded
57
+ // so re-opening an already-migrated DB is a no-op. Never drops or rewrites.
58
+ try {
59
+ const cols = this.db.prepare(`PRAGMA table_info(llm_spend)`).all();
60
+ if (!cols.some((c) => c.name === 'agent_type')) {
61
+ this.db.exec(`ALTER TABLE llm_spend ADD COLUMN agent_type TEXT`);
62
+ }
63
+ this.db.exec(`CREATE INDEX IF NOT EXISTS idx_llm_spend_agent ON llm_spend(agent_type)`);
64
+ this.hasAgentColumn = true;
65
+ }
66
+ catch {
67
+ // Attribution is best-effort; never block ledger use on the migration.
68
+ // hasAgentColumn stays false → record() degrades to un-attributed INSERTs.
69
+ }
44
70
  }
45
71
  record(entry) {
46
72
  try {
47
- this.db
48
- .prepare(`INSERT INTO llm_spend
49
- (ts, provider, model, cost_usd, cost_source, prompt_tokens, completion_tokens, request_id)
50
- VALUES (@ts, @provider, @model, @cost_usd, @cost_source, @prompt_tokens, @completion_tokens, @request_id)`)
51
- .run({
52
- ts: Date.now(),
53
- provider: entry.provider,
54
- model: entry.model,
55
- cost_usd: entry.costUsd,
56
- cost_source: entry.costSource,
57
- prompt_tokens: entry.promptTokens,
58
- completion_tokens: entry.completionTokens,
59
- request_id: entry.requestId,
60
- });
73
+ if (this.hasAgentColumn) {
74
+ this.db
75
+ .prepare(`INSERT INTO llm_spend
76
+ (ts, provider, model, cost_usd, cost_source, prompt_tokens, completion_tokens, request_id, agent_type)
77
+ VALUES (@ts, @provider, @model, @cost_usd, @cost_source, @prompt_tokens, @completion_tokens, @request_id, @agent_type)`)
78
+ .run({
79
+ ts: Date.now(),
80
+ provider: entry.provider,
81
+ model: entry.model,
82
+ cost_usd: entry.costUsd,
83
+ cost_source: entry.costSource,
84
+ prompt_tokens: entry.promptTokens,
85
+ completion_tokens: entry.completionTokens,
86
+ request_id: entry.requestId,
87
+ agent_type: entry.agent ?? null,
88
+ });
89
+ }
90
+ else {
91
+ // Column absent (migration couldn't run) — still record spend so the
92
+ // budget cap holds; just without per-agent attribution.
93
+ this.db
94
+ .prepare(`INSERT INTO llm_spend
95
+ (ts, provider, model, cost_usd, cost_source, prompt_tokens, completion_tokens, request_id)
96
+ VALUES (@ts, @provider, @model, @cost_usd, @cost_source, @prompt_tokens, @completion_tokens, @request_id)`)
97
+ .run({
98
+ ts: Date.now(),
99
+ provider: entry.provider,
100
+ model: entry.model,
101
+ cost_usd: entry.costUsd,
102
+ cost_source: entry.costSource,
103
+ prompt_tokens: entry.promptTokens,
104
+ completion_tokens: entry.completionTokens,
105
+ request_id: entry.requestId,
106
+ });
107
+ }
61
108
  }
62
109
  catch {
63
110
  // A ledger write must never break a generation call.
@@ -74,6 +121,21 @@ export class SqliteSpendLedger {
74
121
  return 0;
75
122
  }
76
123
  }
124
+ spentByAgentSince(windowMs, nowMs = Date.now()) {
125
+ try {
126
+ const rows = this.db
127
+ .prepare(`SELECT COALESCE(agent_type, ?) AS agent, COALESCE(SUM(cost_usd), 0) AS total
128
+ FROM llm_spend WHERE ts >= ? GROUP BY COALESCE(agent_type, ?)`)
129
+ .all(UNATTRIBUTED_AGENT, nowMs - windowMs, UNATTRIBUTED_AGENT);
130
+ const out = {};
131
+ for (const r of rows)
132
+ out[r.agent] = r.total;
133
+ return out;
134
+ }
135
+ catch {
136
+ return {};
137
+ }
138
+ }
77
139
  }
78
140
  /**
79
141
  * In-process fallback ledger. Used when no unified DB is available. Enforces
@@ -82,7 +144,7 @@ export class SqliteSpendLedger {
82
144
  export class InMemorySpendLedger {
83
145
  entries = [];
84
146
  record(entry) {
85
- this.entries.push({ ts: Date.now(), costUsd: entry.costUsd });
147
+ this.entries.push({ ts: Date.now(), costUsd: entry.costUsd, agent: entry.agent ?? UNATTRIBUTED_AGENT });
86
148
  }
87
149
  spentSince(windowMs, nowMs = Date.now()) {
88
150
  const cutoff = nowMs - windowMs;
@@ -93,6 +155,15 @@ export class InMemorySpendLedger {
93
155
  }
94
156
  return total;
95
157
  }
158
+ spentByAgentSince(windowMs, nowMs = Date.now()) {
159
+ const cutoff = nowMs - windowMs;
160
+ const out = {};
161
+ for (const e of this.entries) {
162
+ if (e.ts >= cutoff)
163
+ out[e.agent] = (out[e.agent] ?? 0) + e.costUsd;
164
+ }
165
+ return out;
166
+ }
96
167
  }
97
168
  /**
98
169
  * Build the default ledger. Prefers the unified `memory.db` (cross-process);
@@ -16,6 +16,7 @@
16
16
  const SYSTEM_PROMPT_STRATEGIES = {
17
17
  claude: 'native', // Anthropic supports separate system param
18
18
  'claude-code': 'native', // ADR-123: passed via --append-system-prompt
19
+ codex: 'native', // ADR-124 M3.5: provider flattens system into the prompt
19
20
  openai: 'native', // OpenAI supports system role messages
20
21
  ollama: 'native', // Ollama supports system role
21
22
  openrouter: 'native', // OpenRouter passes through to underlying provider
@@ -18,6 +18,16 @@ export interface SafeDbOptions {
18
18
  busyTimeout?: number;
19
19
  /** Enable WAL mode (default: true for writable, false for readonly) */
20
20
  walMode?: boolean;
21
+ /**
22
+ * M3.2: on a malformed/corrupt existing DB, auto-restore the newest verified
23
+ * backup (from `.agentic-qe/backups/verified/`) and park the corrupt original,
24
+ * instead of opening a broken DB. Default false; enable for the unified
25
+ * `memory.db` open. Non-destructive: the corrupt file is renamed, never deleted,
26
+ * and nothing happens if no verified backup exists. Ignored for readonly opens.
27
+ */
28
+ autoRestore?: boolean;
29
+ /** Override the verified-backup directory used by autoRestore. */
30
+ backupDir?: string;
21
31
  }
22
32
  /**
23
33
  * Open a SQLite database with safe defaults that prevent corruption.