@ebrindley/build 0.0.0-stage → 1.42.1-rc28

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 (878) hide show
  1. package/.build-project/providers/catalog.json +13951 -0
  2. package/.build-project/providers/pricing.json +1978 -0
  3. package/.build-project/providers/registry.json +4103 -0
  4. package/.github/SECURITY.md +75 -0
  5. package/.nvmrc +1 -0
  6. package/CHANGELOG.md +661 -0
  7. package/CODE_OF_CONDUCT.md +44 -0
  8. package/CONTRIBUTING.md +24 -0
  9. package/INSTALL.md +623 -0
  10. package/LICENSE +21 -0
  11. package/README.md +118 -2
  12. package/THIRD_PARTY_NOTICES.txt +26 -0
  13. package/dist/ai-judge-UE5UOYOO.js +191 -0
  14. package/dist/allowlist-grounding-KPLN6NC5.js +16 -0
  15. package/dist/allowlist-utils-MZZKKXRD.js +31 -0
  16. package/dist/apply-completion-mode-EM37UZHB.js +110 -0
  17. package/dist/artifact-migrator-WPACNSEP.js +276 -0
  18. package/dist/ask-2OZ4JFQR.js +176 -0
  19. package/dist/auth-6UKBTJUQ.js +185 -0
  20. package/dist/auth-H5KJL6HL.js +165 -0
  21. package/dist/auth-LVJRJDMU.js +12 -0
  22. package/dist/auth-NHZOCNRR.js +94 -0
  23. package/dist/auth-PBXHWXGW.js +95 -0
  24. package/dist/auth-T7Y5DCAH.js +91 -0
  25. package/dist/auth-TVVKCHQW.js +13 -0
  26. package/dist/auth-UHB2UCSS.js +97 -0
  27. package/dist/auth-VACA3VUL.js +98 -0
  28. package/dist/auth-VVLC77S3.js +44 -0
  29. package/dist/auth-liveness-XO4AGDDD.js +53 -0
  30. package/dist/autoloop-cleanup-SI3VGAAR.js +106 -0
  31. package/dist/autoloop-evidence-gates-J5GG5YQB.js +39 -0
  32. package/dist/autoloop-gate-policy-SUNAEP6Q.js +169 -0
  33. package/dist/autoloop-helpers-VQGQNTT6.js +52 -0
  34. package/dist/autoloop-ledger-P6TIDP7C.js +96 -0
  35. package/dist/autoloop-ledger-subscriber-HFAGOZXH.js +161 -0
  36. package/dist/autoloop-run-defaults-GWGZBNT7.js +27 -0
  37. package/dist/autoloop-run-lock-GBWRM5A2.js +197 -0
  38. package/dist/autoloop-success-C32QVWOP.js +93 -0
  39. package/dist/autoloop-termination-summary-GI6IBQWD.js +58 -0
  40. package/dist/autoloop-trajectory-AMKSQISD.js +70 -0
  41. package/dist/autoloop-wall-clock-T7ZVIQOC.js +12 -0
  42. package/dist/automatic-branch-cleanup-R62X2C5O.js +49 -0
  43. package/dist/autonomous-loop-controller-2WL363LW.js +4386 -0
  44. package/dist/backend-YUTFDZXZ.js +30 -0
  45. package/dist/background-executor-Z3ODTMD7.js +260 -0
  46. package/dist/backlog-75QPEAMW.js +403 -0
  47. package/dist/backlog-execution-intent-GWUV5TUK.js +19 -0
  48. package/dist/backup-active-store-XHWLN34M.js +16 -0
  49. package/dist/backup-machine-stores-UI4LTJOY.js +251 -0
  50. package/dist/backup-telemetry-db-Z4ICYCNW.js +26 -0
  51. package/dist/branch-archive-manager-FMSQ7YJF.js +18 -0
  52. package/dist/branch-cleanup-manager-ST6FJGND.js +35 -0
  53. package/dist/build-gate-7NKWPY62.js +68 -0
  54. package/dist/build-root-S7DINODY.js +18 -0
  55. package/dist/change-summary-R6CB6VXJ.js +50 -0
  56. package/dist/chunk-24MNKKOM.js +173 -0
  57. package/dist/chunk-2H6VZFIO.js +235 -0
  58. package/dist/chunk-2JIX3EKV.js +39 -0
  59. package/dist/chunk-2KO7HVZM.js +106 -0
  60. package/dist/chunk-2L36U25O.js +100 -0
  61. package/dist/chunk-2NMNFUQA.js +475 -0
  62. package/dist/chunk-2OUAEH67.js +83 -0
  63. package/dist/chunk-2VLJUXJU.js +115 -0
  64. package/dist/chunk-2VUEDIUM.js +77 -0
  65. package/dist/chunk-2Y2IIHPI.js +430 -0
  66. package/dist/chunk-2YRG3LHZ.js +220 -0
  67. package/dist/chunk-2Z5Q3SUY.js +65 -0
  68. package/dist/chunk-32IYXIE5.js +873 -0
  69. package/dist/chunk-33IGPPUI.js +292 -0
  70. package/dist/chunk-352PCJLK.js +283 -0
  71. package/dist/chunk-35J4NE6S.js +226 -0
  72. package/dist/chunk-3C7WBIVK.js +1016 -0
  73. package/dist/chunk-3H3EY6U7.js +1225 -0
  74. package/dist/chunk-3H4MWYIC.js +198 -0
  75. package/dist/chunk-3HHWQR3N.js +2479 -0
  76. package/dist/chunk-3JKNRYEE.js +14 -0
  77. package/dist/chunk-3LECCYZY.js +128 -0
  78. package/dist/chunk-3LOY6YZI.js +852 -0
  79. package/dist/chunk-3M57DADF.js +44 -0
  80. package/dist/chunk-3NOTLVLG.js +3803 -0
  81. package/dist/chunk-3OPP2OZB.js +1480 -0
  82. package/dist/chunk-3P5IN5E7.js +9 -0
  83. package/dist/chunk-3PL22IYZ.js +20 -0
  84. package/dist/chunk-3REDAJUJ.js +14 -0
  85. package/dist/chunk-3S2R4XJI.js +203 -0
  86. package/dist/chunk-3UBZI5DW.js +61 -0
  87. package/dist/chunk-3VV5OBGG.js +308 -0
  88. package/dist/chunk-3WY5TU2O.js +823 -0
  89. package/dist/chunk-43OKHTW4.js +82 -0
  90. package/dist/chunk-45KUJBDI.js +112 -0
  91. package/dist/chunk-47X5LM7B.js +224 -0
  92. package/dist/chunk-4B6FQJ77.js +268 -0
  93. package/dist/chunk-4BTDUT2S.js +42 -0
  94. package/dist/chunk-4DJL2VXA.js +24 -0
  95. package/dist/chunk-4EBSXZ73.js +48 -0
  96. package/dist/chunk-4QKP6I4C.js +294 -0
  97. package/dist/chunk-4U6XK2DG.js +300 -0
  98. package/dist/chunk-4UMRUFBD.js +132 -0
  99. package/dist/chunk-4US4SYJ4.js +569 -0
  100. package/dist/chunk-4ZPLWEZO.js +154 -0
  101. package/dist/chunk-56WPYDJH.js +624 -0
  102. package/dist/chunk-57WRCJJW.js +50 -0
  103. package/dist/chunk-5AW4RMNG.js +24 -0
  104. package/dist/chunk-5EQN56OZ.js +155 -0
  105. package/dist/chunk-5F43TUIN.js +38 -0
  106. package/dist/chunk-5FKHIKTS.js +115 -0
  107. package/dist/chunk-5HDP7XZG.js +91 -0
  108. package/dist/chunk-5KQETTNS.js +152 -0
  109. package/dist/chunk-5PDVN2KO.js +498 -0
  110. package/dist/chunk-5UBIFVRD.js +509 -0
  111. package/dist/chunk-5YDE3Q62.js +46 -0
  112. package/dist/chunk-626AJVMU.js +302 -0
  113. package/dist/chunk-633XZ7UF.js +3583 -0
  114. package/dist/chunk-63DHNHGA.js +236 -0
  115. package/dist/chunk-666656AR.js +568 -0
  116. package/dist/chunk-67YRJPJO.js +19 -0
  117. package/dist/chunk-6CAU7VBA.js +20 -0
  118. package/dist/chunk-6CB3KJRY.js +21 -0
  119. package/dist/chunk-6DBMALXL.js +29 -0
  120. package/dist/chunk-6E25C4IA.js +269 -0
  121. package/dist/chunk-6FIBNIQU.js +73 -0
  122. package/dist/chunk-6G57FYBV.js +189 -0
  123. package/dist/chunk-6KZKRTG3.js +4885 -0
  124. package/dist/chunk-6LAH2UV2.js +525 -0
  125. package/dist/chunk-6PZY2TTU.js +921 -0
  126. package/dist/chunk-6T6NHXK4.js +230 -0
  127. package/dist/chunk-6TBEX4R4.js +432 -0
  128. package/dist/chunk-6TRD7TEE.js +911 -0
  129. package/dist/chunk-6XCP2RAI.js +47 -0
  130. package/dist/chunk-726G6WDL.js +351 -0
  131. package/dist/chunk-75EMT7BF.js +616 -0
  132. package/dist/chunk-762ACDZJ.js +4696 -0
  133. package/dist/chunk-77I6G4CO.js +1 -0
  134. package/dist/chunk-7DJ6SQH3.js +510 -0
  135. package/dist/chunk-7HPCIOMA.js +1383 -0
  136. package/dist/chunk-7HYBSRPZ.js +17503 -0
  137. package/dist/chunk-7KBEOPVB.js +73 -0
  138. package/dist/chunk-7LHJDJB3.js +2222 -0
  139. package/dist/chunk-7NVJMH52.js +306 -0
  140. package/dist/chunk-7RMARDVH.js +33 -0
  141. package/dist/chunk-7SEWU4QG.js +36 -0
  142. package/dist/chunk-7TUTZTI2.js +911 -0
  143. package/dist/chunk-7VBDFPUM.js +340 -0
  144. package/dist/chunk-7VRJHTGZ.js +6406 -0
  145. package/dist/chunk-7YAOUUKK.js +493 -0
  146. package/dist/chunk-AAAODV76.js +860 -0
  147. package/dist/chunk-AB4HUJVQ.js +339 -0
  148. package/dist/chunk-AEQWV746.js +611 -0
  149. package/dist/chunk-AHU3FKPA.js +11639 -0
  150. package/dist/chunk-AISTV5XU.js +226 -0
  151. package/dist/chunk-AJBTKGJZ.js +48 -0
  152. package/dist/chunk-AJJNW7XT.js +248 -0
  153. package/dist/chunk-AJN5Y5YZ.js +160 -0
  154. package/dist/chunk-ALDTFNZ3.js +150 -0
  155. package/dist/chunk-ARARVIZK.js +281 -0
  156. package/dist/chunk-ATBM6AW5.js +53 -0
  157. package/dist/chunk-ATKEKM3E.js +206 -0
  158. package/dist/chunk-AUJLW6JZ.js +92 -0
  159. package/dist/chunk-AY3MHEFM.js +24 -0
  160. package/dist/chunk-B7PYTV72.js +236 -0
  161. package/dist/chunk-B7YRSULE.js +1423 -0
  162. package/dist/chunk-BAWJNMWF.js +339 -0
  163. package/dist/chunk-BBPIILO2.js +88 -0
  164. package/dist/chunk-BD3PGW6A.js +957 -0
  165. package/dist/chunk-BDPYFAB4.js +44 -0
  166. package/dist/chunk-BIUV23HH.js +729 -0
  167. package/dist/chunk-BQ3VAGXH.js +243 -0
  168. package/dist/chunk-BQJOGAWT.js +31 -0
  169. package/dist/chunk-BT7EG7CO.js +68 -0
  170. package/dist/chunk-BV3XQQLU.js +27 -0
  171. package/dist/chunk-BY6ODHNZ.js +78 -0
  172. package/dist/chunk-BZRLSZNV.js +79 -0
  173. package/dist/chunk-C3DH3NCY.js +2773 -0
  174. package/dist/chunk-C3KCMAJP.js +756 -0
  175. package/dist/chunk-C7AGUJ5K.js +267 -0
  176. package/dist/chunk-CBSWKQCX.js +342 -0
  177. package/dist/chunk-CE5OZBYY.js +21 -0
  178. package/dist/chunk-CFCVYHSW.js +373 -0
  179. package/dist/chunk-CHA2HDE5.js +126 -0
  180. package/dist/chunk-CIHWPGS5.js +146 -0
  181. package/dist/chunk-CJBSZX3F.js +127 -0
  182. package/dist/chunk-CLA6QIJM.js +43 -0
  183. package/dist/chunk-CPSVQR2I.js +365 -0
  184. package/dist/chunk-CRPRVNQ4.js +254 -0
  185. package/dist/chunk-CSIE4Y7C.js +1093 -0
  186. package/dist/chunk-CSOPSYDY.js +142 -0
  187. package/dist/chunk-CSXLLEGB.js +205 -0
  188. package/dist/chunk-CTDLERWU.js +293 -0
  189. package/dist/chunk-CULBXMSK.js +110 -0
  190. package/dist/chunk-D2M2Y5ID.js +21 -0
  191. package/dist/chunk-D37WY7SH.js +1060 -0
  192. package/dist/chunk-D4VJFCGZ.js +89 -0
  193. package/dist/chunk-D6WTZMZB.js +48 -0
  194. package/dist/chunk-DBHOWDUH.js +976 -0
  195. package/dist/chunk-DCACX7BV.js +19364 -0
  196. package/dist/chunk-DFHX4XYA.js +128 -0
  197. package/dist/chunk-DIKBRRKE.js +176 -0
  198. package/dist/chunk-DJG5RIOR.js +9 -0
  199. package/dist/chunk-DNIOIISW.js +616 -0
  200. package/dist/chunk-DOA5G7IA.js +315 -0
  201. package/dist/chunk-DP7FKP6W.js +855 -0
  202. package/dist/chunk-DS2JZ677.js +79 -0
  203. package/dist/chunk-DSRLVYBS.js +82 -0
  204. package/dist/chunk-EAEVKNJA.js +209 -0
  205. package/dist/chunk-EEUESN3T.js +575 -0
  206. package/dist/chunk-EJ4LUU5Y.js +73 -0
  207. package/dist/chunk-EJIWTPSZ.js +1063 -0
  208. package/dist/chunk-EJVCDTAN.js +116 -0
  209. package/dist/chunk-EKLUBDBQ.js +627 -0
  210. package/dist/chunk-EN62WLFD.js +849 -0
  211. package/dist/chunk-ENYRCL43.js +26 -0
  212. package/dist/chunk-EP22ZMFO.js +273 -0
  213. package/dist/chunk-EP4SVAVI.js +87 -0
  214. package/dist/chunk-EPAMVGI3.js +1562 -0
  215. package/dist/chunk-EPRMH6HO.js +24 -0
  216. package/dist/chunk-ES6NWJ36.js +4057 -0
  217. package/dist/chunk-ETA2K4KI.js +30 -0
  218. package/dist/chunk-ETAD4NKD.js +31 -0
  219. package/dist/chunk-EUWIIDQ7.js +93 -0
  220. package/dist/chunk-EVE3OD2W.js +141 -0
  221. package/dist/chunk-EXA26FFC.js +459 -0
  222. package/dist/chunk-EXSPAA35.js +244 -0
  223. package/dist/chunk-F55YABKV.js +258 -0
  224. package/dist/chunk-F5DNGU5W.js +27 -0
  225. package/dist/chunk-F622MJYV.js +103 -0
  226. package/dist/chunk-F6NIQD74.js +5111 -0
  227. package/dist/chunk-FAWJTC34.js +43 -0
  228. package/dist/chunk-FAZBRHGG.js +47 -0
  229. package/dist/chunk-FIEUSGNJ.js +39 -0
  230. package/dist/chunk-FKCG7K6H.js +1146 -0
  231. package/dist/chunk-FOVVXV45.js +229 -0
  232. package/dist/chunk-FRBPKBFE.js +30 -0
  233. package/dist/chunk-FSA5BPFI.js +268 -0
  234. package/dist/chunk-FSJ5TTZ3.js +29 -0
  235. package/dist/chunk-FT7SGLXT.js +193 -0
  236. package/dist/chunk-FUUVZZUB.js +62 -0
  237. package/dist/chunk-FYMRVUNZ.js +1697 -0
  238. package/dist/chunk-FYTAA5FG.js +162 -0
  239. package/dist/chunk-G3VB2UCZ.js +3892 -0
  240. package/dist/chunk-G6OIMVXH.js +405 -0
  241. package/dist/chunk-G7PQCXVH.js +228 -0
  242. package/dist/chunk-GAB6UW52.js +1087 -0
  243. package/dist/chunk-GAFJEYLK.js +29 -0
  244. package/dist/chunk-GC4CRMTS.js +642 -0
  245. package/dist/chunk-GFHSIWHL.js +5297 -0
  246. package/dist/chunk-GI5M67YA.js +735 -0
  247. package/dist/chunk-GIJWHUWF.js +191 -0
  248. package/dist/chunk-GO6DRECI.js +1 -0
  249. package/dist/chunk-GOFVBTMN.js +314 -0
  250. package/dist/chunk-GQIP72ZJ.js +13 -0
  251. package/dist/chunk-GQXLXRFA.js +28 -0
  252. package/dist/chunk-GT6HFKJB.js +48 -0
  253. package/dist/chunk-GVJGSSKM.js +174 -0
  254. package/dist/chunk-GWHGTG37.js +36 -0
  255. package/dist/chunk-GZAJHLKO.js +18 -0
  256. package/dist/chunk-GZK62I3N.js +314 -0
  257. package/dist/chunk-H3HGMISG.js +483 -0
  258. package/dist/chunk-H3ISZJMJ.js +403 -0
  259. package/dist/chunk-H4HXVWEC.js +9 -0
  260. package/dist/chunk-H6MJGKAW.js +120 -0
  261. package/dist/chunk-HBTTGVR5.js +353 -0
  262. package/dist/chunk-HBZ322WZ.js +35 -0
  263. package/dist/chunk-HEP3ILP4.js +34 -0
  264. package/dist/chunk-HGFXODKO.js +106 -0
  265. package/dist/chunk-HHWDTB6S.js +59 -0
  266. package/dist/chunk-HKO45NLZ.js +286 -0
  267. package/dist/chunk-HLVTEA6A.js +89 -0
  268. package/dist/chunk-HS3SY6HN.js +160 -0
  269. package/dist/chunk-HT5OCGKN.js +899 -0
  270. package/dist/chunk-HXIIEMGF.js +78 -0
  271. package/dist/chunk-I56TH7VM.js +46 -0
  272. package/dist/chunk-I6FN3R5D.js +352 -0
  273. package/dist/chunk-I7SCBHJN.js +655 -0
  274. package/dist/chunk-IJM4GKAT.js +529 -0
  275. package/dist/chunk-IMEYU36R.js +344 -0
  276. package/dist/chunk-INJ3SS6A.js +936 -0
  277. package/dist/chunk-IUKOPCCE.js +441 -0
  278. package/dist/chunk-IVEPZQJO.js +33 -0
  279. package/dist/chunk-IVZEONFC.js +43 -0
  280. package/dist/chunk-IWBHRSG7.js +936 -0
  281. package/dist/chunk-IYY3HQAD.js +484 -0
  282. package/dist/chunk-J2ICMYDA.js +48 -0
  283. package/dist/chunk-J2QCFLUN.js +505 -0
  284. package/dist/chunk-J5HM6V5P.js +1039 -0
  285. package/dist/chunk-J6NZX6QH.js +398 -0
  286. package/dist/chunk-JBBB2Y47.js +432 -0
  287. package/dist/chunk-JDCZQZU7.js +42 -0
  288. package/dist/chunk-JEDNXGSS.js +169 -0
  289. package/dist/chunk-JHP7OT6K.js +89 -0
  290. package/dist/chunk-JLS65NUQ.js +14 -0
  291. package/dist/chunk-JNMWYMC6.js +38 -0
  292. package/dist/chunk-JNSTWNNW.js +25 -0
  293. package/dist/chunk-JOSK6GEU.js +9564 -0
  294. package/dist/chunk-JQUMSIGC.js +100 -0
  295. package/dist/chunk-JVHGMURQ.js +1106 -0
  296. package/dist/chunk-JYXS47FQ.js +951 -0
  297. package/dist/chunk-K2ZMW2Z5.js +2147 -0
  298. package/dist/chunk-K7FAFORM.js +14 -0
  299. package/dist/chunk-KBJXX3ON.js +333 -0
  300. package/dist/chunk-KHFKVQWY.js +486 -0
  301. package/dist/chunk-KHMEI3DQ.js +173 -0
  302. package/dist/chunk-KHW3UD7B.js +69 -0
  303. package/dist/chunk-KKQKWAMF.js +365 -0
  304. package/dist/chunk-KOKXJV2U.js +455 -0
  305. package/dist/chunk-KQ4NGOJI.js +14 -0
  306. package/dist/chunk-KQYQOXMS.js +342 -0
  307. package/dist/chunk-KTMW65UX.js +14 -0
  308. package/dist/chunk-KUEWV2JH.js +593 -0
  309. package/dist/chunk-KXYDV3HP.js +1523 -0
  310. package/dist/chunk-KZ4JMGZ5.js +96 -0
  311. package/dist/chunk-KZWGBUIO.js +2659 -0
  312. package/dist/chunk-L2CBG5IX.js +1892 -0
  313. package/dist/chunk-L2S235CU.js +314 -0
  314. package/dist/chunk-L32WD57F.js +98 -0
  315. package/dist/chunk-LDJCKAJP.js +123 -0
  316. package/dist/chunk-LDWNBEN7.js +541 -0
  317. package/dist/chunk-LJVH57G2.js +241 -0
  318. package/dist/chunk-LPQ7NZEN.js +209 -0
  319. package/dist/chunk-LRGH3CCV.js +148 -0
  320. package/dist/chunk-LULOGMMM.js +1133 -0
  321. package/dist/chunk-LWGKN2GY.js +8284 -0
  322. package/dist/chunk-LX4F24ZH.js +157 -0
  323. package/dist/chunk-M4BUEXXF.js +839 -0
  324. package/dist/chunk-M6UHOC4I.js +553 -0
  325. package/dist/chunk-MCBNWZNO.js +657 -0
  326. package/dist/chunk-MEMSY6VY.js +223 -0
  327. package/dist/chunk-MHLEZQQI.js +145 -0
  328. package/dist/chunk-MJ6WNSIX.js +192 -0
  329. package/dist/chunk-MKASIKWZ.js +442 -0
  330. package/dist/chunk-MNOVBYFU.js +60 -0
  331. package/dist/chunk-MO2BYAAF.js +2896 -0
  332. package/dist/chunk-MP6P7Y2A.js +180 -0
  333. package/dist/chunk-MQ4TFJAG.js +3365 -0
  334. package/dist/chunk-MT2WAIR3.js +131 -0
  335. package/dist/chunk-MTU7VTNN.js +1122 -0
  336. package/dist/chunk-MULMXX4V.js +150 -0
  337. package/dist/chunk-MW2DWWVA.js +147 -0
  338. package/dist/chunk-MY3ZPUN4.js +151 -0
  339. package/dist/chunk-MYLH2S4G.js +64 -0
  340. package/dist/chunk-MZ5WYKNA.js +28 -0
  341. package/dist/chunk-N3Q2JNPV.js +249 -0
  342. package/dist/chunk-N5DX4JAK.js +12 -0
  343. package/dist/chunk-NAINGKHF.js +35 -0
  344. package/dist/chunk-NEQUQHOX.js +223 -0
  345. package/dist/chunk-NHNPEZS4.js +1408 -0
  346. package/dist/chunk-NHTIXJCR.js +17 -0
  347. package/dist/chunk-NKLPMBNV.js +1154 -0
  348. package/dist/chunk-NMVZ5YES.js +199 -0
  349. package/dist/chunk-NRFRY3GE.js +256 -0
  350. package/dist/chunk-NTD6CSI6.js +1266 -0
  351. package/dist/chunk-NUO7B3SH.js +318 -0
  352. package/dist/chunk-NVOHIJKG.js +1319 -0
  353. package/dist/chunk-NXH3RX7O.js +523 -0
  354. package/dist/chunk-NXZ3DURF.js +257 -0
  355. package/dist/chunk-O26L6F7F.js +209 -0
  356. package/dist/chunk-O5RRIUEP.js +124 -0
  357. package/dist/chunk-O5X6BKAO.js +10 -0
  358. package/dist/chunk-O6ZQUVTR.js +31 -0
  359. package/dist/chunk-O7FE75QZ.js +306 -0
  360. package/dist/chunk-OC7Z6AJQ.js +81 -0
  361. package/dist/chunk-OH7IEZ63.js +307 -0
  362. package/dist/chunk-OI734GRS.js +115 -0
  363. package/dist/chunk-OJBH62DK.js +684 -0
  364. package/dist/chunk-OKJLVSTJ.js +247 -0
  365. package/dist/chunk-OMX5VL43.js +79 -0
  366. package/dist/chunk-ONHAMBN6.js +12 -0
  367. package/dist/chunk-ONYPLIVE.js +753 -0
  368. package/dist/chunk-OSK7QWPD.js +637 -0
  369. package/dist/chunk-OTXLMJKP.js +438 -0
  370. package/dist/chunk-OV5PKQMA.js +213 -0
  371. package/dist/chunk-OXK5HA4B.js +31 -0
  372. package/dist/chunk-OYDWFQA4.js +272 -0
  373. package/dist/chunk-OZAUKWHB.js +22 -0
  374. package/dist/chunk-P5P2EKGH.js +94 -0
  375. package/dist/chunk-P6SSWAKY.js +29 -0
  376. package/dist/chunk-P7CQGPLS.js +30 -0
  377. package/dist/chunk-P7RVCHQV.js +197 -0
  378. package/dist/chunk-PEBWDYIW.js +726 -0
  379. package/dist/chunk-PFCXFO7A.js +9193 -0
  380. package/dist/chunk-PFLDWMX6.js +168 -0
  381. package/dist/chunk-PHZGLQSK.js +15883 -0
  382. package/dist/chunk-PNFTPNSR.js +20 -0
  383. package/dist/chunk-PPHXX4W7.js +175 -0
  384. package/dist/chunk-PPZESPJT.js +262 -0
  385. package/dist/chunk-PRDHI2L5.js +296 -0
  386. package/dist/chunk-PSTKWHHN.js +32 -0
  387. package/dist/chunk-PV53ODOT.js +555 -0
  388. package/dist/chunk-PVJBTI7P.js +24 -0
  389. package/dist/chunk-PXKERBKX.js +100 -0
  390. package/dist/chunk-PZ5AY32C.js +10 -0
  391. package/dist/chunk-Q3DHEZA5.js +85 -0
  392. package/dist/chunk-Q5RZDVQK.js +204 -0
  393. package/dist/chunk-Q64IR2Z2.js +4933 -0
  394. package/dist/chunk-Q6Z5UGPL.js +398 -0
  395. package/dist/chunk-QAMAV4DT.js +627 -0
  396. package/dist/chunk-QBWF4444.js +112 -0
  397. package/dist/chunk-QDKYT4CI.js +22805 -0
  398. package/dist/chunk-QFLBRWCJ.js +126 -0
  399. package/dist/chunk-QILAQACE.js +107 -0
  400. package/dist/chunk-QJ6OEH4K.js +3683 -0
  401. package/dist/chunk-QK3NLYTV.js +320 -0
  402. package/dist/chunk-QOFLUS24.js +535 -0
  403. package/dist/chunk-QOIASNEJ.js +98 -0
  404. package/dist/chunk-QP4UTSOV.js +3587 -0
  405. package/dist/chunk-QP5ST7RI.js +904 -0
  406. package/dist/chunk-QS5AGRZN.js +167 -0
  407. package/dist/chunk-QTAOU53N.js +107 -0
  408. package/dist/chunk-QTCLJHLZ.js +351 -0
  409. package/dist/chunk-QTOLTG4L.js +119 -0
  410. package/dist/chunk-QWDV4OSL.js +142 -0
  411. package/dist/chunk-QWMQ74KD.js +508 -0
  412. package/dist/chunk-QX6LOMMH.js +158 -0
  413. package/dist/chunk-QXBKIHY7.js +238 -0
  414. package/dist/chunk-QXBKTJT6.js +270 -0
  415. package/dist/chunk-QYQOVOLR.js +196 -0
  416. package/dist/chunk-R2CKS6WT.js +73 -0
  417. package/dist/chunk-R2TIYG7U.js +68 -0
  418. package/dist/chunk-R2W3LESH.js +465 -0
  419. package/dist/chunk-R5EXHMUN.js +100 -0
  420. package/dist/chunk-RBBTBCHX.js +289 -0
  421. package/dist/chunk-RBKLOUXA.js +121 -0
  422. package/dist/chunk-RCP2Y6DQ.js +86 -0
  423. package/dist/chunk-RCYEJSD5.js +260 -0
  424. package/dist/chunk-RFDVSI4N.js +134 -0
  425. package/dist/chunk-RGJU63FV.js +1717 -0
  426. package/dist/chunk-RI4EX2QE.js +18 -0
  427. package/dist/chunk-RIMARPCF.js +152 -0
  428. package/dist/chunk-RJ6RSNT6.js +42 -0
  429. package/dist/chunk-RNI2GUIF.js +131 -0
  430. package/dist/chunk-RPHUWJ3K.js +264 -0
  431. package/dist/chunk-RPIMAMV7.js +361 -0
  432. package/dist/chunk-RQFX6YRN.js +49 -0
  433. package/dist/chunk-RRC5BL7B.js +285 -0
  434. package/dist/chunk-RSYTSBOO.js +79 -0
  435. package/dist/chunk-RWQQIKIC.js +35 -0
  436. package/dist/chunk-RZUBOZH2.js +373 -0
  437. package/dist/chunk-S2E632FQ.js +380 -0
  438. package/dist/chunk-S2VQCZO4.js +22 -0
  439. package/dist/chunk-S2XIMQJQ.js +681 -0
  440. package/dist/chunk-S47ISY27.js +183 -0
  441. package/dist/chunk-S6I4KISU.js +142 -0
  442. package/dist/chunk-S6TAFQI5.js +2403 -0
  443. package/dist/chunk-SBBNT5ZJ.js +27 -0
  444. package/dist/chunk-SDJV2DTQ.js +491 -0
  445. package/dist/chunk-SDPIFNLD.js +33 -0
  446. package/dist/chunk-SF6YK3JI.js +835 -0
  447. package/dist/chunk-SK3ZFONO.js +976 -0
  448. package/dist/chunk-SKTFRFQ7.js +1775 -0
  449. package/dist/chunk-SNGM452K.js +2002 -0
  450. package/dist/chunk-STW7OFOA.js +581 -0
  451. package/dist/chunk-SU6UFUQY.js +1351 -0
  452. package/dist/chunk-SVJM3LJA.js +27 -0
  453. package/dist/chunk-SVUNZOPG.js +129 -0
  454. package/dist/chunk-T5XLUYV3.js +34 -0
  455. package/dist/chunk-T7E5JS6N.js +57 -0
  456. package/dist/chunk-TA3N5MMO.js +450 -0
  457. package/dist/chunk-TAJPCXSB.js +13 -0
  458. package/dist/chunk-TDEMDRN2.js +1639 -0
  459. package/dist/chunk-TDSEX5CF.js +24 -0
  460. package/dist/chunk-TGCVL74Y.js +28 -0
  461. package/dist/chunk-TGXXYDL4.js +1012 -0
  462. package/dist/chunk-TIKTYUJO.js +296 -0
  463. package/dist/chunk-TIZAJD4M.js +6029 -0
  464. package/dist/chunk-TLSEZZCG.js +5549 -0
  465. package/dist/chunk-TMBDRHAX.js +19 -0
  466. package/dist/chunk-TRURQUBP.js +1217 -0
  467. package/dist/chunk-TSQ256N5.js +152 -0
  468. package/dist/chunk-TUQMMFEQ.js +2244 -0
  469. package/dist/chunk-U424MJLC.js +81 -0
  470. package/dist/chunk-U4DRS6ZW.js +11 -0
  471. package/dist/chunk-U5KS22IN.js +204 -0
  472. package/dist/chunk-UAPHFJPN.js +673 -0
  473. package/dist/chunk-UB7LQITD.js +1040 -0
  474. package/dist/chunk-UBVRHGAZ.js +237 -0
  475. package/dist/chunk-UDNNAQN5.js +98 -0
  476. package/dist/chunk-UJP3XM54.js +342 -0
  477. package/dist/chunk-UJYDH2MW.js +305 -0
  478. package/dist/chunk-UKMPASIC.js +399 -0
  479. package/dist/chunk-UKUL3EHK.js +763 -0
  480. package/dist/chunk-UPHU64NB.js +1327 -0
  481. package/dist/chunk-URMSZXAD.js +43 -0
  482. package/dist/chunk-US3RT6CY.js +525 -0
  483. package/dist/chunk-UTDEOPKV.js +30 -0
  484. package/dist/chunk-UUUDKV7N.js +13 -0
  485. package/dist/chunk-UVKWU3SM.js +39 -0
  486. package/dist/chunk-V2TVPO3X.js +521 -0
  487. package/dist/chunk-V3KLVK7B.js +398 -0
  488. package/dist/chunk-V3YFFHDR.js +17 -0
  489. package/dist/chunk-VAG6GUBR.js +1162 -0
  490. package/dist/chunk-VBENAHDA.js +1814 -0
  491. package/dist/chunk-VBNCKCCI.js +16 -0
  492. package/dist/chunk-VCGSWN5M.js +1300 -0
  493. package/dist/chunk-VE2O2I7M.js +115 -0
  494. package/dist/chunk-VIFHYTI5.js +31 -0
  495. package/dist/chunk-VJER3FUS.js +5080 -0
  496. package/dist/chunk-VLMDM56Q.js +268 -0
  497. package/dist/chunk-VMFZEVVX.js +99 -0
  498. package/dist/chunk-VNDPQVUZ.js +1853 -0
  499. package/dist/chunk-VSPJRLGA.js +4783 -0
  500. package/dist/chunk-VSWC5ZXI.js +103 -0
  501. package/dist/chunk-VYI4DSZP.js +2017 -0
  502. package/dist/chunk-W4BAIQ6L.js +1212 -0
  503. package/dist/chunk-W7VQAL2K.js +2203 -0
  504. package/dist/chunk-WEI3ZDZH.js +33 -0
  505. package/dist/chunk-WEIUS5NT.js +26 -0
  506. package/dist/chunk-WEW7UFWE.js +546 -0
  507. package/dist/chunk-WGQAXLNA.js +384 -0
  508. package/dist/chunk-WJAL4JL3.js +206 -0
  509. package/dist/chunk-WKFOJ5ZG.js +141 -0
  510. package/dist/chunk-WMWQEFLN.js +2177 -0
  511. package/dist/chunk-WNECKKO2.js +154 -0
  512. package/dist/chunk-WPKLYOAM.js +195 -0
  513. package/dist/chunk-WSH37YQN.js +243 -0
  514. package/dist/chunk-WV3KQDPE.js +84 -0
  515. package/dist/chunk-WZFC4V4U.js +389 -0
  516. package/dist/chunk-XCOIBNET.js +13 -0
  517. package/dist/chunk-XDM65LBX.js +53 -0
  518. package/dist/chunk-XGU6PBO5.js +645 -0
  519. package/dist/chunk-XHOI4V62.js +43 -0
  520. package/dist/chunk-XIBVFMLQ.js +6858 -0
  521. package/dist/chunk-XJ2E7YEJ.js +117 -0
  522. package/dist/chunk-XON4ZCYX.js +69 -0
  523. package/dist/chunk-XQPEWJPI.js +127 -0
  524. package/dist/chunk-XSJTIFUY.js +31 -0
  525. package/dist/chunk-XT7OVWM3.js +64 -0
  526. package/dist/chunk-XTXYIISX.js +1104 -0
  527. package/dist/chunk-XV7NZL4B.js +57 -0
  528. package/dist/chunk-XWIZ6B5S.js +109 -0
  529. package/dist/chunk-XWLIKOBX.js +240 -0
  530. package/dist/chunk-XZOXEIN4.js +156 -0
  531. package/dist/chunk-XZRS7I7W.js +3673 -0
  532. package/dist/chunk-Y4ZDDHB6.js +44 -0
  533. package/dist/chunk-Y733RB7T.js +194 -0
  534. package/dist/chunk-YBHUB4XR.js +14931 -0
  535. package/dist/chunk-YKC75XK4.js +147 -0
  536. package/dist/chunk-YKMXFVK7.js +27 -0
  537. package/dist/chunk-YO5BAJ55.js +225 -0
  538. package/dist/chunk-YPDCLXNZ.js +568 -0
  539. package/dist/chunk-YSQPGVFK.js +116 -0
  540. package/dist/chunk-YTVVTIWJ.js +716 -0
  541. package/dist/chunk-YVCMDVBB.js +698 -0
  542. package/dist/chunk-YYTOA6DP.js +114 -0
  543. package/dist/chunk-Z5WGNULV.js +88 -0
  544. package/dist/chunk-Z6NGVVXG.js +66 -0
  545. package/dist/chunk-Z7CDQNH3.js +20 -0
  546. package/dist/chunk-ZH4G3FU5.js +187 -0
  547. package/dist/chunk-ZJB6SQPA.js +257 -0
  548. package/dist/chunk-ZL6E4HMY.js +42 -0
  549. package/dist/chunk-ZMLOC3DW.js +62 -0
  550. package/dist/chunk-ZORW64QH.js +101 -0
  551. package/dist/chunk-ZVVD2XGV.js +1369 -0
  552. package/dist/cli-utils-ER33ODC5.js +31 -0
  553. package/dist/cli-validation-6CHPTANV.js +146 -0
  554. package/dist/commit-utils-3STAY7C7.js +49 -0
  555. package/dist/compatibility-notice-IHNXR2JR.js +21 -0
  556. package/dist/compete-config-resolver-V3GDNJMM.js +125 -0
  557. package/dist/compete-results-XUM3PJHS.js +318 -0
  558. package/dist/competition-outcome-tracker-NWSMVUPG.js +62 -0
  559. package/dist/config-4HHE4OIK.js +238 -0
  560. package/dist/config-D6YAZASH.js +10 -0
  561. package/dist/config-MIPRDYYV.js +46 -0
  562. package/dist/config-audit-GA5DCUAI.js +371 -0
  563. package/dist/config-explain-4LMO556H.js +71 -0
  564. package/dist/config-loader-RGP634PC.js +51 -0
  565. package/dist/config-manager-J2UTO635.js +114 -0
  566. package/dist/config-merger-TVUJBZTS.js +14 -0
  567. package/dist/config-validator-H4ZVIE56.js +122 -0
  568. package/dist/cost-HSIV5U2Y.js +85 -0
  569. package/dist/data-migration-state-OAUL2WPQ.js +35 -0
  570. package/dist/db-migrator-NUOW5NKR.js +34 -0
  571. package/dist/default-paths-UILB62BN.js +49 -0
  572. package/dist/discovery-V6GYUJUC.js +53 -0
  573. package/dist/doctor-BBSMCDWE.js +271 -0
  574. package/dist/doctor-context-blocks-LPY75VZA.js +231 -0
  575. package/dist/doctor-instructions-import-JXHDDOE5.js +70 -0
  576. package/dist/doctor-purpose-6YJOXPBE.js +30 -0
  577. package/dist/domain-analyzer-S35ABUNA.js +48 -0
  578. package/dist/ensure-initialized-Q6PEU55O.js +78 -0
  579. package/dist/entry.js +922 -0
  580. package/dist/error-parser-core-MZFMFANN.js +70 -0
  581. package/dist/evaluation-context-GQBYDAEV.js +63 -0
  582. package/dist/evidence-plan-resolver-5FUAP3SA.js +111 -0
  583. package/dist/execution-data-writer-YCG5UH37.js +72 -0
  584. package/dist/execution-policy-applier-PWZM5LNA.js +63 -0
  585. package/dist/execution-preflight-DASJWWQF.js +223 -0
  586. package/dist/execution-roles-KNSNNB2D.js +112 -0
  587. package/dist/exit-code-error-QIY5DOKF.js +18 -0
  588. package/dist/external-temp-cleanup-CAW7LQ6R.js +360 -0
  589. package/dist/factory-YCYCB7AE.js +209 -0
  590. package/dist/file-utils-ZQYNBQBW.js +45 -0
  591. package/dist/flywheel-allowlist-revert-H22HA6JE.js +21 -0
  592. package/dist/flywheel-autoloop-executor-PDOASJEZ.js +316 -0
  593. package/dist/flywheel-autoloop-unverified-TOKYFCDC.js +36 -0
  594. package/dist/flywheel-competition-executor-YJUMMEJO.js +320 -0
  595. package/dist/flywheel-executor-JWU43KAY.js +437 -0
  596. package/dist/flywheel-git-isolation-FQ2TRWMB.js +56 -0
  597. package/dist/flywheel-manifest-HDPNMWWF.js +114 -0
  598. package/dist/flywheel-model-defaults-UHAKTZT3.js +110 -0
  599. package/dist/flywheel-preflight-III2C7TH.js +163 -0
  600. package/dist/flywheel-resume-2O45MS7Y.js +31 -0
  601. package/dist/flywheel-resume-execution-lease-P2BK257J.js +59 -0
  602. package/dist/flywheel-safety-AVAVFXIJ.js +19 -0
  603. package/dist/flywheel-scope-decision-ALJUUNGG.js +14 -0
  604. package/dist/get-telemetry-logger-MPDVO2JA.js +155 -0
  605. package/dist/git-message-style-KCXGMTDT.js +104 -0
  606. package/dist/guidance-profile-config-G3K5YQOO.js +113 -0
  607. package/dist/guidance-profile-renderer-TX4TNDFL.js +23 -0
  608. package/dist/identity-XLQJ4YJF.js +25 -0
  609. package/dist/index-query-6NUY4FKX.js +37 -0
  610. package/dist/index.js +85588 -0
  611. package/dist/indexer-DL54CM3K.js +44 -0
  612. package/dist/init-OUZ72344.js +245 -0
  613. package/dist/instructions-import-offer-NSYXSITC.js +363 -0
  614. package/dist/instructions-run-NBII7W36.js +114 -0
  615. package/dist/interrupted-work-E4TLKJLE.js +43 -0
  616. package/dist/invoke-provider-auth-NEWEIHCI.js +38 -0
  617. package/dist/judge-scoring.json +64 -0
  618. package/dist/lab-mode-YXQ2Y3NG.js +44 -0
  619. package/dist/lab-utils-5QAMDJR5.js +29 -0
  620. package/dist/linux-host-class-YDWQX65O.js +31 -0
  621. package/dist/llm-judge-executor-OJSNCTFJ.js +179 -0
  622. package/dist/local-ci-config-VZ4WWDT5.js +48 -0
  623. package/dist/local-executor-DCMDDRX2.js +280 -0
  624. package/dist/logger-5KNKS3O4.js +154 -0
  625. package/dist/matrix-prompt-builder-4ZBOGZSB.js +527 -0
  626. package/dist/matrix-result-parser-EZJM4YXP.js +14 -0
  627. package/dist/merge-batch-RTEEMVDB.js +313 -0
  628. package/dist/merge-batch-members-VIWGVRHP.js +24 -0
  629. package/dist/merge-queue-TIIUSOY6.js +126 -0
  630. package/dist/merge-queue-trust-DUNZ4J65.js +21 -0
  631. package/dist/metadata-UKSMWM6J.js +29 -0
  632. package/dist/model-catalog-readiness-R5KJBP5M.js +202 -0
  633. package/dist/model-invocations-db-XTUPPQ34.js +70 -0
  634. package/dist/model-resolver-JFZ2MHOJ.js +39 -0
  635. package/dist/model-resolver-SCQ57KAQ.js +217 -0
  636. package/dist/model-roles-D3OR4NAH.js +110 -0
  637. package/dist/monitor-F4SIU5G2.js +96 -0
  638. package/dist/nested-worktrees-2WUWQ75N.js +23 -0
  639. package/dist/next-S3IRLVER.js +96 -0
  640. package/dist/next-UKPFRXOO.js +148 -0
  641. package/dist/opportunistic-TL4S26VX.js +86 -0
  642. package/dist/optimization-config-resolver-NAEIYU6A.js +148 -0
  643. package/dist/package-HJR4QJT4.js +10 -0
  644. package/dist/parse-git-status-7PIYFPS2.js +21 -0
  645. package/dist/path-security-5WJSY56G.js +41 -0
  646. package/dist/paths-JUI7C4KU.js +45 -0
  647. package/dist/plan-backlog-list-fast-SSTQL5XP.js +89 -0
  648. package/dist/plan-backlog-show-fast-PFRAQ3JB.js +114 -0
  649. package/dist/plan-sprint-list-fast-HD2KBZSY.js +110 -0
  650. package/dist/plan-sprint-status-fast-YFYH7WQF.js +116 -0
  651. package/dist/plan-task-status-fast-JU5PNLUU.js +114 -0
  652. package/dist/plan-to-flywheel-PY5MSY2H.js +529 -0
  653. package/dist/preflight-PJHGTJNZ.js +12 -0
  654. package/dist/process-registry-B4ZUCN7T.js +28 -0
  655. package/dist/process-scanner-TU3QHWVB.js +23 -0
  656. package/dist/processes-6FHVGT72.js +28 -0
  657. package/dist/processes-json-fast-YWTMUWXB.js +72 -0
  658. package/dist/profile-HWTC56SG.js +209 -0
  659. package/dist/project-runtime-paths-2UQNG5Y4.js +41 -0
  660. package/dist/protected-branches-N36LDBFY.js +71 -0
  661. package/dist/provider-aware-resource-manager-YTFAHNGJ.js +40 -0
  662. package/dist/provider-matrix-2KO2V5OM.js +214 -0
  663. package/dist/provider-matrix-renderer-MEC3PER7.js +139 -0
  664. package/dist/provider-output-forwarding-2AHL7YMA.js +26 -0
  665. package/dist/provider-registry-WCZ65QHC.js +136 -0
  666. package/dist/provider-temp-cleanup-IUNPJCZF.js +14 -0
  667. package/dist/prune-engine-EOQVWEP6.js +448 -0
  668. package/dist/publication-turn-S42RZTQF.js +44 -0
  669. package/dist/quality-gate-JQMVCKOO.js +72 -0
  670. package/dist/quickstart-3O6FRCFB.js +105 -0
  671. package/dist/readiness-GW6ABRHB.js +139 -0
  672. package/dist/readiness-KFZCXDTT.js +215 -0
  673. package/dist/registry-7XG376SY.js +217 -0
  674. package/dist/repair-helpers-TTV5FEF4.js +117 -0
  675. package/dist/repo-root-YG6MQZDW.js +28 -0
  676. package/dist/resolution-engine-FFEK7HQ2.js +187 -0
  677. package/dist/resolve-provider-cli-ZEXVZKET.js +44 -0
  678. package/dist/restore-telemetry-db-E7BR5PO7.js +459 -0
  679. package/dist/routing-events-AW6SRZ6G.js +13 -0
  680. package/dist/routing-history-store-FMDDQL6W.js +39 -0
  681. package/dist/routing-planner-C6OHO3EY.js +221 -0
  682. package/dist/runner-KHDNQ5KU.js +1194 -0
  683. package/dist/runner-PIS2BW7Y.js +1801 -0
  684. package/dist/safety-E3JJVSFL.js +67 -0
  685. package/dist/safety-stanza-HE6GXP4G.js +49 -0
  686. package/dist/sandbox-7LBZC5PE.js +72 -0
  687. package/dist/sandbox-ASWCDYZL.js +80 -0
  688. package/dist/schema-extensions.sql +234 -0
  689. package/dist/security-JI22TBEB.js +17 -0
  690. package/dist/setup-bb-plugin-FQXLA76Y.js +531 -0
  691. package/dist/setup-claude-code-NF4ZSHVE.js +197 -0
  692. package/dist/setup-temp-cleanup-2APZ4HMA.js +29 -0
  693. package/dist/shared-utils-34HJZ6JX.js +40 -0
  694. package/dist/simple-artifact-goal-MQLDDMMX.js +29 -0
  695. package/dist/sprint-N7JFTEGL.js +403 -0
  696. package/dist/sprint-bridge-KJXIBNRJ.js +117 -0
  697. package/dist/sprint-completion-dependents-34SR7JVL.js +43 -0
  698. package/dist/sprint-execution-service-A6JPUBWY.js +376 -0
  699. package/dist/sprint-manager-MU2ZUWN2.js +140 -0
  700. package/dist/sqlite-timeout-config-U2CT4LKU.js +13 -0
  701. package/dist/sqlite-wrapper-FSGJ2QEW.js +19 -0
  702. package/dist/stale-cleanup-orchestrator-4X6324VF.js +71 -0
  703. package/dist/storage-access-I3V5LER7.js +31 -0
  704. package/dist/store-XVG64ZR2.js +25 -0
  705. package/dist/task-classifier-MN2DYE5R.js +558 -0
  706. package/dist/task-list-fast-7AT6LTTA.js +112 -0
  707. package/dist/task-manager-LHPBBRRE.js +136 -0
  708. package/dist/task-report-OLPAWBTK.js +64 -0
  709. package/dist/task-type-detector-6RG3YNZF.js +27 -0
  710. package/dist/task-type-resolver-EQHOUIHT.js +64 -0
  711. package/dist/telemetry-5VYSK772.js +212 -0
  712. package/dist/telemetry-health-tracker-4GFW2EMP.js +12 -0
  713. package/dist/telemetry-path-resolver-H3NFHHQY.js +34 -0
  714. package/dist/telemetry-reliability-URJDTNPZ.js +76 -0
  715. package/dist/transports-NREZQTJK.js +106 -0
  716. package/dist/unified-cost-tracker-5MTBJ2CL.js +59 -0
  717. package/dist/unified-state-cleanup-GGAXLHXF.js +292 -0
  718. package/dist/universal-cost-calculator-JTFEJVWK.js +70 -0
  719. package/dist/user-agents-QLIGPLP2.js +30 -0
  720. package/dist/variant-execution-I4ZZUAWY.js +319 -0
  721. package/dist/variant-opt-parser-JHCXKY2K.js +251 -0
  722. package/dist/variant-request-input-PL37N3AN.js +11 -0
  723. package/dist/variant-state-cleanup-KHMXRBZN.js +287 -0
  724. package/dist/variant-worker-COB5YTJX.js +4517 -0
  725. package/dist/variant-worker.js +26 -0
  726. package/dist/variants-XGDSXYG3.js +267 -0
  727. package/dist/verification-command-YSPU4VBH.js +66 -0
  728. package/dist/verify-command-timeout-DQUYH33Z.js +11 -0
  729. package/dist/verify-commands-LOHMOFKC.js +65 -0
  730. package/dist/warning-JSKSFKBT.js +13 -0
  731. package/dist/work-edge-K65NHVWY.js +50 -0
  732. package/dist/work-item-writer-KY5ZWZWW.js +30 -0
  733. package/dist/worker-JNXXEIT6.js +1504 -0
  734. package/dist/worker.js +21 -0
  735. package/dist/workspace-tree-identity-JO5PSKTW.js +52 -0
  736. package/dist/worktree-environment-WTVABAQW.js +64 -0
  737. package/dist/worktree-metrics-VZRQI26T.js +66 -0
  738. package/dist/writeback-TQFQBJNU.js +50 -0
  739. package/docs/CLI_REFERENCE.md +8287 -0
  740. package/docs/PROVIDER_SETUP.md +1737 -0
  741. package/docs/README.md +96 -0
  742. package/docs/TROUBLESHOOTING.md +2209 -0
  743. package/docs/getting-started/QUICK_START.md +260 -0
  744. package/docs/getting-started/README.md +198 -0
  745. package/docs/getting-started/SETUP.md +25 -0
  746. package/docs/reference/AGENT_CONTEXT.md +84 -0
  747. package/docs/reference/DELIVERY_ONBOARDING.md +198 -0
  748. package/docs/reference/PROVIDER_RELEASE_TIERS.md +34 -0
  749. package/docs/reference/SECURITY.md +291 -0
  750. package/docs/reference/TELEMETRY_PRIVACY.md +259 -0
  751. package/integrations/bb-plugin-build/README.md +391 -0
  752. package/integrations/bb-plugin-build/app.css +431 -0
  753. package/integrations/bb-plugin-build/app.tsx +6210 -0
  754. package/integrations/bb-plugin-build/package.json +45 -0
  755. package/integrations/bb-plugin-build/server.ts +488 -0
  756. package/integrations/bb-plugin-build/src/adapter.ts +3672 -0
  757. package/integrations/bb-plugin-build/src/backlog-authoring-model.ts +150 -0
  758. package/integrations/bb-plugin-build/src/backlog-authoring-schema.ts +458 -0
  759. package/integrations/bb-plugin-build/src/backlog-authoring-service.ts +467 -0
  760. package/integrations/bb-plugin-build/src/backlog-authoring-view.ts +1025 -0
  761. package/integrations/bb-plugin-build/src/command-evidence-schema.ts +13 -0
  762. package/integrations/bb-plugin-build/src/competition-defaults-schema.ts +122 -0
  763. package/integrations/bb-plugin-build/src/competition-defaults-service.ts +105 -0
  764. package/integrations/bb-plugin-build/src/competition-defaults-view.ts +138 -0
  765. package/integrations/bb-plugin-build/src/contract.ts +718 -0
  766. package/integrations/bb-plugin-build/src/execution-activity-view.ts +98 -0
  767. package/integrations/bb-plugin-build/src/finalize-model.ts +205 -0
  768. package/integrations/bb-plugin-build/src/finalize-schema.ts +150 -0
  769. package/integrations/bb-plugin-build/src/finalize-service.ts +330 -0
  770. package/integrations/bb-plugin-build/src/finalize-view.ts +159 -0
  771. package/integrations/bb-plugin-build/src/judge-operations-readback.ts +303 -0
  772. package/integrations/bb-plugin-build/src/judge-operations-schema.ts +33 -0
  773. package/integrations/bb-plugin-build/src/judge-operations-view.ts +459 -0
  774. package/integrations/bb-plugin-build/src/model.ts +1318 -0
  775. package/integrations/bb-plugin-build/src/monitor-service.ts +233 -0
  776. package/integrations/bb-plugin-build/src/panel-read-ux.ts +97 -0
  777. package/integrations/bb-plugin-build/src/patch-preview-schema.ts +166 -0
  778. package/integrations/bb-plugin-build/src/patch-preview-service.ts +480 -0
  779. package/integrations/bb-plugin-build/src/patch-preview-view.ts +109 -0
  780. package/integrations/bb-plugin-build/src/planning-readback.ts +228 -0
  781. package/integrations/bb-plugin-build/src/planning-service.ts +647 -0
  782. package/integrations/bb-plugin-build/src/planning-sync-evidence.ts +37 -0
  783. package/integrations/bb-plugin-build/src/planning-workspace-readback.ts +33 -0
  784. package/integrations/bb-plugin-build/src/planning-workspace-schema.ts +131 -0
  785. package/integrations/bb-plugin-build/src/planning-workspace.ts +333 -0
  786. package/integrations/bb-plugin-build/src/poll-handoff.ts +136 -0
  787. package/integrations/bb-plugin-build/src/project-target-server.ts +21 -0
  788. package/integrations/bb-plugin-build/src/project-target.ts +45 -0
  789. package/integrations/bb-plugin-build/src/reference-index.ts +218 -0
  790. package/integrations/bb-plugin-build/src/repository-identity.ts +17 -0
  791. package/integrations/bb-plugin-build/src/repository-schema.ts +30 -0
  792. package/integrations/bb-plugin-build/src/result-explorer-view.ts +597 -0
  793. package/integrations/bb-plugin-build/src/result-readback-model.ts +163 -0
  794. package/integrations/bb-plugin-build/src/result-readback-schema.ts +319 -0
  795. package/integrations/bb-plugin-build/src/result-readback-service.ts +333 -0
  796. package/integrations/bb-plugin-build/src/run-page-view.ts +332 -0
  797. package/integrations/bb-plugin-build/src/setup-config-schema.ts +293 -0
  798. package/integrations/bb-plugin-build/src/setup-config-service.ts +620 -0
  799. package/integrations/bb-plugin-build/src/setup-config-view.ts +388 -0
  800. package/integrations/bb-plugin-build/src/sprint-close-model.ts +150 -0
  801. package/integrations/bb-plugin-build/src/sprint-close-schema.ts +128 -0
  802. package/integrations/bb-plugin-build/src/sprint-close-service.ts +293 -0
  803. package/integrations/bb-plugin-build/src/sprint-close-view.ts +116 -0
  804. package/integrations/bb-plugin-build/src/sprint-composition-model.ts +709 -0
  805. package/integrations/bb-plugin-build/src/sprint-composition-readback.ts +231 -0
  806. package/integrations/bb-plugin-build/src/sprint-composition-schema.ts +597 -0
  807. package/integrations/bb-plugin-build/src/sprint-composition-service.ts +773 -0
  808. package/integrations/bb-plugin-build/src/sprint-composition-view.ts +1221 -0
  809. package/integrations/bb-plugin-build/src/sprint-execution-schema.ts +188 -0
  810. package/integrations/bb-plugin-build/src/sprint-execution-service.ts +289 -0
  811. package/integrations/bb-plugin-build/src/sprint-execution-view.ts +54 -0
  812. package/integrations/bb-plugin-build/src/task-authoring-model.ts +563 -0
  813. package/integrations/bb-plugin-build/src/task-authoring-schema.ts +629 -0
  814. package/integrations/bb-plugin-build/src/task-authoring-service.ts +842 -0
  815. package/integrations/bb-plugin-build/src/task-authoring-view.ts +1227 -0
  816. package/integrations/bb-plugin-build/src/task-catalog-schema.ts +46 -0
  817. package/integrations/bb-plugin-build/src/task-catalog-service.ts +44 -0
  818. package/integrations/bb-plugin-build/src/task-detail-readback.ts +56 -0
  819. package/integrations/bb-plugin-build/src/task-detail-schema.ts +171 -0
  820. package/integrations/bb-plugin-build/src/task-navigation.ts +446 -0
  821. package/integrations/bb-plugin-build/src/workbench-route.ts +74 -0
  822. package/package.json +306 -4
  823. package/schemas/README.md +80 -0
  824. package/schemas/build.config.schema.json +2110 -0
  825. package/schemas/config-v1.schema.json +136 -0
  826. package/schemas/execution-config.schema.json +47 -0
  827. package/schemas/judge-scoring.schema.json +257 -0
  828. package/schemas/provider-config.schema.json +179 -0
  829. package/schemas/telemetry-config.schema.json +79 -0
  830. package/schemas/user-preferences.schema.json +141 -0
  831. package/scripts/assert-node-runtime.mjs +146 -0
  832. package/scripts/preflight-native.mjs +49 -0
  833. package/scripts/setup-git-hooks.mjs +39 -0
  834. package/scripts/sync.sh +2871 -0
  835. package/scripts/write-node-launcher.sh +138 -0
  836. package/src/resources/gemini/slash-packs/default/plan.toml +15 -0
  837. package/src/resources/gemini/slash-packs/default/summary.toml +16 -0
  838. package/src/resources/gemini/slash-packs/default/tests.toml +16 -0
  839. package/templates/.build-project/README.md +41 -0
  840. package/templates/.build-project/agents/README.md +50 -0
  841. package/templates/.build-project/agents/architect.md +10 -0
  842. package/templates/.build-project/agents/code-reviewer.md +10 -0
  843. package/templates/.build-project/agents/debugger.md +10 -0
  844. package/templates/.build-project/agents/documenter.md +10 -0
  845. package/templates/.build-project/agents/test-automator.md +10 -0
  846. package/templates/.build-project/config/build.config.jsonc +64 -0
  847. package/templates/.build-project/config/project-context.template.json +6 -0
  848. package/templates/.build-project/config/task-type-aliases.presets/kanban.yaml +14 -0
  849. package/templates/.build-project/config/task-type-aliases.presets/scrum.yaml +17 -0
  850. package/templates/.build-project/config/task-type-aliases.presets/xp.yaml +12 -0
  851. package/templates/.build-project/config/task-type-aliases.yaml +29 -0
  852. package/templates/.build-project/gitignore.template +70 -0
  853. package/templates/.build-project/task-types/analysis.yaml +38 -0
  854. package/templates/.build-project/task-types/architecture.yaml +35 -0
  855. package/templates/.build-project/task-types/doc.yaml +34 -0
  856. package/templates/.build-project/task-types/feature.yaml +22 -0
  857. package/templates/.build-project/task-types/general.yaml +4 -0
  858. package/templates/.build-project/task-types/security.yaml +38 -0
  859. package/templates/AGENTS.template.md +85 -0
  860. package/templates/CLAUDE.template.md +1 -0
  861. package/templates/GEMINI.template.md +1 -0
  862. package/templates/builtin-workflows/code-review.yaml +73 -0
  863. package/templates/builtin-workflows/skills/code-review/SKILL.md +61 -0
  864. package/templates/builtin-workflows/skills/execution-contract-analysis/SKILL.md +24 -0
  865. package/templates/builtin-workflows/skills/execution-contract-code/SKILL.md +19 -0
  866. package/templates/builtin-workflows/skills/execution-contract-structured/SKILL.md +17 -0
  867. package/templates/ci/incident-intake.mjs +787 -0
  868. package/templates/ci/incidents.yml.template +103 -0
  869. package/templates/ci/node.yml.template +17 -0
  870. package/templates/ci/python.yml.template +17 -0
  871. package/templates/guard-kit/GUARD_SETUP.md.template +258 -0
  872. package/templates/guard-kit/build-guard.yml.template +189 -0
  873. package/templates/guard-kit/check.mjs.template +932 -0
  874. package/templates/guard-kit/config.json.template +7 -0
  875. package/templates/guard-kit/policy.json.template +1 -0
  876. package/templates/profiles/README.md +57 -0
  877. package/templates/profiles/frontier-claude.json +23 -0
  878. package/templates/profiles/frontier-codex.json +23 -0
@@ -0,0 +1,4933 @@
1
+ import {
2
+ ModelMetadataResolver
3
+ } from "./chunk-YVCMDVBB.js";
4
+ import {
5
+ CURSOR_KNOWN_MODELS
6
+ } from "./chunk-7HPCIOMA.js";
7
+ import {
8
+ parseJsonWithContext
9
+ } from "./chunk-P6SSWAKY.js";
10
+ import {
11
+ logWarning
12
+ } from "./chunk-ETAD4NKD.js";
13
+ import {
14
+ sqlite_wrapper_default
15
+ } from "./chunk-XQPEWJPI.js";
16
+ import {
17
+ wrapWithSandbox
18
+ } from "./chunk-B7YRSULE.js";
19
+ import {
20
+ TelemetryPathResolver
21
+ } from "./chunk-C3KCMAJP.js";
22
+ import {
23
+ applySqlitePragmas
24
+ } from "./chunk-HEP3ILP4.js";
25
+ import {
26
+ ProviderRegistry,
27
+ refreshInventoryContext,
28
+ registerProviderRegistryReloadInvalidator,
29
+ resolveSharedRefreshPaths
30
+ } from "./chunk-QJ6OEH4K.js";
31
+ import {
32
+ listModelRouteIds
33
+ } from "./chunk-QXBKTJT6.js";
34
+ import {
35
+ createScopedProviderChildEnv
36
+ } from "./chunk-WGQAXLNA.js";
37
+ import {
38
+ getBuildProjectRoot
39
+ } from "./chunk-CHA2HDE5.js";
40
+ import {
41
+ debugLog,
42
+ emitWarning
43
+ } from "./chunk-7VRJHTGZ.js";
44
+
45
+ // src/telemetry/model-invocations-db.ts
46
+ import { randomBytes } from "crypto";
47
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
48
+ import path5 from "path";
49
+
50
+ // src/telemetry/cost-precedence.ts
51
+ var NON_METERED_COST_SQL = `(
52
+ e.computed_cost_cents IS NULL
53
+ AND (
54
+ (e.billing_mode IS NOT NULL AND e.billing_mode <> 'metered')
55
+ OR (
56
+ e.cost_snapshot IS NOT NULL
57
+ AND json_valid(e.cost_snapshot)
58
+ AND json_extract(e.cost_snapshot, '$.billingMode') IS NOT NULL
59
+ AND json_extract(e.cost_snapshot, '$.billingMode') <> 'metered'
60
+ )
61
+ )
62
+ )`;
63
+ var SNAPSHOT_FIRST_COST_CENTS_SQL = `
64
+ CASE
65
+ WHEN ${NON_METERED_COST_SQL}
66
+ THEN NULL
67
+ WHEN e.cost_snapshot IS NOT NULL AND json_valid(e.cost_snapshot) THEN
68
+ COALESCE(
69
+ -- REAL (not INTEGER): snapshot costs carry sub-cent precision; an
70
+ -- integer cast truncates small invocations (< 1 cent) to zero.
71
+ CAST(json_extract(e.cost_snapshot, '$.totalCostCents') AS REAL),
72
+ e.computed_cost_cents
73
+ )
74
+ ELSE e.computed_cost_cents
75
+ END
76
+ `;
77
+ function resolveSnapshotFirstCostCents(input) {
78
+ const billingMode = input.costSnapshot?.billingMode;
79
+ if (input.computedCostCents == null && (typeof billingMode === "string" && billingMode !== "metered" || typeof input.billingMode === "string" && input.billingMode !== "metered")) {
80
+ return null;
81
+ }
82
+ const snapshotCost = input.costSnapshot?.totalCostCents;
83
+ if (typeof snapshotCost === "number" && Number.isFinite(snapshotCost)) {
84
+ return snapshotCost;
85
+ }
86
+ const computedCost = input.computedCostCents;
87
+ return typeof computedCost === "number" && Number.isFinite(computedCost) ? computedCost : null;
88
+ }
89
+ function finiteNumberOrZero(value) {
90
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
91
+ }
92
+ function resolveSnapshotFirstCostSnapshot(input) {
93
+ const totalCostCents = resolveSnapshotFirstCostCents(input);
94
+ if (totalCostCents === null) {
95
+ return null;
96
+ }
97
+ const snapshot = input.costSnapshot;
98
+ return {
99
+ inputCostCents: finiteNumberOrZero(snapshot?.inputCostCents),
100
+ outputCostCents: finiteNumberOrZero(snapshot?.outputCostCents),
101
+ cacheCostCents: finiteNumberOrZero(snapshot?.cacheCostCents),
102
+ toolCostCents: finiteNumberOrZero(snapshot?.toolCostCents),
103
+ infrastructureCostCents: finiteNumberOrZero(snapshot?.infrastructureCostCents),
104
+ totalCostCents,
105
+ currency: typeof snapshot?.currency === "string" && snapshot.currency ? snapshot.currency : "USD"
106
+ };
107
+ }
108
+
109
+ // src/telemetry/invocation-cost-enrichment.ts
110
+ import fs3 from "fs";
111
+ import path4 from "path";
112
+
113
+ // src/core/config/model-registry.ts
114
+ import * as crypto from "crypto";
115
+ import * as fs from "fs";
116
+ import * as path from "path";
117
+ import yaml from "js-yaml";
118
+ import { z } from "zod";
119
+ var BillingModeSchema = z.enum(["metered", "flat_rate", "non_metered"]);
120
+ var CostSchemaZ = z.object({
121
+ prompt_per_1k: z.number().min(0).optional(),
122
+ completion_per_1k: z.number().min(0).optional(),
123
+ cache_read_per_1k: z.number().min(0).optional(),
124
+ cache_write_per_1k: z.number().min(0).optional(),
125
+ /** Cache writes held for the longer duration (Anthropic's 1-hour write); absent prices them at `cache_write_per_1k`. */
126
+ cache_write_1h_per_1k: z.number().min(0).optional(),
127
+ reasoning_per_1k: z.number().min(0).optional(),
128
+ tool_per_1k: z.number().min(0).optional(),
129
+ currency: z.string().min(1).optional(),
130
+ long_context_pricing: z.record(z.string(), z.unknown()).optional(),
131
+ /** Per speed-tier rate sets in the same per-1k shape as the standard rates, keyed by tier name. */
132
+ speed_tiers: z.record(z.string(), z.record(z.string(), z.unknown())).optional(),
133
+ /** Tier a model id selected (e.g. a `-fast` id) and the rate set priced; `applied` is `standard` when the tier had no rates. */
134
+ routed_speed_tier: z.object({
135
+ requested: z.string(),
136
+ applied: z.string(),
137
+ unpublishedLongContextAboveTokens: z.number().optional()
138
+ }).optional(),
139
+ extra_fees: z.record(z.string(), z.number()).optional(),
140
+ billing_mode: BillingModeSchema.optional(),
141
+ billing_source: z.string().optional(),
142
+ pricing_source: z.string().optional(),
143
+ /** How the rates were reached when not from the model's own row, e.g. `passthrough:<provider>/<model>`. */
144
+ resolution_route: z.string().optional(),
145
+ pricing_confidence: z.enum(["high", "medium", "low"]).optional(),
146
+ flat_rate_per_seat_month: z.number().min(0).optional(),
147
+ notes: z.string().optional()
148
+ }).superRefine((value, ctx) => {
149
+ const billingMode = value.billing_mode ?? "metered";
150
+ if (billingMode === "metered") {
151
+ if (typeof value.prompt_per_1k !== "number") {
152
+ ctx.addIssue({
153
+ code: z.ZodIssueCode.custom,
154
+ message: "Metered billing requires prompt_per_1k",
155
+ path: ["prompt_per_1k"]
156
+ });
157
+ }
158
+ if (typeof value.completion_per_1k !== "number") {
159
+ ctx.addIssue({
160
+ code: z.ZodIssueCode.custom,
161
+ message: "Metered billing requires completion_per_1k",
162
+ path: ["completion_per_1k"]
163
+ });
164
+ }
165
+ }
166
+ });
167
+ var ModelDefZ = z.object({
168
+ id: z.string().min(1),
169
+ display_name: z.string().min(1),
170
+ capabilities: z.array(z.string()),
171
+ costSchema: CostSchemaZ.optional()
172
+ });
173
+ var ProviderFrontmatterZ = z.object({
174
+ id: z.string().min(1),
175
+ display_name: z.string().min(1),
176
+ models: z.array(ModelDefZ).min(1, "At least one model is required"),
177
+ auth_hint: z.string().optional()
178
+ });
179
+ function computeRegistryVersion(registry) {
180
+ const sortedProviders = [...registry.providers].sort((a, b) => a.providerId.localeCompare(b.providerId)).map((provider) => ({
181
+ authHint: provider.authHint,
182
+ displayName: provider.displayName,
183
+ modelMappings: provider.modelMappings,
184
+ models: [...provider.models || []].sort((a, b) => a.modelId.localeCompare(b.modelId)).map((model) => ({
185
+ capabilities: [...model.capabilities].sort(),
186
+ costSchema: model.costSchema,
187
+ displayName: model.displayName,
188
+ modelId: model.modelId,
189
+ unpricedReason: model.unpricedReason
190
+ })),
191
+ providerId: provider.providerId
192
+ }));
193
+ const canonical = JSON.stringify(sortJsonValue(sortedProviders));
194
+ return crypto.createHash("sha256").update(canonical).digest("hex");
195
+ }
196
+ function sortJsonValue(value) {
197
+ if (Array.isArray(value)) return value.map(sortJsonValue);
198
+ if (value && typeof value === "object") {
199
+ return Object.fromEntries(
200
+ Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, child]) => [key, sortJsonValue(child)])
201
+ );
202
+ }
203
+ return value;
204
+ }
205
+ function buildRegistry(providers) {
206
+ const sorted = [...providers].sort((a, b) => a.providerId.localeCompare(b.providerId));
207
+ const registry = {
208
+ providers: sorted,
209
+ registryVersion: ""
210
+ // Placeholder, will be computed
211
+ };
212
+ const versionData = { ...registry, registryVersion: "" };
213
+ registry.registryVersion = computeRegistryVersion(versionData);
214
+ return registry;
215
+ }
216
+
217
+ // src/cost-tracking/list-price-rates.ts
218
+ var PER_1K_TO_PER_1M = 1e3;
219
+ function nonEmptyString(value) {
220
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
221
+ }
222
+ function metaRecord(row) {
223
+ return row._meta && typeof row._meta === "object" ? row._meta : {};
224
+ }
225
+ function pricingSourceFromRow(row) {
226
+ const meta = metaRecord(row);
227
+ return nonEmptyString(row.pricing_source) ?? nonEmptyString(meta.pricing_source) ?? nonEmptyString(meta.source);
228
+ }
229
+ function billingSourceFromRow(row) {
230
+ return nonEmptyString(row.billing_source) ?? nonEmptyString(metaRecord(row).billing_source);
231
+ }
232
+ function pricingConfidenceFromRow(row) {
233
+ const meta = metaRecord(row);
234
+ const value = row.pricing_confidence ?? meta.pricing_confidence ?? meta.match_confidence;
235
+ return value === "high" || value === "medium" || value === "low" ? value : void 0;
236
+ }
237
+ function longContextFromRow(value) {
238
+ if (!value) return void 0;
239
+ return {
240
+ thresholdTokens: value.threshold_tokens,
241
+ inputMultiplier: value.input_multiplier,
242
+ outputMultiplier: value.output_multiplier,
243
+ ...value.cache_read_multiplier !== void 0 ? { cacheReadMultiplier: value.cache_read_multiplier } : {},
244
+ ...value.cache_write_multiplier !== void 0 ? { cacheWriteMultiplier: value.cache_write_multiplier } : {}
245
+ };
246
+ }
247
+ function rateCardFromPricingRow(row) {
248
+ const inputPer1m = row.input_per_1m_tokens ?? (row.input_per_1k_tokens === void 0 ? void 0 : row.input_per_1k_tokens * PER_1K_TO_PER_1M);
249
+ const outputPer1m = row.output_per_1m_tokens ?? (row.output_per_1k_tokens === void 0 ? void 0 : row.output_per_1k_tokens * PER_1K_TO_PER_1M);
250
+ if (inputPer1m === void 0 || outputPer1m === void 0) return void 0;
251
+ const longContext = longContextFromRow(row.long_context_pricing);
252
+ const speedTiers = speedTiersFromRow(row);
253
+ return {
254
+ inputPer1m,
255
+ outputPer1m,
256
+ ...row.cache_read_per_1m_tokens !== void 0 ? { cacheReadPer1m: row.cache_read_per_1m_tokens } : {},
257
+ ...row.cache_write_per_1m_tokens !== void 0 ? { cacheWritePer1m: row.cache_write_per_1m_tokens } : {},
258
+ ...row.cache_write_1h_per_1m_tokens !== void 0 ? { cacheWriteLongPer1m: row.cache_write_1h_per_1m_tokens } : {},
259
+ ...longContext ? { longContext } : {},
260
+ ...speedTiers ? { speedTiers } : {}
261
+ };
262
+ }
263
+ function speedTiersFromRow(row) {
264
+ const entries = Object.entries(row.speed_tiers ?? {});
265
+ if (entries.length === 0) return void 0;
266
+ return Object.fromEntries(
267
+ entries.map(([name, tier]) => {
268
+ const longContext = longContextFromRow(tier.long_context_pricing);
269
+ return [
270
+ name,
271
+ {
272
+ inputPer1m: tier.input_per_1m_tokens,
273
+ outputPer1m: tier.output_per_1m_tokens,
274
+ ...tier.cache_read_per_1m_tokens !== void 0 ? { cacheReadPer1m: tier.cache_read_per_1m_tokens } : {},
275
+ ...tier.cache_write_per_1m_tokens !== void 0 ? { cacheWritePer1m: tier.cache_write_per_1m_tokens } : {},
276
+ ...tier.cache_write_1h_per_1m_tokens !== void 0 ? { cacheWriteLongPer1m: tier.cache_write_1h_per_1m_tokens } : {},
277
+ ...longContext ? { longContext } : {}
278
+ }
279
+ ];
280
+ })
281
+ );
282
+ }
283
+ function pricingRowAtSpeedTier(row, tierName) {
284
+ const tier = row.speed_tiers?.[tierName];
285
+ if (!tier) return row;
286
+ const { speed_tiers: _speedTiers, long_context_pricing: _longContext, ...rest } = row;
287
+ return {
288
+ ...rest,
289
+ input_per_1m_tokens: tier.input_per_1m_tokens,
290
+ output_per_1m_tokens: tier.output_per_1m_tokens,
291
+ cache_read_per_1m_tokens: tier.cache_read_per_1m_tokens,
292
+ cache_write_per_1m_tokens: tier.cache_write_per_1m_tokens,
293
+ cache_write_1h_per_1m_tokens: tier.cache_write_1h_per_1m_tokens,
294
+ input_per_1k_tokens: void 0,
295
+ output_per_1k_tokens: void 0,
296
+ ...tier.long_context_pricing ? { long_context_pricing: tier.long_context_pricing } : {}
297
+ };
298
+ }
299
+ function numberOrUndefined(value) {
300
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
301
+ }
302
+ function longContextFromSnapshot(value) {
303
+ const thresholdTokens = numberOrUndefined(value?.threshold_tokens);
304
+ if (!value || thresholdTokens === void 0) return void 0;
305
+ const cacheReadMultiplier = numberOrUndefined(value.cache_read_multiplier);
306
+ const cacheWriteMultiplier = numberOrUndefined(value.cache_write_multiplier);
307
+ return {
308
+ thresholdTokens,
309
+ inputMultiplier: numberOrUndefined(value.input_multiplier) ?? 1,
310
+ outputMultiplier: numberOrUndefined(value.output_multiplier) ?? 1,
311
+ ...cacheReadMultiplier !== void 0 ? { cacheReadMultiplier } : {},
312
+ ...cacheWriteMultiplier !== void 0 ? { cacheWriteMultiplier } : {}
313
+ };
314
+ }
315
+ function ratesFromSnapshot(schema) {
316
+ const cacheRead = numberOrUndefined(schema.cache_read_per_1k);
317
+ const cacheWrite = numberOrUndefined(schema.cache_write_per_1k);
318
+ const cacheWriteLong = numberOrUndefined(schema.cache_write_1h_per_1k);
319
+ const longContext = longContextFromSnapshot(schema.long_context_pricing);
320
+ return {
321
+ inputPer1m: (schema.prompt_per_1k ?? 0) * PER_1K_TO_PER_1M,
322
+ outputPer1m: (schema.completion_per_1k ?? 0) * PER_1K_TO_PER_1M,
323
+ ...cacheRead !== void 0 ? { cacheReadPer1m: cacheRead * PER_1K_TO_PER_1M } : {},
324
+ ...cacheWrite !== void 0 ? { cacheWritePer1m: cacheWrite * PER_1K_TO_PER_1M } : {},
325
+ ...cacheWriteLong !== void 0 ? { cacheWriteLongPer1m: cacheWriteLong * PER_1K_TO_PER_1M } : {},
326
+ ...longContext ? { longContext } : {}
327
+ };
328
+ }
329
+ function rateCardFromSnapshotCostSchema(schema) {
330
+ const tiers = Object.entries(schema.speed_tiers ?? {});
331
+ return {
332
+ ...ratesFromSnapshot(schema),
333
+ ...tiers.length > 0 ? {
334
+ speedTiers: Object.fromEntries(
335
+ tiers.map(([name, tier]) => [name, ratesFromSnapshot(tier)])
336
+ )
337
+ } : {}
338
+ };
339
+ }
340
+ function per1kFromRates(rates) {
341
+ return {
342
+ prompt_per_1k: rates.inputPer1m / PER_1K_TO_PER_1M,
343
+ completion_per_1k: rates.outputPer1m / PER_1K_TO_PER_1M,
344
+ ...rates.cacheReadPer1m !== void 0 ? { cache_read_per_1k: rates.cacheReadPer1m / PER_1K_TO_PER_1M } : {},
345
+ ...rates.cacheWritePer1m !== void 0 ? { cache_write_per_1k: rates.cacheWritePer1m / PER_1K_TO_PER_1M } : {},
346
+ ...rates.cacheWriteLongPer1m !== void 0 ? { cache_write_1h_per_1k: rates.cacheWriteLongPer1m / PER_1K_TO_PER_1M } : {}
347
+ };
348
+ }
349
+ function snapshotCostSchemaFromRow(row) {
350
+ const card = rateCardFromPricingRow(row);
351
+ if (!card) return void 0;
352
+ const speedTiers = Object.entries(row.speed_tiers ?? {}).map(([name, tier]) => [
353
+ name,
354
+ {
355
+ ...per1kFromRates({
356
+ inputPer1m: tier.input_per_1m_tokens,
357
+ outputPer1m: tier.output_per_1m_tokens,
358
+ cacheReadPer1m: tier.cache_read_per_1m_tokens,
359
+ cacheWritePer1m: tier.cache_write_per_1m_tokens,
360
+ cacheWriteLongPer1m: tier.cache_write_1h_per_1m_tokens
361
+ }),
362
+ ...tier.long_context_pricing ? { long_context_pricing: tier.long_context_pricing } : {}
363
+ }
364
+ ]);
365
+ return {
366
+ ...per1kFromRates(card),
367
+ // A row that states per-1k rates keeps them exactly instead of a round trip.
368
+ ...row.input_per_1m_tokens === void 0 ? { prompt_per_1k: row.input_per_1k_tokens } : {},
369
+ ...row.output_per_1m_tokens === void 0 ? { completion_per_1k: row.output_per_1k_tokens } : {},
370
+ ...row.long_context_pricing ? { long_context_pricing: row.long_context_pricing } : {},
371
+ ...speedTiers.length > 0 ? { speed_tiers: Object.fromEntries(speedTiers) } : {}
372
+ };
373
+ }
374
+ function rateCardFromModelPricing(pricing) {
375
+ return {
376
+ inputPer1m: pricing.inputTokens,
377
+ outputPer1m: pricing.outputTokens,
378
+ cacheReadPer1m: pricing.cacheRead,
379
+ cacheWritePer1m: pricing.cacheWrite,
380
+ cacheWriteLongPer1m: pricing.cacheWriteLong,
381
+ longContext: pricing.longContextPricing,
382
+ speedTiers: pricing.speedTiers
383
+ };
384
+ }
385
+
386
+ // src/core/providers/cursor/model-discovery.ts
387
+ import { execFileSync } from "child_process";
388
+ import { readFileSync as readFileSync2, statSync, unlinkSync } from "fs";
389
+ import * as fs2 from "fs/promises";
390
+ import { homedir } from "os";
391
+ import * as path3 from "path";
392
+
393
+ // src/core/providers/cursor/model-cache-path.ts
394
+ import path2 from "path";
395
+ function cursorModelCacheDir(options = {}) {
396
+ const projectRoot = options.projectRoot ?? getBuildProjectRoot();
397
+ return path2.join(
398
+ options.modelCacheDir ?? path2.join(resolveSharedRefreshPaths().storageRoot, "cache", "models"),
399
+ options.inventoryContext ?? refreshInventoryContext(projectRoot),
400
+ "providers"
401
+ );
402
+ }
403
+
404
+ // src/core/providers/cursor/model-discovery.ts
405
+ var DEFAULT_CACHE_TTL_MS = 60 * 60 * 1e3;
406
+ function getCacheTTL() {
407
+ const envTTL = process.env.BUILD_CURSOR_CACHE_TTL_MS;
408
+ if (envTTL) {
409
+ const parsed = Number.parseInt(envTTL, 10);
410
+ if (!Number.isNaN(parsed) && parsed > 0) {
411
+ return parsed;
412
+ }
413
+ }
414
+ return DEFAULT_CACHE_TTL_MS;
415
+ }
416
+ function getDefaultCacheDir() {
417
+ return cursorModelCacheDir();
418
+ }
419
+ function getCacheFilePath(cacheDir) {
420
+ return path3.join(cacheDir, "cursor-models.json");
421
+ }
422
+ function createCursorDiscoveryChildEnv() {
423
+ const env = createScopedProviderChildEnv("cursor");
424
+ const apiKey = getCursorApiKeyFromEnv();
425
+ if (apiKey) {
426
+ env.CURSOR_API_KEY = apiKey;
427
+ }
428
+ return env;
429
+ }
430
+ function getCursorApiKeyFromEnv() {
431
+ const candidate = process.env.CURSOR_API_KEY?.trim();
432
+ if (candidate && candidate.length > 0) return candidate;
433
+ try {
434
+ const credPath = path3.join(homedir(), ".build", "credentials", "cursor.json");
435
+ const raw = readFileSync2(credPath, "utf-8");
436
+ const creds = JSON.parse(raw);
437
+ const key = (typeof creds.apiKey === "string" ? creds.apiKey : void 0) ?? (typeof creds.api_key === "string" ? creds.api_key : void 0);
438
+ if (key && key.trim().length > 0) return key.trim();
439
+ } catch {
440
+ }
441
+ return null;
442
+ }
443
+ function inferCursorModelProvider(modelId) {
444
+ const normalized = modelId.trim().toLowerCase();
445
+ if (normalized === "auto" || normalized === "cheetah" || normalized === "grok" || normalized.startsWith("grok-") || normalized.startsWith("composer") || normalized.startsWith("cursor-grok-") || normalized.startsWith("cursor-")) {
446
+ return "cursor";
447
+ }
448
+ if (normalized.startsWith("claude") || normalized.startsWith("sonnet") || normalized.startsWith("opus") || normalized.startsWith("haiku")) {
449
+ return "claude";
450
+ }
451
+ if (normalized.startsWith("gpt") || normalized.startsWith("o1") || normalized.startsWith("o3")) {
452
+ return "codex";
453
+ }
454
+ return "unknown";
455
+ }
456
+ function normalizeApiModels(payload) {
457
+ const candidates = [];
458
+ const collect = (value) => {
459
+ if (!value) return;
460
+ if (typeof value === "string") {
461
+ const trimmed = value.trim();
462
+ if (trimmed) candidates.push(trimmed);
463
+ return;
464
+ }
465
+ if (Array.isArray(value)) {
466
+ for (const entry of value) collect(entry);
467
+ return;
468
+ }
469
+ if (typeof value === "object") {
470
+ const obj = value;
471
+ const idCandidate = typeof obj.id === "string" && obj.id || typeof obj.model === "string" && obj.model || typeof obj.slug === "string" && obj.slug || typeof obj.name === "string" && obj.name;
472
+ if (idCandidate) {
473
+ candidates.push(idCandidate.trim());
474
+ }
475
+ }
476
+ };
477
+ if (Array.isArray(payload)) {
478
+ collect(payload);
479
+ } else if (payload && typeof payload === "object") {
480
+ const obj = payload;
481
+ collect(obj.models);
482
+ collect(obj.data);
483
+ collect(obj.results);
484
+ }
485
+ const seen = /* @__PURE__ */ new Set();
486
+ const models = [];
487
+ for (const id of candidates) {
488
+ if (!id || seen.has(id)) continue;
489
+ seen.add(id);
490
+ models.push({
491
+ id,
492
+ provider: inferCursorModelProvider(id),
493
+ isDefault: id === "auto"
494
+ });
495
+ }
496
+ return models;
497
+ }
498
+ function isBareCursorModelIdLine(value) {
499
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(value)) {
500
+ return false;
501
+ }
502
+ const normalized = value.toLowerCase();
503
+ return normalized.includes("-") || normalized === "auto" || normalized === "cheetah" || normalized === "grok";
504
+ }
505
+ function parseCursorModelOutput(output) {
506
+ const trimmed = output.trim();
507
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
508
+ try {
509
+ const fromJson = normalizeApiModels(JSON.parse(trimmed));
510
+ if (fromJson.length > 0) {
511
+ return fromJson;
512
+ }
513
+ } catch {
514
+ }
515
+ }
516
+ const models = [];
517
+ const seen = /* @__PURE__ */ new Set();
518
+ const lines = output.split("\n");
519
+ let currentProvider = "unknown";
520
+ for (const line of lines) {
521
+ const plainMatch = line.match(/^([a-zA-Z0-9][a-zA-Z0-9._-]*)\s+-\s+(.+)/);
522
+ if (plainMatch) {
523
+ const modelId = (plainMatch[1] ?? "").trim();
524
+ const displayName = (plainMatch[2] ?? "").trim();
525
+ if (modelId && !seen.has(modelId)) {
526
+ seen.add(modelId);
527
+ models.push({
528
+ id: modelId,
529
+ provider: inferCursorModelProvider(modelId),
530
+ displayName: displayName.length > 0 ? displayName : void 0,
531
+ isDefault: line.includes("(default)"),
532
+ isLegacy: false
533
+ });
534
+ }
535
+ continue;
536
+ }
537
+ const bareId = line.trim();
538
+ if (isBareCursorModelIdLine(bareId) && !seen.has(bareId)) {
539
+ seen.add(bareId);
540
+ models.push({
541
+ id: bareId,
542
+ provider: inferCursorModelProvider(bareId),
543
+ isDefault: bareId === "auto",
544
+ isLegacy: false
545
+ });
546
+ continue;
547
+ }
548
+ if (line.includes("### **Claude**")) {
549
+ currentProvider = "claude";
550
+ continue;
551
+ }
552
+ if (line.includes("### **Codex**")) {
553
+ currentProvider = "codex";
554
+ continue;
555
+ }
556
+ if (line.includes("### **Cursor**")) {
557
+ currentProvider = "cursor";
558
+ continue;
559
+ }
560
+ const bulletMatch = line.match(/^[-*]\s+(.+)/);
561
+ if (bulletMatch) {
562
+ const content = bulletMatch[1];
563
+ if (!content) continue;
564
+ const isDefault = line.includes("(default)");
565
+ const isLegacy = line.toLowerCase().includes("legacy");
566
+ const backtickMatches = content.matchAll(/`([^`]+)`/g);
567
+ for (const match of backtickMatches) {
568
+ const modelId = match[1]?.trim();
569
+ if (modelId && modelId.toLowerCase() !== "legacy:" && !seen.has(modelId)) {
570
+ seen.add(modelId);
571
+ models.push({
572
+ id: modelId,
573
+ provider: currentProvider === "unknown" ? inferCursorModelProvider(modelId) : currentProvider,
574
+ isDefault,
575
+ isLegacy
576
+ });
577
+ }
578
+ }
579
+ }
580
+ }
581
+ return models;
582
+ }
583
+ async function executeCursorCLI(cliCommand) {
584
+ const { command: resolvedCommand, prefixArgs } = cliCommand ?? (await import("./resolve-provider-cli-ZEXVZKET.js")).resolveProviderCliCommand("cursor");
585
+ try {
586
+ let version;
587
+ try {
588
+ const sandboxed = wrapWithSandbox({
589
+ workingDir: process.cwd(),
590
+ command: resolvedCommand,
591
+ args: [...prefixArgs, "--version"],
592
+ providerName: "Cursor"
593
+ });
594
+ const versionOutput = execFileSync(sandboxed.command, sandboxed.args, {
595
+ encoding: "utf-8",
596
+ timeout: 5e3,
597
+ stdio: ["ignore", "pipe", "pipe"],
598
+ env: createCursorDiscoveryChildEnv()
599
+ }).trim();
600
+ version = versionOutput;
601
+ } catch {
602
+ }
603
+ const listingArgSets = [
604
+ [...prefixArgs, "models"],
605
+ [...prefixArgs, "--list-models"]
606
+ ];
607
+ let lastError;
608
+ for (const listingArgs of listingArgSets) {
609
+ try {
610
+ const sandboxed = wrapWithSandbox({
611
+ workingDir: process.cwd(),
612
+ command: resolvedCommand,
613
+ args: listingArgs,
614
+ providerName: "Cursor"
615
+ });
616
+ const output = execFileSync(sandboxed.command, sandboxed.args, {
617
+ encoding: "utf-8",
618
+ timeout: 1e4,
619
+ stdio: ["ignore", "pipe", "pipe"],
620
+ env: createCursorDiscoveryChildEnv()
621
+ });
622
+ const models = parseCursorModelOutput(output);
623
+ if (models.length > 0) {
624
+ return { models, version };
625
+ }
626
+ lastError = new Error("No models found in cursor-agent models output");
627
+ } catch (error) {
628
+ lastError = error instanceof Error ? error : new Error(String(error));
629
+ }
630
+ }
631
+ throw lastError ?? new Error("No models found in cursor-agent models output");
632
+ } catch (error) {
633
+ throw new Error(
634
+ `Failed to execute cursor-agent CLI: ${error instanceof Error ? error.message : String(error)}`
635
+ );
636
+ }
637
+ }
638
+ async function executeCursorApi(apiKey) {
639
+ const controller = new AbortController();
640
+ const timeout = setTimeout(() => controller.abort(), 12e3);
641
+ try {
642
+ const auth = Buffer.from(`${apiKey}:`, "utf8").toString("base64");
643
+ const response = await fetch("https://api.cursor.com/v0/models", {
644
+ method: "GET",
645
+ headers: {
646
+ Authorization: `Basic ${auth}`,
647
+ Accept: "application/json"
648
+ },
649
+ signal: controller.signal
650
+ });
651
+ if (!response.ok) {
652
+ const body = await response.text().catch(() => "");
653
+ throw new Error(`HTTP ${response.status}: ${body || response.statusText}`);
654
+ }
655
+ const parsed = await response.json();
656
+ const models = normalizeApiModels(parsed);
657
+ if (models.length === 0) {
658
+ throw new Error("No models found in Cursor API response");
659
+ }
660
+ return { models };
661
+ } catch (error) {
662
+ throw new Error(
663
+ `Failed to fetch Cursor API models: ${error instanceof Error ? error.message : String(error)}`
664
+ );
665
+ } finally {
666
+ clearTimeout(timeout);
667
+ }
668
+ }
669
+ async function readCache(cacheFilePath) {
670
+ try {
671
+ const content = await fs2.readFile(cacheFilePath, "utf-8");
672
+ const cached = JSON.parse(content);
673
+ if (!cached.models || !Array.isArray(cached.models) || !cached.timestamp) {
674
+ return null;
675
+ }
676
+ const age = Date.now() - cached.timestamp;
677
+ const ttl = getCacheTTL();
678
+ if (age > ttl) {
679
+ return null;
680
+ }
681
+ return cached;
682
+ } catch {
683
+ return null;
684
+ }
685
+ }
686
+ async function writeCache(cacheFilePath, result) {
687
+ try {
688
+ const cacheDir = path3.dirname(cacheFilePath);
689
+ await fs2.mkdir(cacheDir, { recursive: true });
690
+ await fs2.writeFile(cacheFilePath, JSON.stringify(result, null, 2), "utf-8");
691
+ } catch (error) {
692
+ console.warn(
693
+ `[Cursor Model Discovery] Failed to write cache: ${error instanceof Error ? error.message : String(error)}`
694
+ );
695
+ }
696
+ }
697
+ async function discoverCursorModels(options) {
698
+ const { forceRefresh = false, cacheDir = getDefaultCacheDir(), cliOnly = false } = options || {};
699
+ const cacheFilePath = getCacheFilePath(cacheDir);
700
+ if (!forceRefresh) {
701
+ const cached = await readCache(cacheFilePath);
702
+ if (cached) {
703
+ return cached;
704
+ }
705
+ }
706
+ const apiKey = cliOnly ? null : getCursorApiKeyFromEnv();
707
+ if (apiKey) {
708
+ try {
709
+ const { models } = await executeCursorApi(apiKey);
710
+ const result = {
711
+ models,
712
+ source: "api",
713
+ timestamp: Date.now(),
714
+ canonicalMappings: buildCanonicalMappings(models)
715
+ };
716
+ rememberAuthoritativeDiscovery(cacheFilePath, result);
717
+ await writeCache(cacheFilePath, result);
718
+ return result;
719
+ } catch (error) {
720
+ console.warn(
721
+ `[Cursor Model Discovery] API discovery failed, falling back to CLI: ${error instanceof Error ? error.message : String(error)}`
722
+ );
723
+ }
724
+ }
725
+ try {
726
+ const { models, version } = await executeCursorCLI(options?.cliCommand);
727
+ const result = {
728
+ models,
729
+ source: "cli",
730
+ timestamp: Date.now(),
731
+ cliVersion: version,
732
+ canonicalMappings: buildCanonicalMappings(models)
733
+ };
734
+ rememberAuthoritativeDiscovery(cacheFilePath, result);
735
+ await writeCache(cacheFilePath, result);
736
+ return result;
737
+ } catch (error) {
738
+ console.warn(
739
+ `[Cursor Model Discovery] CLI execution failed, using fallback: ${error instanceof Error ? error.message : String(error)}`
740
+ );
741
+ const result = {
742
+ models: CURSOR_KNOWN_MODELS,
743
+ source: "fallback",
744
+ timestamp: Date.now(),
745
+ canonicalMappings: buildCanonicalMappings(CURSOR_KNOWN_MODELS)
746
+ };
747
+ return result;
748
+ }
749
+ }
750
+ var cachedSnapshot = null;
751
+ var cachedMappingSnapshot = null;
752
+ function uniqueDiscoveredModelIds(models) {
753
+ return Array.from(
754
+ new Set(
755
+ models.map((model) => model?.id).filter((id) => typeof id === "string" && id.trim().length > 0)
756
+ )
757
+ );
758
+ }
759
+ function rememberAuthoritativeDiscovery(cachePath, result) {
760
+ if (result.source !== "api" && result.source !== "cli") {
761
+ return;
762
+ }
763
+ cachedSnapshot = {
764
+ cachePath,
765
+ mtimeMs: Date.now(),
766
+ modelIds: uniqueDiscoveredModelIds(result.models)
767
+ };
768
+ cachedMappingSnapshot = {
769
+ cachePath,
770
+ mtimeMs: Date.now(),
771
+ mappings: { ...result.canonicalMappings ?? {} }
772
+ };
773
+ }
774
+ function getCachedCursorModelIds(options) {
775
+ const cacheDir = options?.cacheDir ?? getDefaultCacheDir();
776
+ const cachePath = getCacheFilePath(cacheDir);
777
+ const ttl = getCacheTTL();
778
+ try {
779
+ const stat = statSync(cachePath);
780
+ if (Date.now() - stat.mtimeMs > ttl) {
781
+ if (cachedSnapshot?.cachePath === cachePath) {
782
+ cachedSnapshot = null;
783
+ }
784
+ return [];
785
+ }
786
+ if (cachedSnapshot && cachedSnapshot.cachePath === cachePath && cachedSnapshot.mtimeMs === stat.mtimeMs) {
787
+ return [...cachedSnapshot.modelIds];
788
+ }
789
+ const raw = readFileSync2(cachePath, "utf-8");
790
+ const parsed = JSON.parse(raw);
791
+ const unique = Array.isArray(parsed?.models) ? uniqueDiscoveredModelIds(parsed.models) : [];
792
+ cachedSnapshot = {
793
+ cachePath,
794
+ mtimeMs: stat.mtimeMs,
795
+ modelIds: unique
796
+ };
797
+ return [...unique];
798
+ } catch (error) {
799
+ if (error?.code === "ENOENT") {
800
+ if (cachedSnapshot?.cachePath === cachePath) {
801
+ return [...cachedSnapshot.modelIds];
802
+ }
803
+ return [];
804
+ }
805
+ if (cachedSnapshot?.cachePath === cachePath) {
806
+ cachedSnapshot = null;
807
+ }
808
+ try {
809
+ unlinkSync(cachePath);
810
+ } catch {
811
+ }
812
+ logWarning(
813
+ "cursor-model-discovery",
814
+ `Ignoring corrupt model cache at ${cachePath}: ${error instanceof Error ? error.message : String(error)}`
815
+ );
816
+ return [];
817
+ }
818
+ }
819
+ var LEGACY_CURSOR_CLAUDE_PATTERN = /^claude-(\d+(?:\.\d+)?)-(\w+?)(?:-([\w-]+))?$/;
820
+ var CURRENT_CURSOR_CLAUDE_PATTERN = /^claude-(opus|sonnet|fable)-(\d+(?:-\d+)*)(?:-([\w-]+))?$/;
821
+ function parseCursorClaudeModelId(modelId) {
822
+ const current = modelId.match(CURRENT_CURSOR_CLAUDE_PATTERN);
823
+ if (current) {
824
+ return {
825
+ tier: current[1] ?? "",
826
+ version: (current[2] ?? "").replaceAll("-", "."),
827
+ suffix: current[3] ?? ""
828
+ };
829
+ }
830
+ const legacy = modelId.match(LEGACY_CURSOR_CLAUDE_PATTERN);
831
+ if (!legacy) return void 0;
832
+ return { version: legacy[1] ?? "", tier: legacy[2] ?? "", suffix: legacy[3] ?? "" };
833
+ }
834
+ function buildCanonicalMappings(models) {
835
+ const mappings = {};
836
+ const canonicalRankings = /* @__PURE__ */ new Map();
837
+ for (const model of models) {
838
+ const parsed = parseCursorClaudeModelId(model.id);
839
+ if (!parsed) continue;
840
+ const { version, tier, suffix } = parsed;
841
+ if (suffix.includes("thinking")) continue;
842
+ const canonical = `${tier}-${version}`;
843
+ const baseRank = tier === "opus" ? ["high", "", "medium", "low", "xhigh", "max"].indexOf(suffix) : ["medium", "", "low", "high", "xhigh", "max"].indexOf(suffix);
844
+ const rank = baseRank === -1 ? Number.MAX_SAFE_INTEGER : baseRank;
845
+ if (suffix === "1m") {
846
+ const canonical1m = `${tier}-${version}-1m`;
847
+ if (!mappings[canonical1m]) {
848
+ mappings[canonical1m] = model.id;
849
+ }
850
+ } else {
851
+ const existingRank = canonicalRankings.get(canonical);
852
+ if (existingRank === void 0 || rank < existingRank) {
853
+ mappings[canonical] = model.id;
854
+ canonicalRankings.set(canonical, rank);
855
+ }
856
+ }
857
+ }
858
+ return mappings;
859
+ }
860
+ function getCachedCanonicalMappings(options) {
861
+ const cacheDir = options?.cacheDir ?? getDefaultCacheDir();
862
+ const cachePath = getCacheFilePath(cacheDir);
863
+ const ttl = getCacheTTL();
864
+ try {
865
+ const stat = statSync(cachePath);
866
+ if (Date.now() - stat.mtimeMs > ttl) {
867
+ if (cachedMappingSnapshot?.cachePath === cachePath) cachedMappingSnapshot = null;
868
+ return {};
869
+ }
870
+ if (cachedMappingSnapshot && cachedMappingSnapshot.cachePath === cachePath && cachedMappingSnapshot.mtimeMs === stat.mtimeMs) {
871
+ return { ...cachedMappingSnapshot.mappings };
872
+ }
873
+ const raw = readFileSync2(cachePath, "utf-8");
874
+ const parsed = JSON.parse(raw);
875
+ const mappings = { ...parsed?.canonicalMappings ?? {} };
876
+ cachedMappingSnapshot = { cachePath, mtimeMs: stat.mtimeMs, mappings };
877
+ return { ...mappings };
878
+ } catch (error) {
879
+ if (error?.code === "ENOENT") {
880
+ if (cachedMappingSnapshot?.cachePath === cachePath) {
881
+ return { ...cachedMappingSnapshot.mappings };
882
+ }
883
+ return {};
884
+ }
885
+ if (cachedMappingSnapshot?.cachePath === cachePath) cachedMappingSnapshot = null;
886
+ try {
887
+ unlinkSync(cachePath);
888
+ } catch {
889
+ }
890
+ console.warn(
891
+ `[cursor-model-discovery] Ignoring corrupt model cache at ${cachePath}: ${error instanceof Error ? error.message : String(error)}`
892
+ );
893
+ return {};
894
+ }
895
+ }
896
+
897
+ // src/core/providers/cursor/config.ts
898
+ var CURSOR_FALLBACK_MODELS = CURSOR_KNOWN_MODELS.map((m) => m.id);
899
+ var GROK_FAST_PRICE_MODELS = {
900
+ "4.7": "grok-4.7-build-fast"
901
+ };
902
+
903
+ // src/core/providers/cursor/model-price-target.ts
904
+ var EFFORT_SUFFIX = /-(?:extra-high|xhigh|none|minimal|low|medium|high|max)$/;
905
+ var MAX_SUFFIX_STRIPS = 4;
906
+ var MAKER_ROUTE_PREFIX = { kimi: "moonshotai", glm: "z-ai" };
907
+ function stripVariantSuffixes(modelId) {
908
+ let base = modelId;
909
+ let fast = false;
910
+ for (let i = 0; i < MAX_SUFFIX_STRIPS; i++) {
911
+ if (base.endsWith("-fast")) {
912
+ fast = true;
913
+ base = base.slice(0, -"-fast".length);
914
+ } else if (base.endsWith("-thinking")) {
915
+ base = base.slice(0, -"-thinking".length);
916
+ } else if (EFFORT_SUFFIX.test(base)) {
917
+ base = base.replace(EFFORT_SUFFIX, "");
918
+ } else {
919
+ break;
920
+ }
921
+ }
922
+ return { base, fast };
923
+ }
924
+ function resolveCursorMakerPriceTarget(modelId, hasRow = () => false) {
925
+ const { base, fast } = stripVariantSuffixes(modelId.toLowerCase());
926
+ const fastTier = fast ? { speed_tier: "fast" } : {};
927
+ const cursorFastRow = `${base}-fast`;
928
+ if (fast && hasRow("cursor", cursorFastRow)) {
929
+ return { provider: "cursor", model: cursorFastRow };
930
+ }
931
+ const grok = /^(?:cursor-)?grok-(\d+\.\d+)$/.exec(base);
932
+ if (grok) {
933
+ const version = grok[1];
934
+ const fastModel = fast && version ? GROK_FAST_PRICE_MODELS[version] : void 0;
935
+ if (fastModel) return { provider: "grok", model: fastModel };
936
+ return { provider: "grok", model: `grok-${version}` };
937
+ }
938
+ if (/^claude-(?:opus|sonnet|haiku|fable)-\d+(?:-\d+)?$/.test(base)) {
939
+ return { provider: "claude", model: base, ...fastTier };
940
+ }
941
+ const legacyClaude = /^claude-(\d+)(?:\.(\d+))?-(opus|sonnet|haiku)$/.exec(base);
942
+ if (legacyClaude) {
943
+ const [, major, minor, family] = legacyClaude;
944
+ const version = minor === void 0 ? major : `${major}-${minor}`;
945
+ return { provider: "claude", model: `claude-${family}-${version}`, ...fastTier };
946
+ }
947
+ if (/^gpt-\d/.test(base)) {
948
+ return { provider: "codex", model: base, ...fastTier };
949
+ }
950
+ if (/^gemini-/.test(base)) {
951
+ const preview = `${base}-preview`;
952
+ if (!hasRow("gemini", base) && hasRow("gemini", preview)) {
953
+ return {
954
+ provider: "gemini",
955
+ model: preview,
956
+ note: `${base} priced at the preview rate pending GA`
957
+ };
958
+ }
959
+ return { provider: "gemini", model: base };
960
+ }
961
+ const makerRoute = /^(kimi|glm)-/.exec(base);
962
+ if (makerRoute) {
963
+ const prefix = MAKER_ROUTE_PREFIX[makerRoute[1] ?? ""];
964
+ return prefix ? { provider: "openrouter", model: `${prefix}/${base}` } : null;
965
+ }
966
+ return null;
967
+ }
968
+
969
+ // src/core/providers/model-update/pricing/passthrough-resolver.ts
970
+ var DEFAULT_CONFIG = {
971
+ maxDepth: 5,
972
+ strict: false
973
+ };
974
+ var DEFAULT_CURSOR_MAPPINGS = {
975
+ // Claude models
976
+ "sonnet-4.6": { provider: "claude", model: "claude-sonnet-4-6" },
977
+ "sonnet-4-6": { provider: "claude", model: "claude-sonnet-4-6" },
978
+ "claude-sonnet-4-6": { provider: "claude", model: "claude-sonnet-4-6" },
979
+ "sonnet-4.5": { provider: "claude", model: "claude-sonnet-4-5" },
980
+ "sonnet-4-5": { provider: "claude", model: "claude-sonnet-4-5" },
981
+ "claude-sonnet-4-5": { provider: "claude", model: "claude-sonnet-4-5" },
982
+ "opus-4.6": { provider: "claude", model: "claude-opus-4-6" },
983
+ "opus-4-6": { provider: "claude", model: "claude-opus-4-6" },
984
+ "claude-opus-4-6": { provider: "claude", model: "claude-opus-4-6" },
985
+ "haiku-4.5": { provider: "claude", model: "claude-haiku-4-5" },
986
+ "haiku-4-5": { provider: "claude", model: "claude-haiku-4-5" },
987
+ "claude-haiku-4-5": { provider: "claude", model: "claude-haiku-4-5" },
988
+ "claude-haiku-4-5-20251001": { provider: "claude", model: "claude-haiku-4-5" },
989
+ "opus-4.5": { provider: "claude", model: "claude-opus-4-5" },
990
+ "opus-4-5": { provider: "claude", model: "claude-opus-4-5" },
991
+ "claude-opus-4-5": { provider: "claude", model: "claude-opus-4-5" },
992
+ "claude-3.5-sonnet": { provider: "claude", model: "claude-3-5-sonnet" },
993
+ "claude-3-5-sonnet": { provider: "claude", model: "claude-3-5-sonnet" },
994
+ // OpenAI models
995
+ "gpt-5": { provider: "codex", model: "gpt-5" },
996
+ "gpt-5-mini": { provider: "codex", model: "gpt-5-mini" },
997
+ "gpt-4o": { provider: "codex", model: "gpt-4o" },
998
+ "gpt-4o-mini": { provider: "codex", model: "gpt-4o-mini" },
999
+ "gpt-4-turbo": { provider: "codex", model: "gpt-4-turbo" },
1000
+ o1: { provider: "codex", model: "o1" },
1001
+ "o1-mini": { provider: "codex", model: "o1-mini" },
1002
+ "o1-pro": { provider: "codex", model: "o1-pro" },
1003
+ o3: { provider: "codex", model: "o3" },
1004
+ "o3-mini": { provider: "codex", model: "o3-mini" },
1005
+ // Gemini models
1006
+ "gemini-2.5-pro": { provider: "gemini", model: "gemini-2.5-pro" },
1007
+ "gemini-2.5-flash": { provider: "gemini", model: "gemini-2.5-flash" },
1008
+ "gemini-2.0-flash": { provider: "gemini", model: "gemini-2.0-flash" },
1009
+ "gemini-1.5-pro": { provider: "gemini", model: "gemini-1.5-pro" },
1010
+ "gemini-1.5-flash": { provider: "gemini", model: "gemini-1.5-flash" }
1011
+ };
1012
+ var PassthroughResolver = class {
1013
+ config;
1014
+ customMappings = /* @__PURE__ */ new Map();
1015
+ constructor(config = {}) {
1016
+ this.config = { ...DEFAULT_CONFIG, ...config };
1017
+ }
1018
+ /**
1019
+ * Register custom pass-through mappings for a provider.
1020
+ */
1021
+ registerMappings(providerId, mappings) {
1022
+ this.customMappings.set(providerId.toLowerCase(), mappings);
1023
+ }
1024
+ /**
1025
+ * Resolve pricing for a model, following pass-through chains.
1026
+ */
1027
+ resolvePricing(providerId, modelId, depth = 0, visited = /* @__PURE__ */ new Set()) {
1028
+ const key = `${providerId}:${modelId}`;
1029
+ if (visited.has(key)) {
1030
+ return {
1031
+ status: "unknown",
1032
+ modelId,
1033
+ reason: `Cycle detected in pass-through chain: ${Array.from(visited).join(" -> ")} -> ${key}`
1034
+ };
1035
+ }
1036
+ if (depth >= this.config.maxDepth) {
1037
+ return {
1038
+ status: "unknown",
1039
+ modelId,
1040
+ reason: `Max pass-through depth (${this.config.maxDepth}) exceeded`
1041
+ };
1042
+ }
1043
+ visited.add(key);
1044
+ const pricing = this.getDirectPricing(providerId, modelId);
1045
+ if (pricing) {
1046
+ return {
1047
+ status: "resolved",
1048
+ provider: providerId,
1049
+ model: modelId,
1050
+ pricing: pricing.pricing,
1051
+ source: pricing.source
1052
+ };
1053
+ }
1054
+ const passthrough = this.getPassthroughTarget(providerId, modelId);
1055
+ if (passthrough) {
1056
+ const upstream = this.resolvePricing(
1057
+ passthrough.provider,
1058
+ passthrough.model,
1059
+ depth + 1,
1060
+ visited
1061
+ );
1062
+ return upstream.status === "resolved" ? { ...upstream, pricing: applyTargetToPricing(upstream.pricing, passthrough) } : upstream;
1063
+ }
1064
+ return {
1065
+ status: "unknown",
1066
+ modelId,
1067
+ reason: `No pricing found for ${providerId}:${modelId}`
1068
+ };
1069
+ }
1070
+ /**
1071
+ * Get pass-through target for a model, if configured. Names the upstream row
1072
+ * without resolving its rates, for callers that read rows from their own
1073
+ * registry snapshot: such a caller passes that registry's `pricing` block so
1074
+ * the lookup neither reads nor clones the live registry per model.
1075
+ * `hasPricingRow` answers which rows exist for the same reason, defaulting to
1076
+ * the live registry.
1077
+ */
1078
+ getPassthroughTarget(providerId, modelId, providerPricing, hasPricingRow = registryHasPricingRow) {
1079
+ const normalizedProvider = providerId.toLowerCase();
1080
+ const normalizedModel = modelId.toLowerCase();
1081
+ const customMap = this.customMappings.get(normalizedProvider);
1082
+ const customTarget = customMap?.[normalizedModel];
1083
+ if (customTarget) {
1084
+ return customTarget;
1085
+ }
1086
+ try {
1087
+ const pricingConfig = providerPricing ?? ProviderRegistry.getProviderConfig(normalizedProvider)?.pricing;
1088
+ if (pricingConfig && "_meta" in pricingConfig) {
1089
+ const meta = pricingConfig._meta;
1090
+ const yamlTarget = meta?.passthrough_map?.[normalizedModel];
1091
+ if (yamlTarget) {
1092
+ return yamlTarget;
1093
+ }
1094
+ }
1095
+ const providerTarget = this.resolveProviderPassthroughTarget(pricingConfig, normalizedModel);
1096
+ if (providerTarget) {
1097
+ return providerTarget;
1098
+ }
1099
+ if (pricingConfig && "passthrough_map" in pricingConfig) {
1100
+ const map = pricingConfig.passthrough_map;
1101
+ const directTarget = map[normalizedModel];
1102
+ if (directTarget) {
1103
+ return directTarget;
1104
+ }
1105
+ }
1106
+ } catch {
1107
+ }
1108
+ if (normalizedProvider === "cursor") {
1109
+ return DEFAULT_CURSOR_MAPPINGS[normalizedModel] ?? resolveCursorMakerPriceTarget(normalizedModel, hasPricingRow);
1110
+ }
1111
+ return null;
1112
+ }
1113
+ /** Provider-level route: the first-class `pricing.passthrough`, else the legacy `_meta` keys. */
1114
+ resolveProviderPassthroughTarget(pricingConfig, normalizedModel) {
1115
+ const meta = pricingConfig?._meta ?? {};
1116
+ const provider = pricingConfig?.passthrough?.provider ?? meta.passthrough_provider;
1117
+ if (!provider) {
1118
+ return null;
1119
+ }
1120
+ return {
1121
+ provider,
1122
+ model: stripConfiguredRoutePrefixes(
1123
+ normalizedModel,
1124
+ pricingConfig?.passthrough?.strip_prefixes ?? meta.passthrough_strip_prefixes
1125
+ )
1126
+ };
1127
+ }
1128
+ /**
1129
+ * Get direct pricing for a model from the shared metadata resolver.
1130
+ */
1131
+ getDirectPricing(providerId, modelId) {
1132
+ try {
1133
+ const resolvedPricing = ModelMetadataResolver.getPricing(providerId, modelId);
1134
+ if (!resolvedPricing) {
1135
+ return null;
1136
+ }
1137
+ const { modelPricing: pricing } = resolvedPricing;
1138
+ const card = rateCardFromPricingRow(pricing);
1139
+ if (!card) {
1140
+ return null;
1141
+ }
1142
+ return {
1143
+ pricing: {
1144
+ input_per_1m_tokens: formatUsdPer1m(card.inputPer1m),
1145
+ output_per_1m_tokens: formatUsdPer1m(card.outputPer1m),
1146
+ cache_read_per_1m_tokens: card.cacheReadPer1m === void 0 ? void 0 : formatUsdPer1m(card.cacheReadPer1m),
1147
+ cache_write_per_1m_tokens: card.cacheWritePer1m === void 0 ? void 0 : formatUsdPer1m(card.cacheWritePer1m),
1148
+ cache_write_1h_per_1m_tokens: card.cacheWriteLongPer1m === void 0 ? void 0 : formatUsdPer1m(card.cacheWriteLongPer1m),
1149
+ long_context_pricing: pricing.long_context_pricing,
1150
+ speed_tiers: pricing.speed_tiers,
1151
+ context_window: pricing.context_window,
1152
+ features: pricing.features,
1153
+ notes: pricing.notes
1154
+ },
1155
+ source: pricingSourceFromRow(pricing) ?? "manual"
1156
+ };
1157
+ } catch {
1158
+ return null;
1159
+ }
1160
+ }
1161
+ /**
1162
+ * Check if a provider uses pass-through pricing.
1163
+ */
1164
+ isPassthroughProvider(providerId) {
1165
+ const normalizedProvider = providerId.toLowerCase();
1166
+ if (this.customMappings.has(normalizedProvider)) {
1167
+ return true;
1168
+ }
1169
+ try {
1170
+ const providerConfig = ProviderRegistry.getProviderConfig(normalizedProvider);
1171
+ const pricingConfig = providerConfig?.pricing;
1172
+ if (pricingConfig?.passthrough) {
1173
+ return true;
1174
+ }
1175
+ if (pricingConfig && "_meta" in pricingConfig) {
1176
+ const meta = pricingConfig._meta;
1177
+ return meta?.pricing_source === "passthrough";
1178
+ }
1179
+ } catch {
1180
+ }
1181
+ return normalizedProvider === "cursor";
1182
+ }
1183
+ /**
1184
+ * Get all pass-through mappings for a provider.
1185
+ */
1186
+ getMappings(providerId) {
1187
+ const normalizedProvider = providerId.toLowerCase();
1188
+ const mappings = {};
1189
+ const customMap = this.customMappings.get(normalizedProvider);
1190
+ if (customMap) {
1191
+ Object.assign(mappings, customMap);
1192
+ }
1193
+ try {
1194
+ const providerConfig = ProviderRegistry.getProviderConfig(normalizedProvider);
1195
+ const pricingConfig = providerConfig?.pricing;
1196
+ if (pricingConfig && "_meta" in pricingConfig) {
1197
+ const meta = pricingConfig._meta;
1198
+ if (meta?.passthrough_map) {
1199
+ Object.assign(mappings, meta.passthrough_map);
1200
+ }
1201
+ }
1202
+ if (pricingConfig && "passthrough_map" in pricingConfig) {
1203
+ const map = pricingConfig.passthrough_map;
1204
+ Object.assign(mappings, map);
1205
+ }
1206
+ } catch {
1207
+ }
1208
+ if (normalizedProvider === "cursor") {
1209
+ return { ...DEFAULT_CURSOR_MAPPINGS, ...mappings };
1210
+ }
1211
+ return mappings;
1212
+ }
1213
+ };
1214
+ function registryHasPricingRow(providerId, modelId) {
1215
+ try {
1216
+ return ProviderRegistry.getProviderConfig(providerId)?.pricing?.models?.[modelId] !== void 0;
1217
+ } catch {
1218
+ return false;
1219
+ }
1220
+ }
1221
+ function stripConfiguredRoutePrefixes(modelId, prefixes) {
1222
+ if (!prefixes || prefixes.length === 0) {
1223
+ return modelId;
1224
+ }
1225
+ const removablePrefixes = new Set(prefixes.map((prefix) => prefix.toLowerCase().trim()));
1226
+ const segments = modelId.split("/").filter(Boolean);
1227
+ while (segments.length > 2 && removablePrefixes.has(segments[0]?.toLowerCase() ?? "")) {
1228
+ segments.shift();
1229
+ }
1230
+ return segments.join("/") || modelId;
1231
+ }
1232
+ function formatUsdPer1m(value) {
1233
+ const [whole = "0", fraction = ""] = value.toFixed(6).split(".");
1234
+ return `${whole}.${fraction.replace(/0+$/, "").padEnd(2, "0")}`;
1235
+ }
1236
+ function joinPricingNotes(...notes) {
1237
+ const present = notes.filter((note) => Boolean(note));
1238
+ return present.length > 0 ? present.join(" ") : void 0;
1239
+ }
1240
+ function applyTargetToPricing(pricing, target) {
1241
+ const noted = target.note ? { ...pricing, notes: joinPricingNotes(pricing.notes, target.note) } : pricing;
1242
+ const tierName = target.speed_tier;
1243
+ if (!tierName) {
1244
+ return noted;
1245
+ }
1246
+ const tier = pricing.speed_tiers?.[tierName];
1247
+ if (!tier) {
1248
+ return {
1249
+ ...noted,
1250
+ routed_speed_tier: { requested: tierName, applied: "standard" },
1251
+ notes: joinPricingNotes(noted.notes, `${tierName} requested, priced at standard.`)
1252
+ };
1253
+ }
1254
+ const unpublishedAbove = tier.long_context_pricing ? void 0 : pricing.long_context_pricing?.threshold_tokens;
1255
+ return {
1256
+ ...noted,
1257
+ input_per_1m_tokens: formatUsdPer1m(tier.input_per_1m_tokens),
1258
+ output_per_1m_tokens: formatUsdPer1m(tier.output_per_1m_tokens),
1259
+ cache_read_per_1m_tokens: tier.cache_read_per_1m_tokens === void 0 ? void 0 : formatUsdPer1m(tier.cache_read_per_1m_tokens),
1260
+ cache_write_per_1m_tokens: tier.cache_write_per_1m_tokens === void 0 ? void 0 : formatUsdPer1m(tier.cache_write_per_1m_tokens),
1261
+ cache_write_1h_per_1m_tokens: tier.cache_write_1h_per_1m_tokens === void 0 ? void 0 : formatUsdPer1m(tier.cache_write_1h_per_1m_tokens),
1262
+ long_context_pricing: tier.long_context_pricing,
1263
+ speed_tiers: void 0,
1264
+ routed_speed_tier: {
1265
+ requested: tierName,
1266
+ applied: tierName,
1267
+ ...unpublishedAbove === void 0 ? {} : { unpublishedLongContextAboveTokens: unpublishedAbove }
1268
+ }
1269
+ };
1270
+ }
1271
+ var passthroughResolver = new PassthroughResolver();
1272
+ function resolvePassthroughPricing(providerId, modelId) {
1273
+ return passthroughResolver.resolvePricing(providerId, modelId);
1274
+ }
1275
+
1276
+ // src/telemetry/worker.ts
1277
+ import { createHash as createHash2 } from "crypto";
1278
+
1279
+ // src/cost-tracking/cache-write-duration.ts
1280
+ var CACHE_WRITE_LONG_TOKENS_DIMENSION = "cacheWriteLongTokens";
1281
+ function clampLongCacheWriteTokens(cacheWriteTokens, longTokens) {
1282
+ if (longTokens === void 0 || !Number.isFinite(longTokens) || longTokens <= 0) return 0;
1283
+ return Math.min(longTokens, Math.max(0, cacheWriteTokens ?? 0));
1284
+ }
1285
+ function readLongCacheWriteTokens(extraDimensionsJson) {
1286
+ if (!extraDimensionsJson) return void 0;
1287
+ try {
1288
+ const value = JSON.parse(extraDimensionsJson)?.[CACHE_WRITE_LONG_TOKENS_DIMENSION];
1289
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
1290
+ } catch {
1291
+ return void 0;
1292
+ }
1293
+ }
1294
+ function cacheWriteUsd(writeRate, longRate, cacheWriteTokens, longTokens) {
1295
+ const long = clampLongCacheWriteTokens(cacheWriteTokens, longTokens);
1296
+ const effectiveLongRate = longRate ?? writeRate;
1297
+ return (writeRate === void 0 ? 0 : (cacheWriteTokens - long) / 1e6 * writeRate) + (effectiveLongRate === void 0 ? 0 : long / 1e6 * effectiveLongRate);
1298
+ }
1299
+
1300
+ // src/cost-tracking/usage-granularity.ts
1301
+ function usageGranularityFromEventSemantics(eventSemantics) {
1302
+ return eventSemantics === "final" ? "request" : "run_aggregate";
1303
+ }
1304
+ function longContextContextTokens(usage) {
1305
+ const cacheReadTokens = usage.cacheReadIncludedInInput ? 0 : usage.cacheReadTokens ?? 0;
1306
+ const cacheWriteTokens = usage.cacheWriteIncludedInInput ? 0 : usage.cacheWriteTokens ?? 0;
1307
+ return usage.inputTokens + cacheReadTokens + cacheWriteTokens;
1308
+ }
1309
+
1310
+ // src/cost-tracking/request-long-context.ts
1311
+ var MAX_REQUEST_EVIDENCE = 1e3;
1312
+ function boundRequestEvidence(requests, complete = true) {
1313
+ if (requests.length === 0) return void 0;
1314
+ const truncated = requests.length > MAX_REQUEST_EVIDENCE;
1315
+ return {
1316
+ requests: truncated ? requests.slice(0, MAX_REQUEST_EVIDENCE) : requests,
1317
+ complete: complete && !truncated
1318
+ };
1319
+ }
1320
+ var STATUS_PRECEDENCE = ["applied", "below_threshold", "not_defined"];
1321
+ function sumOf(requests, pick) {
1322
+ return requests.reduce((total, request) => total + pick(request), 0);
1323
+ }
1324
+ function premiumIncrement(rates, usage, request) {
1325
+ const premium = rates.longContext;
1326
+ if (!premium) return { inputUsd: 0, outputUsd: 0, cacheUsd: 0 };
1327
+ const cacheRead = request.cachedInputTokens;
1328
+ const cacheWrite = request.cacheWriteTokens;
1329
+ const cacheWriteLong = clampLongCacheWriteTokens(cacheWrite, request.cacheWriteLongTokens);
1330
+ const uncached = Math.max(0, request.inputTokens - cacheRead - cacheWrite);
1331
+ const inputBilled = uncached + (rates.cacheReadPer1m === void 0 && usage.cacheReadIncludedInInput ? cacheRead : 0);
1332
+ const extra = (multiplier, rate, tokens) => rate === void 0 ? 0 : ((multiplier ?? 1) - 1) * rate * tokens / 1e6;
1333
+ return {
1334
+ inputUsd: extra(premium.inputMultiplier, rates.inputPer1m, inputBilled),
1335
+ outputUsd: extra(premium.outputMultiplier, rates.outputPer1m, request.outputTokens),
1336
+ cacheUsd: extra(premium.cacheReadMultiplier, rates.cacheReadPer1m, cacheRead) + extra(premium.cacheWriteMultiplier, rates.cacheWritePer1m, cacheWrite - cacheWriteLong) + extra(
1337
+ premium.cacheWriteMultiplier,
1338
+ rates.cacheWriteLongPer1m ?? rates.cacheWritePer1m,
1339
+ cacheWriteLong
1340
+ )
1341
+ };
1342
+ }
1343
+ function assessRequestLongContext(standard, ratesFor, usage, evidence) {
1344
+ const requests = evidence?.requests ?? [];
1345
+ if (!evidence || requests.length === 0) return void 0;
1346
+ const runPrompt = longContextContextTokens(usage);
1347
+ const covered = [
1348
+ [sumOf(requests, (r) => r.inputTokens), runPrompt],
1349
+ [sumOf(requests, (r) => r.outputTokens), usage.outputTokens],
1350
+ [sumOf(requests, (r) => r.cachedInputTokens), usage.cacheReadTokens ?? 0],
1351
+ [sumOf(requests, (r) => r.cacheWriteTokens), usage.cacheWriteTokens ?? 0]
1352
+ ];
1353
+ if (covered.some(([requested, run]) => requested > run)) {
1354
+ return {
1355
+ inputUsd: 0,
1356
+ outputUsd: 0,
1357
+ cacheUsd: 0,
1358
+ status: "not_assessed",
1359
+ incompleteReason: "request usage exceeds the run usage"
1360
+ };
1361
+ }
1362
+ const reasons = [];
1363
+ if (!evidence.complete) reasons.push("request evidence is incomplete");
1364
+ if (covered.some(([requested, run]) => requested < run)) {
1365
+ reasons.push("request evidence covers only part of the run usage");
1366
+ }
1367
+ const total = { inputUsd: 0, outputUsd: 0, cacheUsd: 0 };
1368
+ const statuses = /* @__PURE__ */ new Set();
1369
+ const unpriceableTiers = /* @__PURE__ */ new Set();
1370
+ for (const request of requests) {
1371
+ const { rates, tier, unresolvedTier } = ratesFor(request.speed);
1372
+ if (unresolvedTier && standard.longContext && request.inputTokens > standard.longContext.thresholdTokens) {
1373
+ unpriceableTiers.add(unresolvedTier);
1374
+ continue;
1375
+ }
1376
+ const premium = rates.longContext;
1377
+ if (!premium) {
1378
+ if (rates !== standard && standard.longContext && request.inputTokens > standard.longContext.thresholdTokens) {
1379
+ unpriceableTiers.add(tier);
1380
+ } else {
1381
+ statuses.add("not_defined");
1382
+ }
1383
+ continue;
1384
+ }
1385
+ if (request.inputTokens > premium.thresholdTokens) {
1386
+ statuses.add("applied");
1387
+ const increment = premiumIncrement(rates, usage, request);
1388
+ total.inputUsd += increment.inputUsd;
1389
+ total.outputUsd += increment.outputUsd;
1390
+ total.cacheUsd += increment.cacheUsd;
1391
+ } else {
1392
+ statuses.add("below_threshold");
1393
+ }
1394
+ }
1395
+ if (unpriceableTiers.size > 0) {
1396
+ reasons.push(`no long-context rates for speed tier ${[...unpriceableTiers].sort().join(", ")}`);
1397
+ }
1398
+ return {
1399
+ ...total,
1400
+ status: reasons.length > 0 ? "not_assessed" : STATUS_PRECEDENCE.find((status) => statuses.has(status)) ?? "not_assessed",
1401
+ ...reasons.length > 0 ? { incompleteReason: reasons.join("; ") } : {}
1402
+ };
1403
+ }
1404
+
1405
+ // src/cost-tracking/list-price.ts
1406
+ function listPriceUsageFromRecord(record) {
1407
+ return {
1408
+ inputTokens: record.inputTokens,
1409
+ outputTokens: record.outputTokens,
1410
+ cacheReadTokens: record.cacheReadTokens ?? 0,
1411
+ cacheWriteTokens: record.cacheWriteTokens,
1412
+ cacheWriteLongTokens: record.cacheWriteLongTokens,
1413
+ cacheReadIncludedInInput: record.cacheReadTokensIncludedInInput,
1414
+ granularity: record.granularity,
1415
+ requestEvidence: record.requestEvidence
1416
+ };
1417
+ }
1418
+ var STANDARD_SPEED_TIER = "standard";
1419
+ function normalizeTierName(tier) {
1420
+ const normalized = tier?.trim().toLowerCase();
1421
+ return normalized ? normalized : void 0;
1422
+ }
1423
+ function selectListPriceRates(card, requestedTier) {
1424
+ const requested = normalizeTierName(requestedTier);
1425
+ if (requested === void 0 || requested === STANDARD_SPEED_TIER) {
1426
+ return {
1427
+ rates: card,
1428
+ speedTier: { ...requested ? { requested } : {}, applied: STANDARD_SPEED_TIER }
1429
+ };
1430
+ }
1431
+ const tierEntry = Object.entries(card.speedTiers ?? {}).find(
1432
+ ([name]) => name.toLowerCase() === requested
1433
+ );
1434
+ return tierEntry ? { rates: tierEntry[1], speedTier: { requested, applied: requested } } : { rates: card, speedTier: { requested, applied: STANDARD_SPEED_TIER } };
1435
+ }
1436
+ function routedSpeedTier(runRequested, routed) {
1437
+ if (normalizeTierName(runRequested) !== void 0 || !routed) {
1438
+ return { requested: runRequested };
1439
+ }
1440
+ if (routed.applied === STANDARD_SPEED_TIER) {
1441
+ return { requested: routed.requested };
1442
+ }
1443
+ return {
1444
+ reported: { requested: routed.requested, applied: routed.applied },
1445
+ unpublishedAbove: routed.unpublishedLongContextAboveTokens
1446
+ };
1447
+ }
1448
+ function unpublishedThreshold(card, rates, routedAbove) {
1449
+ return routedAbove ?? (rates === card ? void 0 : card.longContext?.thresholdTokens);
1450
+ }
1451
+ function priceWithRates(rates, usage, unpublishedAbove) {
1452
+ const cacheReadTokens = usage.cacheReadTokens ?? 0;
1453
+ const cacheWriteTokens = usage.cacheWriteTokens ?? 0;
1454
+ const cacheWriteLongTokens = clampLongCacheWriteTokens(
1455
+ cacheWriteTokens,
1456
+ usage.cacheWriteLongTokens
1457
+ );
1458
+ const longContext = rates.longContext;
1459
+ let longContextPremium;
1460
+ let premium;
1461
+ const standardThreshold = unpublishedAbove;
1462
+ if (usage.granularity !== "request") {
1463
+ const threshold = longContext?.thresholdTokens ?? standardThreshold;
1464
+ longContextPremium = threshold !== void 0 && longContextContextTokens(usage) <= threshold ? "below_threshold" : "not_assessed";
1465
+ } else if (!longContext) {
1466
+ longContextPremium = standardThreshold === void 0 ? "not_defined" : longContextContextTokens(usage) > standardThreshold ? "not_assessed" : "below_threshold";
1467
+ } else if (longContextContextTokens(usage) > longContext.thresholdTokens) {
1468
+ longContextPremium = "applied";
1469
+ premium = longContext;
1470
+ } else {
1471
+ longContextPremium = "below_threshold";
1472
+ }
1473
+ const inputRate = rates.inputPer1m * (premium?.inputMultiplier ?? 1);
1474
+ const outputRate = rates.outputPer1m * (premium?.outputMultiplier ?? 1);
1475
+ const cacheReadRate = rates.cacheReadPer1m === void 0 ? void 0 : rates.cacheReadPer1m * (premium?.cacheReadMultiplier ?? 1);
1476
+ const cacheWriteRate = rates.cacheWritePer1m === void 0 ? void 0 : rates.cacheWritePer1m * (premium?.cacheWriteMultiplier ?? 1);
1477
+ const cacheWriteLongRate = rates.cacheWriteLongPer1m === void 0 ? void 0 : rates.cacheWriteLongPer1m * (premium?.cacheWriteMultiplier ?? 1);
1478
+ const billableInputTokens = usage.cacheReadIncludedInInput && cacheReadRate !== void 0 ? Math.max(0, usage.inputTokens - cacheReadTokens) : usage.inputTokens;
1479
+ return {
1480
+ inputUsd: billableInputTokens / 1e6 * inputRate,
1481
+ outputUsd: usage.outputTokens / 1e6 * outputRate,
1482
+ cacheUsd: (cacheReadRate === void 0 ? 0 : cacheReadTokens / 1e6 * cacheReadRate) + cacheWriteUsd(cacheWriteRate, cacheWriteLongRate, cacheWriteTokens, cacheWriteLongTokens),
1483
+ longContextPremium
1484
+ };
1485
+ }
1486
+ var PREMIUM_PRECEDENCE = [
1487
+ "applied",
1488
+ "below_threshold",
1489
+ "not_assessed",
1490
+ "not_defined"
1491
+ ];
1492
+ function speedPortionUsage(usage, portion) {
1493
+ return {
1494
+ ...usage,
1495
+ inputTokens: portion.inputTokens,
1496
+ outputTokens: portion.outputTokens,
1497
+ cacheReadTokens: portion.cacheReadTokens ?? 0,
1498
+ cacheWriteTokens: portion.cacheWriteTokens ?? 0,
1499
+ // The run's long-write count describes the whole run, not this portion.
1500
+ cacheWriteLongTokens: portion.cacheWriteLongTokens ?? 0
1501
+ };
1502
+ }
1503
+ function breakdownMatchesUsage(usage, portions) {
1504
+ const sum = (field) => portions.reduce((total, portion) => total + (portion[field] ?? 0), 0);
1505
+ return sum("inputTokens") === usage.inputTokens && sum("outputTokens") === usage.outputTokens && sum("cacheReadTokens") === (usage.cacheReadTokens ?? 0) && sum("cacheWriteTokens") === (usage.cacheWriteTokens ?? 0);
1506
+ }
1507
+ function computeSpeedBreakdownPrice(card, usage, requestedTier, breakdown, routedAbove) {
1508
+ const requested = normalizeTierName(requestedTier);
1509
+ const tierEntries = Object.entries(breakdown.tiers).map(([name, portion]) => [normalizeTierName(name), portion]).filter((entry) => entry[0] !== void 0).sort(([a], [b]) => a.localeCompare(b));
1510
+ if (tierEntries.length === 0 && !breakdown.unknown) return void 0;
1511
+ const observed = tierEntries.map(([name]) => name);
1512
+ const portions = [...tierEntries.map(([, portion]) => portion), breakdown.unknown].filter(
1513
+ (portion) => portion !== void 0
1514
+ );
1515
+ const unknownIsStandard = breakdown.unknown !== void 0 && Object.keys(card.speedTiers ?? {}).length === 0;
1516
+ const effectiveTiers = /* @__PURE__ */ new Set([
1517
+ ...observed,
1518
+ ...unknownIsStandard ? [STANDARD_SPEED_TIER] : []
1519
+ ]);
1520
+ const [onlyTier] = effectiveTiers;
1521
+ if (effectiveTiers.size === 1 && onlyTier !== void 0 && (!breakdown.unknown || unknownIsStandard)) {
1522
+ const { rates, speedTier } = selectListPriceRates(card, onlyTier);
1523
+ if (speedTier.applied === onlyTier) {
1524
+ const priced2 = priceWithRates(rates, usage, unpublishedThreshold(card, rates, routedAbove));
1525
+ return {
1526
+ ...priced2,
1527
+ totalUsd: priced2.inputUsd + priced2.outputUsd + priced2.cacheUsd,
1528
+ speedTier: {
1529
+ ...requested ? { requested } : {},
1530
+ applied: onlyTier,
1531
+ observed,
1532
+ assessment: "complete"
1533
+ },
1534
+ complete: true
1535
+ };
1536
+ }
1537
+ }
1538
+ const missingTiers = observed.filter(
1539
+ (name) => selectListPriceRates(card, name).speedTier.applied !== name
1540
+ );
1541
+ const incompleteReason = !breakdownMatchesUsage(usage, portions) ? "speed breakdown does not sum to the run usage" : missingTiers.length > 0 ? `no rates for observed speed tier ${missingTiers.join(", ")}` : void 0;
1542
+ if (incompleteReason !== void 0) {
1543
+ const fallback = computeBaseListPrice(card, usage, requestedTier, void 0, routedAbove);
1544
+ return {
1545
+ ...fallback,
1546
+ speedTier: { ...fallback.speedTier, observed, assessment: "incomplete", incompleteReason }
1547
+ };
1548
+ }
1549
+ const priced = [];
1550
+ const appliedNames = /* @__PURE__ */ new Set();
1551
+ for (const [name, portion] of tierEntries) {
1552
+ const { rates, speedTier } = selectListPriceRates(card, name);
1553
+ appliedNames.add(speedTier.applied);
1554
+ priced.push(
1555
+ priceWithRates(
1556
+ rates,
1557
+ speedPortionUsage(usage, portion),
1558
+ unpublishedThreshold(card, rates, routedAbove)
1559
+ )
1560
+ );
1561
+ }
1562
+ if (breakdown.unknown) {
1563
+ const { rates, speedTier } = selectListPriceRates(card, requested);
1564
+ appliedNames.add(speedTier.applied);
1565
+ priced.push(
1566
+ priceWithRates(
1567
+ rates,
1568
+ speedPortionUsage(usage, breakdown.unknown),
1569
+ unpublishedThreshold(card, rates, routedAbove)
1570
+ )
1571
+ );
1572
+ }
1573
+ const inputUsd = priced.reduce((total, portion) => total + portion.inputUsd, 0);
1574
+ const outputUsd = priced.reduce((total, portion) => total + portion.outputUsd, 0);
1575
+ const cacheUsd = priced.reduce((total, portion) => total + portion.cacheUsd, 0);
1576
+ const longContextPremium = PREMIUM_PRECEDENCE.find(
1577
+ (status) => priced.some((portion) => portion.longContextPremium === status)
1578
+ ) ?? "not_assessed";
1579
+ const unknownSpeed = breakdown.unknown !== void 0;
1580
+ return {
1581
+ inputUsd,
1582
+ outputUsd,
1583
+ cacheUsd,
1584
+ totalUsd: inputUsd + outputUsd + cacheUsd,
1585
+ speedTier: {
1586
+ ...requested ? { requested } : {},
1587
+ applied: appliedNames.size === 1 ? [...appliedNames].join("") : "mixed",
1588
+ observed,
1589
+ assessment: unknownSpeed ? "incomplete" : "complete",
1590
+ ...unknownSpeed ? { incompleteReason: "some responses reported no speed" } : {}
1591
+ },
1592
+ longContextPremium,
1593
+ complete: true
1594
+ };
1595
+ }
1596
+ function computeBaseListPrice(card, usage, requestedTier, speedBreakdown, routedAbove) {
1597
+ if (speedBreakdown) {
1598
+ const byBreakdown = computeSpeedBreakdownPrice(
1599
+ card,
1600
+ usage,
1601
+ requestedTier,
1602
+ speedBreakdown,
1603
+ routedAbove
1604
+ );
1605
+ if (byBreakdown) return byBreakdown;
1606
+ }
1607
+ const { rates, speedTier } = selectListPriceRates(card, requestedTier);
1608
+ const priced = priceWithRates(rates, usage, unpublishedThreshold(card, rates, routedAbove));
1609
+ const unverified = speedTier.applied !== STANDARD_SPEED_TIER;
1610
+ return {
1611
+ ...priced,
1612
+ totalUsd: priced.inputUsd + priced.outputUsd + priced.cacheUsd,
1613
+ speedTier: unverified ? { ...speedTier, assessment: "incomplete", incompleteReason: "served tier unverified" } : speedTier,
1614
+ complete: true
1615
+ };
1616
+ }
1617
+ function definesLongContext(card, routedAbove) {
1618
+ return routedAbove !== void 0 || card.longContext !== void 0 || Object.values(card.speedTiers ?? {}).some((tier) => tier.longContext !== void 0);
1619
+ }
1620
+ function isComplete(speedTier, premium, premiumDefined) {
1621
+ const tierFellBack = speedTier.requested !== void 0 && speedTier.requested !== STANDARD_SPEED_TIER && speedTier.requested !== speedTier.applied && // Evidence of the speed that ran prices the tier it observed, not a fallback.
1622
+ speedTier.assessment !== "complete";
1623
+ const speedUnassessed = speedTier.assessment === "incomplete";
1624
+ return !tierFellBack && !speedUnassessed && !(premium === "not_assessed" && premiumDefined);
1625
+ }
1626
+ function lowestLongContextThreshold(card, routedAbove) {
1627
+ const thresholds = [card, ...Object.values(card.speedTiers ?? {})].map((rates) => rates.longContext?.thresholdTokens).concat(routedAbove).filter((threshold) => threshold !== void 0);
1628
+ return thresholds.length > 0 ? Math.min(...thresholds) : void 0;
1629
+ }
1630
+ function computeListPrice(card, usage, requestedTier, options = {}) {
1631
+ const routedTier = routedSpeedTier(requestedTier, options.routed);
1632
+ const effectiveTier = routedTier.requested;
1633
+ const base = computeBaseListPrice(
1634
+ card,
1635
+ usage,
1636
+ effectiveTier,
1637
+ options.speedBreakdown,
1638
+ routedTier.unpublishedAbove
1639
+ );
1640
+ const premiumDefined = definesLongContext(card, routedTier.unpublishedAbove);
1641
+ const finish = (result) => {
1642
+ const speedTier = routedTier.reported ?? result.speedTier;
1643
+ return {
1644
+ ...result,
1645
+ speedTier,
1646
+ complete: isComplete(speedTier, result.longContextPremium, premiumDefined)
1647
+ };
1648
+ };
1649
+ if (usage.granularity === "request") return finish(base);
1650
+ const lowestThreshold = lowestLongContextThreshold(card, routedTier.unpublishedAbove);
1651
+ const runPrompt = longContextContextTokens(usage);
1652
+ const contradicted = (usage.requestEvidence?.requests ?? []).some(
1653
+ (request) => request.inputTokens > runPrompt
1654
+ );
1655
+ if (lowestThreshold !== void 0 && runPrompt <= lowestThreshold && !contradicted) {
1656
+ return finish({ ...base, longContextPremium: "below_threshold" });
1657
+ }
1658
+ const assessed = assessRequestLongContext(
1659
+ card,
1660
+ (speed) => {
1661
+ const { rates, speedTier } = selectListPriceRates(card, speed ?? effectiveTier);
1662
+ const reported = normalizeTierName(speed);
1663
+ return {
1664
+ rates,
1665
+ tier: speedTier.applied,
1666
+ ...reported && reported !== speedTier.applied ? { unresolvedTier: reported } : {}
1667
+ };
1668
+ },
1669
+ usage,
1670
+ usage.requestEvidence
1671
+ );
1672
+ if (!assessed) return finish(base);
1673
+ const routedAbove = routedTier.unpublishedAbove;
1674
+ const crossesUnpublished = routedAbove !== void 0 && (usage.requestEvidence?.requests ?? []).some((request) => request.inputTokens > routedAbove);
1675
+ const premium = crossesUnpublished && assessed.status !== "applied" ? {
1676
+ ...assessed,
1677
+ status: "not_assessed",
1678
+ incompleteReason: [
1679
+ assessed.incompleteReason,
1680
+ "no long-context rates for the routed speed tier"
1681
+ ].filter(Boolean).join("; ")
1682
+ } : assessed;
1683
+ const inputUsd = base.inputUsd + premium.inputUsd;
1684
+ const outputUsd = base.outputUsd + premium.outputUsd;
1685
+ const cacheUsd = base.cacheUsd + premium.cacheUsd;
1686
+ return finish({
1687
+ ...base,
1688
+ inputUsd,
1689
+ outputUsd,
1690
+ cacheUsd,
1691
+ totalUsd: inputUsd + outputUsd + cacheUsd,
1692
+ longContextPremium: premium.status,
1693
+ ...premium.incompleteReason ? { longContextIncompleteReason: premium.incompleteReason } : {}
1694
+ });
1695
+ }
1696
+
1697
+ // src/telemetry/model-evidence.ts
1698
+ var EVIDENCE_FIELDS = ["source", "unverifiedReason", "modelProvider", "observedModelId"];
1699
+ var MAX_EVIDENCE_FIELD_LENGTH = 128;
1700
+ function cleanField(value) {
1701
+ if (typeof value !== "string") return void 0;
1702
+ const trimmed = value.trim();
1703
+ return trimmed.length > 0 && trimmed.length <= MAX_EVIDENCE_FIELD_LENGTH ? trimmed : void 0;
1704
+ }
1705
+ function cleanModelEvidence(evidence) {
1706
+ if (!evidence) return void 0;
1707
+ const cleaned = {};
1708
+ for (const field of EVIDENCE_FIELDS) {
1709
+ const value = cleanField(evidence[field]);
1710
+ if (value) cleaned[field] = value;
1711
+ }
1712
+ return Object.keys(cleaned).length > 0 ? cleaned : void 0;
1713
+ }
1714
+ function modelEvidenceAvailability(evidence) {
1715
+ const cleaned = cleanModelEvidence(evidence);
1716
+ return cleaned ? { modelEvidence: cleaned } : {};
1717
+ }
1718
+ function readModelEvidence(availabilityJson) {
1719
+ if (!availabilityJson) return void 0;
1720
+ try {
1721
+ const parsed = JSON.parse(availabilityJson);
1722
+ const stored = parsed?.modelEvidence;
1723
+ return stored && typeof stored === "object" ? cleanModelEvidence(stored) : void 0;
1724
+ } catch {
1725
+ return void 0;
1726
+ }
1727
+ }
1728
+ function isAmbiguousModelEvidence(evidence) {
1729
+ return evidence?.unverifiedReason?.endsWith("_ambiguous") === true;
1730
+ }
1731
+
1732
+ // src/telemetry/observed-speed-usage.ts
1733
+ function observedSpeedAvailability(breakdown) {
1734
+ return breakdown ? { observedSpeed: breakdown } : {};
1735
+ }
1736
+ function readSpeedUsage(value) {
1737
+ if (typeof value !== "object" || value === null) return void 0;
1738
+ const record = value;
1739
+ const count = (key) => {
1740
+ const value2 = record[key];
1741
+ return typeof value2 === "number" && Number.isFinite(value2) ? value2 : void 0;
1742
+ };
1743
+ const inputTokens = count("inputTokens");
1744
+ const outputTokens = count("outputTokens");
1745
+ if (inputTokens === void 0 || outputTokens === void 0) return void 0;
1746
+ return {
1747
+ inputTokens,
1748
+ outputTokens,
1749
+ cacheReadTokens: count("cacheReadTokens"),
1750
+ cacheWriteTokens: count("cacheWriteTokens"),
1751
+ cacheWriteLongTokens: count("cacheWriteLongTokens")
1752
+ };
1753
+ }
1754
+ function readObservedSpeedBreakdown(availabilityJson) {
1755
+ if (!availabilityJson) return void 0;
1756
+ try {
1757
+ const parsed = JSON.parse(availabilityJson);
1758
+ const observed = parsed?.observedSpeed;
1759
+ if (typeof observed !== "object" || observed === null) return void 0;
1760
+ const tiers = {};
1761
+ for (const [name, value] of Object.entries(
1762
+ typeof observed.tiers === "object" && observed.tiers !== null ? observed.tiers : {}
1763
+ )) {
1764
+ const usage = readSpeedUsage(value);
1765
+ if (!usage) return void 0;
1766
+ tiers[name] = usage;
1767
+ }
1768
+ const unknown = observed.unknown === void 0 ? void 0 : readSpeedUsage(observed.unknown);
1769
+ if (observed.unknown !== void 0 && !unknown) return void 0;
1770
+ return { tiers, ...unknown ? { unknown } : {} };
1771
+ } catch {
1772
+ return void 0;
1773
+ }
1774
+ }
1775
+
1776
+ // src/telemetry/request-evidence-usage.ts
1777
+ function requestEvidenceAvailability(evidence) {
1778
+ return evidence && evidence.requests.length > 0 ? { requestEvidence: evidence } : {};
1779
+ }
1780
+ function readRequest(value) {
1781
+ if (typeof value !== "object" || value === null) return void 0;
1782
+ const record = value;
1783
+ const count = (key) => {
1784
+ const field = record[key];
1785
+ return typeof field === "number" && Number.isFinite(field) && field >= 0 ? field : void 0;
1786
+ };
1787
+ const inputTokens = count("inputTokens");
1788
+ const cachedInputTokens = count("cachedInputTokens");
1789
+ const cacheWriteTokens = count("cacheWriteTokens");
1790
+ const outputTokens = count("outputTokens");
1791
+ const cacheWriteLongTokens = count("cacheWriteLongTokens");
1792
+ if (inputTokens === void 0 || cachedInputTokens === void 0 || cacheWriteTokens === void 0 || outputTokens === void 0) {
1793
+ return void 0;
1794
+ }
1795
+ return {
1796
+ inputTokens,
1797
+ cachedInputTokens,
1798
+ cacheWriteTokens,
1799
+ ...cacheWriteLongTokens !== void 0 ? { cacheWriteLongTokens } : {},
1800
+ outputTokens,
1801
+ ...typeof record.speed === "string" && record.speed ? { speed: record.speed } : {}
1802
+ };
1803
+ }
1804
+ function parseRequestEvidence(value) {
1805
+ if (typeof value !== "object" || value === null) return void 0;
1806
+ const { requests, complete } = value;
1807
+ if (!Array.isArray(requests) || requests.length === 0 || typeof complete !== "boolean") {
1808
+ return void 0;
1809
+ }
1810
+ const parsed = [];
1811
+ for (const entry of requests) {
1812
+ const request = readRequest(entry);
1813
+ if (!request) return void 0;
1814
+ parsed.push(request);
1815
+ }
1816
+ return { requests: parsed, complete };
1817
+ }
1818
+ function readRequestEvidence(availabilityJson) {
1819
+ if (!availabilityJson) return void 0;
1820
+ try {
1821
+ const parsed = JSON.parse(availabilityJson);
1822
+ return parseRequestEvidence(parsed?.requestEvidence);
1823
+ } catch {
1824
+ return void 0;
1825
+ }
1826
+ }
1827
+
1828
+ // src/telemetry/requested-service-tier.ts
1829
+ function requestedServiceTierAvailability(serviceTier) {
1830
+ const tier = serviceTier?.trim();
1831
+ return tier ? { requested: { serviceTier: tier } } : {};
1832
+ }
1833
+ function readRequestedServiceTier(availabilityJson) {
1834
+ if (!availabilityJson) return void 0;
1835
+ try {
1836
+ const parsed = JSON.parse(availabilityJson);
1837
+ const tier = parsed?.requested?.serviceTier;
1838
+ return typeof tier === "string" && tier.trim() ? tier.trim() : void 0;
1839
+ } catch {
1840
+ return void 0;
1841
+ }
1842
+ }
1843
+
1844
+ // src/telemetry/worker.ts
1845
+ function resolveRegistryModelCandidates(provider, modelId) {
1846
+ const trimmed = modelId.trim();
1847
+ if (trimmed.length === 0) {
1848
+ return [];
1849
+ }
1850
+ const candidates = /* @__PURE__ */ new Set([trimmed]);
1851
+ const mappings = provider.modelMappings ?? {};
1852
+ const directMapped = mappings[trimmed];
1853
+ if (directMapped) {
1854
+ candidates.add(directMapped);
1855
+ } else {
1856
+ const lowered = trimmed.toLowerCase();
1857
+ for (const [alias, target] of Object.entries(mappings)) {
1858
+ if (alias.toLowerCase() === lowered && target) {
1859
+ candidates.add(target);
1860
+ break;
1861
+ }
1862
+ }
1863
+ }
1864
+ return Array.from(candidates);
1865
+ }
1866
+ function roundCostCents(value) {
1867
+ return Math.round(value * 1e6) / 1e6;
1868
+ }
1869
+ function findRegistryModel(provider, modelId) {
1870
+ const candidates = resolveRegistryModelCandidates(provider, modelId);
1871
+ for (const candidate of candidates) {
1872
+ const model = provider.models.find((entry) => entry.modelId === candidate);
1873
+ if (model) {
1874
+ return { model, resolvedModelId: candidate };
1875
+ }
1876
+ }
1877
+ return null;
1878
+ }
1879
+ function unverifiedModelSuffix(evidence) {
1880
+ return evidence?.unverifiedReason ? ` (model_unverified: ${evidence.unverifiedReason})` : "";
1881
+ }
1882
+ function optionalAttributionId(value) {
1883
+ if (typeof value !== "string") return null;
1884
+ const trimmed = value.trim();
1885
+ return trimmed.length > 0 ? trimmed : null;
1886
+ }
1887
+ function hasPricedTokenEvidence(invocation, evidence) {
1888
+ return [
1889
+ evidence?.inputTokens,
1890
+ evidence?.outputTokens,
1891
+ evidence?.cacheReadTokens,
1892
+ evidence?.cacheWriteTokens,
1893
+ invocation.tokensIn,
1894
+ invocation.tokensOut
1895
+ ].some((value) => typeof value === "number");
1896
+ }
1897
+ function declaresMeteredRates(model) {
1898
+ const schema = model.costSchema;
1899
+ if (!schema) return false;
1900
+ return typeof schema.prompt_per_1k === "number" || typeof schema.completion_per_1k === "number" || typeof schema.cache_read_per_1k === "number" || typeof schema.cache_write_per_1k === "number" || schema.extra_fees !== void 0 || typeof schema.billing_source === "string";
1901
+ }
1902
+ function findProviderAndModel(registry, providerId, modelId) {
1903
+ const provider = registry.providers.find((p) => p.providerId === providerId);
1904
+ if (!provider) return null;
1905
+ const resolvedModel = findRegistryModel(provider, modelId);
1906
+ if (!resolvedModel) return null;
1907
+ return { provider, ...resolvedModel };
1908
+ }
1909
+ function collectRegistrySnapshotsForEnrichment(db, preferredRegistryVersion) {
1910
+ const preferred = db.getRegistrySnapshot(preferredRegistryVersion);
1911
+ return preferred ? [preferred] : [];
1912
+ }
1913
+ var TelemetryWorker = class {
1914
+ constructor(db, options = {}) {
1915
+ this.db = db;
1916
+ this.options = options;
1917
+ }
1918
+ db;
1919
+ options;
1920
+ warn(message) {
1921
+ if (this.options.diagnostics === "silent") return;
1922
+ console.warn(message);
1923
+ }
1924
+ error(message, detail) {
1925
+ if (this.options.diagnostics === "silent") return;
1926
+ console.error(message, detail);
1927
+ }
1928
+ markUnpriced(invocation, reason) {
1929
+ const enrichedAt = Date.now();
1930
+ this.db.insertEnrichedInvocation({
1931
+ id: generateInvocationId(),
1932
+ invocationId: invocation.id,
1933
+ computedCostCents: null,
1934
+ pricingSource: "unavailable",
1935
+ enrichedAt,
1936
+ billingMode: null,
1937
+ usageData: JSON.stringify({
1938
+ inputTokens: invocation.tokensIn ?? 0,
1939
+ outputTokens: invocation.tokensOut ?? 0
1940
+ }),
1941
+ costSnapshot: null,
1942
+ pricingVersion: invocation.registryVersion,
1943
+ taskId: optionalAttributionId(invocation.taskId),
1944
+ competitionId: optionalAttributionId(invocation.competitionId) ?? optionalAttributionId(invocation.taskId),
1945
+ variantId: optionalAttributionId(invocation.variantId)
1946
+ });
1947
+ const identity = JSON.stringify({
1948
+ invocationId: invocation.id,
1949
+ pricingVersion: invocation.registryVersion,
1950
+ methodologyVersion: "api-equivalent-v1",
1951
+ status: "unavailable"
1952
+ });
1953
+ this.db.insertCostEvaluation({
1954
+ evaluationId: createHash2("sha256").update(identity).digest("hex"),
1955
+ invocationId: invocation.id,
1956
+ pricingVersion: invocation.registryVersion,
1957
+ methodologyVersion: "api-equivalent-v1",
1958
+ apiEquivalentCostUsd: null,
1959
+ providerReportedCostUsd: invocation.providerReportedCostUsd ?? null,
1960
+ comparisonDeltaUsd: null,
1961
+ currency: "USD",
1962
+ status: "unavailable",
1963
+ createdAt: enrichedAt
1964
+ });
1965
+ this.warn(reason);
1966
+ }
1967
+ /**
1968
+ * Enrich unenriched invocations with cost data
1969
+ * Returns number of rows enriched
1970
+ */
1971
+ async enrichInvocations(overrides) {
1972
+ const unenriched = this.db.getUnenrichedInvocations(1e3);
1973
+ let enrichedCount = 0;
1974
+ for (const invocation of unenriched) {
1975
+ try {
1976
+ const snapshots = collectRegistrySnapshotsForEnrichment(
1977
+ this.db,
1978
+ invocation.registryVersion
1979
+ );
1980
+ if (snapshots.length === 0) {
1981
+ this.markUnpriced(
1982
+ invocation,
1983
+ `Registry snapshot not found for version ${invocation.registryVersion}`
1984
+ );
1985
+ enrichedCount++;
1986
+ continue;
1987
+ }
1988
+ const reportedModelId = optionalAttributionId(invocation.reportedModelId);
1989
+ const evidence = this.db.getUsageEvidenceForInvocation(invocation.id);
1990
+ const modelEvidence = readModelEvidence(evidence?.availabilityJson);
1991
+ if (isAmbiguousModelEvidence(modelEvidence)) {
1992
+ this.markUnpriced(
1993
+ invocation,
1994
+ `Multiple models used${unverifiedModelSuffix(modelEvidence)}; cost unavailable`
1995
+ );
1996
+ enrichedCount++;
1997
+ continue;
1998
+ }
1999
+ const pricingModelIds = reportedModelId !== null && reportedModelId !== invocation.modelId ? modelEvidence?.source ? [reportedModelId] : [reportedModelId, invocation.modelId] : [invocation.modelId];
2000
+ let resolved = null;
2001
+ let resolvedPricingVersion = null;
2002
+ let providerSeen = false;
2003
+ let unpricedReason;
2004
+ for (const snapshot of snapshots) {
2005
+ const registry = parseJsonWithContext(
2006
+ snapshot.jsonBlob,
2007
+ "Model registry snapshot"
2008
+ );
2009
+ if (registry.providers.some((p) => p.providerId === invocation.providerId)) {
2010
+ providerSeen = true;
2011
+ }
2012
+ unpricedReason = findProviderAndModel(registry, invocation.providerId, invocation.modelId)?.model.unpricedReason;
2013
+ if (unpricedReason) break;
2014
+ for (const pricingModelId of pricingModelIds) {
2015
+ const candidate = findProviderAndModel(registry, invocation.providerId, pricingModelId);
2016
+ if (candidate) {
2017
+ resolved = candidate;
2018
+ resolvedPricingVersion = snapshot.registryVersion;
2019
+ break;
2020
+ }
2021
+ }
2022
+ if (resolved) break;
2023
+ }
2024
+ if (unpricedReason) {
2025
+ this.markUnpriced(invocation, unpricedReason);
2026
+ enrichedCount++;
2027
+ continue;
2028
+ }
2029
+ if (!resolved) {
2030
+ const reason = !providerSeen ? `Provider ${invocation.providerId} not found in registry` : `Model ${pricingModelIds.join(" / ")} not found for provider ${invocation.providerId}`;
2031
+ const unverified = unverifiedModelSuffix(modelEvidence);
2032
+ this.markUnpriced(invocation, `${reason}${unverified}`);
2033
+ enrichedCount++;
2034
+ continue;
2035
+ }
2036
+ const { model, resolvedModelId } = resolved;
2037
+ let pricingSource = model.costSchema?.pricing_source ?? "frontmatter";
2038
+ let pricingConfidence = model.costSchema?.pricing_confidence;
2039
+ let billingSource = model.costSchema?.billing_source;
2040
+ let rateCard = rateCardFromSnapshotCostSchema(model.costSchema ?? {});
2041
+ let extraFees = model.costSchema?.extra_fees ?? {};
2042
+ let billingMode = model.costSchema?.billing_mode ?? "metered";
2043
+ const overrideKey = `${invocation.providerId}:${invocation.modelId}`;
2044
+ const resolvedOverrideKey = `${invocation.providerId}:${resolvedModelId}`;
2045
+ const override = overrides?.[overrideKey] ?? (resolvedOverrideKey !== overrideKey ? overrides?.[resolvedOverrideKey] : void 0);
2046
+ if (override) {
2047
+ pricingSource = `override:${overrides?.[overrideKey] ? overrideKey : resolvedOverrideKey}`;
2048
+ rateCard = {
2049
+ inputPer1m: override.promptPer1k * 1e3,
2050
+ outputPer1m: override.completionPer1k * 1e3,
2051
+ ...rateCard.longContext ? { longContext: rateCard.longContext } : {}
2052
+ };
2053
+ extraFees = override.extraFees ?? {};
2054
+ billingMode = "metered";
2055
+ pricingConfidence = void 0;
2056
+ billingSource = void 0;
2057
+ }
2058
+ if (billingMode === "metered" && !override && !declaresMeteredRates(model)) {
2059
+ this.markUnpriced(
2060
+ invocation,
2061
+ `No pricing schema for ${invocation.providerId}/${resolvedModelId}; cost unavailable${unverifiedModelSuffix(modelEvidence)}`
2062
+ );
2063
+ enrichedCount++;
2064
+ continue;
2065
+ }
2066
+ if (billingMode === "metered" && !hasPricedTokenEvidence(invocation, evidence)) {
2067
+ this.markUnpriced(
2068
+ invocation,
2069
+ `No usage evidence for invocation ${invocation.id}; cost unavailable`
2070
+ );
2071
+ enrichedCount++;
2072
+ continue;
2073
+ }
2074
+ const perModelKeys = evidence?.extraDimensionsJson ? Object.keys(JSON.parse(evidence.extraDimensionsJson)).filter((key) => key.startsWith("modelUsage/")).map((key) => key.split("/")[1]) : [];
2075
+ if (billingMode === "metered" && new Set(perModelKeys).size > 1) {
2076
+ this.markUnpriced(
2077
+ invocation,
2078
+ "Multiple reported models: aggregate usage cannot be priced as one model"
2079
+ );
2080
+ enrichedCount++;
2081
+ continue;
2082
+ }
2083
+ const tokensIn = evidence?.inputTokens ?? invocation.tokensIn ?? 0;
2084
+ const tokensOut = evidence?.outputTokens ?? invocation.tokensOut ?? 0;
2085
+ const cacheReadTokens = evidence?.cacheReadTokens ?? 0;
2086
+ const cacheWriteTokens = evidence?.cacheWriteTokens ?? 0;
2087
+ const cacheWriteLongTokens = readLongCacheWriteTokens(evidence?.extraDimensionsJson);
2088
+ const usageGranularity = usageGranularityFromEventSemantics(evidence?.eventSemantics);
2089
+ const requestedServiceTier = readRequestedServiceTier(evidence?.availabilityJson);
2090
+ const observedSpeed = readObservedSpeedBreakdown(evidence?.availabilityJson);
2091
+ const requestEvidence = readRequestEvidence(evidence?.availabilityJson);
2092
+ const listPrice = computeListPrice(
2093
+ rateCard,
2094
+ {
2095
+ inputTokens: tokensIn,
2096
+ outputTokens: tokensOut,
2097
+ cacheReadTokens,
2098
+ cacheWriteTokens,
2099
+ cacheWriteLongTokens,
2100
+ cacheReadIncludedInInput: evidence?.cacheReadIncludedInInput === true,
2101
+ cacheWriteIncludedInInput: evidence?.cacheWriteIncludedInInput === true,
2102
+ granularity: usageGranularity,
2103
+ requestEvidence
2104
+ },
2105
+ requestedServiceTier,
2106
+ { speedBreakdown: observedSpeed, routed: model.costSchema?.routed_speed_tier }
2107
+ );
2108
+ const extraFeesCents = Object.values(extraFees).reduce(
2109
+ (sum, fee) => sum + (typeof fee === "number" ? fee * 100 : 0),
2110
+ 0
2111
+ );
2112
+ let costCents = null;
2113
+ if (billingMode === "metered") {
2114
+ costCents = roundCostCents(listPrice.totalUsd * 100 + extraFeesCents);
2115
+ } else if (!model.costSchema?.pricing_source && !override) {
2116
+ pricingSource = `non_metered:${billingMode}`;
2117
+ }
2118
+ const originalModelId = typeof invocation.requestedModelId === "string" && invocation.requestedModelId.trim().length > 0 ? invocation.requestedModelId : invocation.modelId;
2119
+ const usageData = JSON.stringify({
2120
+ inputTokens: tokensIn,
2121
+ outputTokens: tokensOut,
2122
+ cacheReadTokens,
2123
+ cacheWriteTokens,
2124
+ // Persisted so recalculation prices the long-duration writes the same way.
2125
+ ...cacheWriteLongTokens !== void 0 ? { cacheWriteLongTokens } : {},
2126
+ cacheReadTokensIncludedInInput: evidence?.cacheReadIncludedInInput === true,
2127
+ // Persisted so recalculation prices the record the same way.
2128
+ granularity: usageGranularity,
2129
+ // Persisted so recalculation reprices a fast run at the fast tier.
2130
+ ...requestedServiceTier ? { requestedServiceTier } : {},
2131
+ ...observedSpeed ? { observedSpeed } : {},
2132
+ ...requestEvidence ? { requestEvidence } : {},
2133
+ originalModelId,
2134
+ canonicalModelId: resolvedModelId,
2135
+ // Present only when the provider reported one, so a reader can tell
2136
+ // "priced against what ran" from "priced against what was asked for".
2137
+ ...reportedModelId !== null ? { reportedModelId } : {},
2138
+ ...modelEvidence?.source ? { modelEvidenceSource: modelEvidence.source } : {}
2139
+ });
2140
+ const inputCostCents = billingMode === "metered" ? roundCostCents(listPrice.inputUsd * 100) : 0;
2141
+ const outputCostCents = billingMode === "metered" ? roundCostCents(listPrice.outputUsd * 100) : 0;
2142
+ const costSnapshot = JSON.stringify({
2143
+ inputCostCents,
2144
+ outputCostCents,
2145
+ cacheCostCents: billingMode === "metered" ? roundCostCents(listPrice.cacheUsd * 100) : 0,
2146
+ toolCostCents: 0,
2147
+ infrastructureCostCents: 0,
2148
+ totalCostCents: costCents,
2149
+ currency: "USD",
2150
+ pricingSource,
2151
+ ...pricingConfidence ? { pricingConfidence } : {},
2152
+ billingMode,
2153
+ ...billingSource ? { billingSource } : {},
2154
+ // 'not_assessed' flags a run aggregate priced at flat rates: the
2155
+ // long-context premium may apply to requests inside it, so this cost
2156
+ // can understate the list price.
2157
+ longContextPremium: listPrice.longContextPremium,
2158
+ ...listPrice.longContextIncompleteReason ? { longContextIncompleteReason: listPrice.longContextIncompleteReason } : {},
2159
+ speedTier: listPrice.speedTier,
2160
+ originalModelId,
2161
+ canonicalModelId: resolvedModelId,
2162
+ ...reportedModelId !== null ? { reportedModelId } : {},
2163
+ ...modelEvidence?.source ? { modelEvidenceSource: modelEvidence.source } : {},
2164
+ pricingVersion: resolvedPricingVersion ?? invocation.registryVersion
2165
+ });
2166
+ const taskId = optionalAttributionId(invocation.taskId);
2167
+ const competitionId = optionalAttributionId(invocation.competitionId) ?? taskId;
2168
+ const variantId = optionalAttributionId(invocation.variantId);
2169
+ const enrichedId = generateInvocationId();
2170
+ const enriched = {
2171
+ id: enrichedId,
2172
+ invocationId: invocation.id,
2173
+ computedCostCents: costCents,
2174
+ pricingSource,
2175
+ enrichedAt: Date.now(),
2176
+ billingMode,
2177
+ usageData,
2178
+ costSnapshot,
2179
+ pricingVersion: resolvedPricingVersion ?? invocation.registryVersion,
2180
+ taskId,
2181
+ competitionId,
2182
+ variantId
2183
+ };
2184
+ this.db.insertEnrichedInvocation(enriched);
2185
+ const apiEquivalentCostUsd = costCents === null ? null : costCents / 100;
2186
+ const providerReportedCostUsd = invocation.providerReportedCostUsd ?? null;
2187
+ const evaluationIdentity = JSON.stringify({
2188
+ invocationId: invocation.id,
2189
+ evidenceId: evidence?.evidenceId ?? null,
2190
+ pricingVersion: resolvedPricingVersion ?? invocation.registryVersion,
2191
+ methodologyVersion: "api-equivalent-v1",
2192
+ apiEquivalentCostUsd,
2193
+ providerReportedCostUsd
2194
+ });
2195
+ this.db.insertCostEvaluation({
2196
+ evaluationId: createHash2("sha256").update(evaluationIdentity).digest("hex"),
2197
+ invocationId: invocation.id,
2198
+ evidenceId: evidence?.evidenceId ?? null,
2199
+ pricingVersion: resolvedPricingVersion ?? invocation.registryVersion,
2200
+ methodologyVersion: "api-equivalent-v1",
2201
+ apiEquivalentCostUsd,
2202
+ providerReportedCostUsd,
2203
+ comparisonDeltaUsd: apiEquivalentCostUsd !== null && providerReportedCostUsd !== null ? providerReportedCostUsd - apiEquivalentCostUsd : null,
2204
+ currency: model.costSchema?.currency ?? "USD",
2205
+ // A premium or tier the list price could not assess is partial, not fully priced.
2206
+ status: apiEquivalentCostUsd === null ? "unavailable" : listPrice.complete ? "complete" : "partial",
2207
+ createdAt: Date.now()
2208
+ });
2209
+ enrichedCount++;
2210
+ } catch (error) {
2211
+ this.error(
2212
+ `Failed to enrich invocation ${invocation.id}:`,
2213
+ error instanceof Error ? error.message : String(error)
2214
+ );
2215
+ }
2216
+ }
2217
+ return enrichedCount;
2218
+ }
2219
+ /**
2220
+ * Aggregate enriched invocations into daily cost buckets.
2221
+ * Returns number of daily aggregate rows upserted.
2222
+ */
2223
+ aggregateDaily() {
2224
+ return this.db.backfillDailyCostAggregations();
2225
+ }
2226
+ /**
2227
+ * Run full enrichment and aggregation cycle
2228
+ */
2229
+ async runCycle(overrides) {
2230
+ const enrichedCount = await this.enrichInvocations(overrides);
2231
+ const dailyAggregatedCount = this.aggregateDaily();
2232
+ return { enrichedCount, dailyAggregatedCount };
2233
+ }
2234
+ };
2235
+
2236
+ // src/telemetry/invocation-cost-enrichment.ts
2237
+ var PENDING_ENRICHMENT_HINT = "Invocations exist in this window but cost enrichment is pending or incomplete.";
2238
+ var NO_INVOCATIONS_HINT = "No model invocations recorded in this window.";
2239
+ var ZERO_USAGE_HINT = "Invocations were recorded but reported zero token usage; per-token cost is $0.";
2240
+ var MISSING_USAGE_HINT = "Invocations were recorded without usage evidence; their cost is unavailable, not $0.";
2241
+ var MISSING_PRICING_HINT = "Invocations reported usage but no pricing was available for their model; their cost is unavailable, not $0.";
2242
+ function pricingMetadata(pricing) {
2243
+ if (!pricing) return {};
2244
+ const pricingSource = pricingSourceFromRow(pricing);
2245
+ const pricingConfidence = pricingConfidenceFromRow(pricing);
2246
+ const billingSource = billingSourceFromRow(pricing);
2247
+ return {
2248
+ ...pricingSource ? { pricing_source: pricingSource } : {},
2249
+ ...pricingConfidence ? { pricing_confidence: pricingConfidence } : {},
2250
+ ...billingSource ? { billing_source: billingSource } : {}
2251
+ };
2252
+ }
2253
+ function passthroughPricingRow(parsed, providerId, modelId) {
2254
+ const target = passthroughResolver.getPassthroughTarget(
2255
+ providerId,
2256
+ modelId,
2257
+ parsed.providers[providerId]?.pricing,
2258
+ (provider, model) => parsed.providers[provider]?.pricing?.models?.[model] !== void 0
2259
+ );
2260
+ const upstreamRow = target && parsed.providers[target.provider]?.pricing?.models?.[target.model];
2261
+ if (!target || !upstreamRow) return void 0;
2262
+ const row = target.speed_tier ? pricingRowAtSpeedTier(upstreamRow, target.speed_tier) : upstreamRow;
2263
+ if (!snapshotCostSchemaFromRow(row)) return void 0;
2264
+ const tier = target.speed_tier ? upstreamRow.speed_tiers?.[target.speed_tier] : void 0;
2265
+ const unpublishedAbove = tier?.long_context_pricing ? void 0 : upstreamRow.long_context_pricing?.threshold_tokens;
2266
+ const routedSpeedTier2 = target.speed_tier ? {
2267
+ requested: target.speed_tier,
2268
+ applied: tier ? target.speed_tier : "standard",
2269
+ ...tier && unpublishedAbove !== void 0 ? { unpublishedLongContextAboveTokens: unpublishedAbove } : {}
2270
+ } : void 0;
2271
+ return {
2272
+ row,
2273
+ upstream: `${target.provider}/${target.model}`,
2274
+ ...routedSpeedTier2 ? { routedSpeedTier: routedSpeedTier2 } : {},
2275
+ ...target.note ? { note: target.note } : {}
2276
+ };
2277
+ }
2278
+ function buildInvocationRegistry(parsed) {
2279
+ const providers = Object.entries(parsed.providers).map(
2280
+ ([providerId, provider]) => ({
2281
+ providerId,
2282
+ displayName: provider.display_name,
2283
+ modelMappings: { ...provider.models.mappings },
2284
+ models: provider.models.available.map((model) => {
2285
+ const declared = provider.pricing?.models?.[model.id];
2286
+ const unpricedReason = declared?.unpriced_reason;
2287
+ const passthrough = declared?.non_metered || declared && snapshotCostSchemaFromRow(declared) ? void 0 : passthroughPricingRow(parsed, providerId, model.id);
2288
+ const pricing = passthrough?.row ?? declared;
2289
+ const per1kRates = pricing ? snapshotCostSchemaFromRow(pricing) : void 0;
2290
+ const metadata = passthrough ? {
2291
+ ...pricingSourceFromRow(passthrough.row) ? { pricing_source: pricingSourceFromRow(passthrough.row) } : {},
2292
+ resolution_route: `passthrough:${passthrough.upstream}`
2293
+ } : pricingMetadata(pricing);
2294
+ return {
2295
+ modelId: model.id,
2296
+ displayName: model.display_name,
2297
+ capabilities: model.capabilities,
2298
+ ...pricing?.non_metered ? {
2299
+ costSchema: {
2300
+ billing_mode: "non_metered",
2301
+ ...metadata,
2302
+ notes: pricing.notes
2303
+ }
2304
+ } : per1kRates ? {
2305
+ costSchema: {
2306
+ ...per1kRates,
2307
+ currency: provider.pricing?.currency ?? "USD",
2308
+ billing_mode: "metered",
2309
+ ...metadata,
2310
+ ...passthrough?.routedSpeedTier ? { routed_speed_tier: passthrough.routedSpeedTier } : {},
2311
+ notes: joinPricingNotes(
2312
+ pricing?.notes,
2313
+ passthrough?.note,
2314
+ passthrough?.routedSpeedTier?.applied === "standard" ? `${passthrough.routedSpeedTier.requested} requested, priced at standard.` : void 0
2315
+ )
2316
+ }
2317
+ } : {},
2318
+ ...unpricedReason ? { unpricedReason } : {}
2319
+ };
2320
+ })
2321
+ })
2322
+ );
2323
+ return buildRegistry(providers);
2324
+ }
2325
+ function prepareInvocationRegistry() {
2326
+ try {
2327
+ return buildInvocationRegistry(ProviderRegistry.getActiveRegistry());
2328
+ } catch (error) {
2329
+ debugLog(
2330
+ `[Telemetry] Active registry snapshot unavailable: ${error instanceof Error ? error.message : String(error)}`
2331
+ );
2332
+ return null;
2333
+ }
2334
+ }
2335
+ function captureInvocationRegistryVersion(db, prepared) {
2336
+ try {
2337
+ const registry = prepared === void 0 ? prepareInvocationRegistry() : prepared;
2338
+ if (!registry) return null;
2339
+ db.insertRegistrySnapshot({
2340
+ registryVersion: registry.registryVersion,
2341
+ createdAt: Date.now(),
2342
+ jsonBlob: JSON.stringify(registry)
2343
+ });
2344
+ return registry.registryVersion;
2345
+ } catch {
2346
+ return null;
2347
+ }
2348
+ }
2349
+ function ensureRegistrySnapshot(db) {
2350
+ return captureInvocationRegistryVersion(db) !== null;
2351
+ }
2352
+ function ensureRegistrySnapshotForPendingEnrichment(db, options) {
2353
+ const existingSnapshot = db.getLatestRegistrySnapshot();
2354
+ if (options.refreshRegistrySnapshot === false && existingSnapshot) {
2355
+ return true;
2356
+ }
2357
+ return ensureRegistrySnapshot(db);
2358
+ }
2359
+ async function ensurePendingInvocationCostEnrichment(db, options = {}) {
2360
+ const pendingCount = db.countUnenrichedInvocations();
2361
+ if (pendingCount === 0) {
2362
+ return { enrichedCount: 0, registrySnapshotEnsured: Boolean(db.getLatestRegistrySnapshot()) };
2363
+ }
2364
+ const registrySnapshotEnsured = ensureRegistrySnapshotForPendingEnrichment(db, options);
2365
+ if (!registrySnapshotEnsured) {
2366
+ return { enrichedCount: 0, registrySnapshotEnsured: false };
2367
+ }
2368
+ const worker = new TelemetryWorker(db, { diagnostics: "silent" });
2369
+ const enrichedCount = options.aggregate === false ? await worker.enrichInvocations() : (await worker.runCycle()).enrichedCount;
2370
+ return {
2371
+ enrichedCount,
2372
+ registrySnapshotEnsured: true
2373
+ };
2374
+ }
2375
+ function resolveUnpricedBreakdown(db, startTimestampMs, unpricedInvocationCount) {
2376
+ if (unpricedInvocationCount === 0) {
2377
+ return {};
2378
+ }
2379
+ const unpricedMissingPricingCount = Math.min(
2380
+ unpricedInvocationCount,
2381
+ db.countUnpricedWithTokenEvidenceSince(startTimestampMs)
2382
+ );
2383
+ return {
2384
+ unpricedCause: unpricedMissingPricingCount > 0 ? "missing_pricing" : "missing_usage",
2385
+ unpricedMissingPricingCount
2386
+ };
2387
+ }
2388
+ var UNPRICED_CAUSE_HINTS = {
2389
+ missing_pricing: MISSING_PRICING_HINT,
2390
+ missing_usage: MISSING_USAGE_HINT
2391
+ };
2392
+ function listInvocationProviderIdsSince(db, startTimestampMs) {
2393
+ try {
2394
+ const rows = db.db.prepare("SELECT DISTINCT provider_id FROM model_invocations WHERE timestamp >= ?").all(startTimestampMs);
2395
+ return rows.map((row) => row.provider_id.toLowerCase());
2396
+ } catch {
2397
+ return void 0;
2398
+ }
2399
+ }
2400
+ function summarizeInvocationCostVisibility(db, startTimestampMs) {
2401
+ const rawInvocationCount = db.countRawInvocationsSince(startTimestampMs);
2402
+ const enrichedInvocationCount = db.countEnrichedInvocationsSince(startTimestampMs);
2403
+ const pendingEnrichmentCount = Math.max(0, rawInvocationCount - enrichedInvocationCount);
2404
+ const unpricedInvocationCount = db.countUnpricedInvocationsSince(startTimestampMs);
2405
+ const unpricedBreakdown = resolveUnpricedBreakdown(db, startTimestampMs, unpricedInvocationCount);
2406
+ const unpricedCause = unpricedBreakdown.unpricedCause;
2407
+ const counts = {
2408
+ rawInvocationCount,
2409
+ enrichedInvocationCount,
2410
+ pendingEnrichmentCount,
2411
+ unpricedInvocationCount,
2412
+ ...unpricedBreakdown
2413
+ };
2414
+ if (rawInvocationCount === 0) {
2415
+ return {
2416
+ ...counts,
2417
+ visibility: "no_invocations",
2418
+ hint: NO_INVOCATIONS_HINT
2419
+ };
2420
+ }
2421
+ if (pendingEnrichmentCount > 0) {
2422
+ return {
2423
+ ...counts,
2424
+ visibility: "pending_enrichment",
2425
+ hint: PENDING_ENRICHMENT_HINT
2426
+ };
2427
+ }
2428
+ const hasTokenUsage = db.hasTokenUsageSince(startTimestampMs);
2429
+ if (!hasTokenUsage) {
2430
+ return {
2431
+ ...counts,
2432
+ visibility: "zero_usage",
2433
+ // A window with no positive usage is a real $0 only when every row was
2434
+ // priced; unpriced rows get the explanation their cause calls for.
2435
+ hint: unpricedCause ? UNPRICED_CAUSE_HINTS[unpricedCause] : ZERO_USAGE_HINT
2436
+ };
2437
+ }
2438
+ return {
2439
+ ...counts,
2440
+ visibility: "enriched",
2441
+ // Fully enriched is not fully priced. A window whose usage was positive
2442
+ // still needs the pricing gap named, or the only thing an operator sees is
2443
+ // a bare count of rows the total left out.
2444
+ ...unpricedCause ? { hint: UNPRICED_CAUSE_HINTS[unpricedCause] } : {}
2445
+ };
2446
+ }
2447
+
2448
+ // src/telemetry/cost-projection.ts
2449
+ function availableCostAmount(amount, unit, source, detail) {
2450
+ return {
2451
+ amount,
2452
+ unit,
2453
+ status: "available",
2454
+ source,
2455
+ ...detail ? { detail } : {}
2456
+ };
2457
+ }
2458
+ function unavailableCostAmount(unit, source, detail) {
2459
+ return {
2460
+ amount: null,
2461
+ unit,
2462
+ status: "unavailable",
2463
+ source,
2464
+ ...detail ? { detail } : {}
2465
+ };
2466
+ }
2467
+ function errorCostAmount(unit, source, error, detail) {
2468
+ return {
2469
+ amount: null,
2470
+ unit,
2471
+ status: "error",
2472
+ source,
2473
+ error,
2474
+ ...detail ? { detail } : {}
2475
+ };
2476
+ }
2477
+ function isAvailableCostAmount(value) {
2478
+ return value.status === "available";
2479
+ }
2480
+ function costAmountOrNull(value) {
2481
+ return value.status === "available" ? value.amount : null;
2482
+ }
2483
+ var PENDING_HINT = "Invocations exist in this window but cost enrichment is pending; totals may under-report until enrichment completes.";
2484
+ var FAILED_HINT = "Invocations exist in this window but cost enrichment could not run (no pricing snapshot available); showing raw counts only.";
2485
+ var RAW_ZERO_USAGE_HINT = "Invocations were recorded but reported zero token usage; per-token cost is $0.";
2486
+ var NO_INVOCATIONS_HINT2 = "No model invocations recorded in this window.";
2487
+ function mapVisibilityToState(summary, attempt) {
2488
+ switch (summary.visibility) {
2489
+ case "enriched":
2490
+ return "enriched";
2491
+ case "zero_usage":
2492
+ return "raw";
2493
+ case "no_invocations":
2494
+ return "enriched";
2495
+ case "pending_enrichment":
2496
+ if (attempt && attempt.registrySnapshotEnsured === false) {
2497
+ return "failed";
2498
+ }
2499
+ return "pending";
2500
+ }
2501
+ }
2502
+ function resolveHint(state, summary) {
2503
+ switch (state) {
2504
+ case "pending":
2505
+ return PENDING_HINT;
2506
+ case "failed":
2507
+ return FAILED_HINT;
2508
+ case "raw":
2509
+ return summary.hint ?? RAW_ZERO_USAGE_HINT;
2510
+ case "enriched":
2511
+ if (summary.rawInvocationCount === 0) {
2512
+ return NO_INVOCATIONS_HINT2;
2513
+ }
2514
+ return summary.hint;
2515
+ }
2516
+ }
2517
+ function projectCostVisibility(db, startTimestampMs, attempt) {
2518
+ const summary = summarizeInvocationCostVisibility(db, startTimestampMs);
2519
+ return fromVisibilitySummary(summary, attempt);
2520
+ }
2521
+ function fromVisibilitySummary(summary, attempt) {
2522
+ const state = mapVisibilityToState(summary, attempt);
2523
+ const totalsMayUnderReport = state === "pending" || state === "failed" || summary.unpricedInvocationCount > 0;
2524
+ const hint = resolveHint(state, summary);
2525
+ return {
2526
+ rawInvocationCount: summary.rawInvocationCount,
2527
+ enrichedInvocationCount: summary.enrichedInvocationCount,
2528
+ pendingEnrichmentCount: summary.pendingEnrichmentCount,
2529
+ unpricedInvocationCount: summary.unpricedInvocationCount,
2530
+ ...summary.unpricedCause ? { unpricedCause: summary.unpricedCause } : {},
2531
+ ...summary.unpricedMissingPricingCount !== void 0 ? { unpricedMissingPricingCount: summary.unpricedMissingPricingCount } : {},
2532
+ state,
2533
+ visibility: summary.visibility,
2534
+ totalsMayUnderReport,
2535
+ ...hint ? { hint } : {}
2536
+ };
2537
+ }
2538
+ function formatCostProjectionNotice(projection) {
2539
+ const unpriced = formatUnpricedClause(
2540
+ projection.unpricedInvocationCount,
2541
+ projection.unpricedMissingPricingCount
2542
+ );
2543
+ if (projection.state === "enriched" && projection.rawInvocationCount === 0) {
2544
+ return NO_INVOCATIONS_HINT2;
2545
+ }
2546
+ if (projection.state === "enriched") {
2547
+ return unpriced;
2548
+ }
2549
+ const counts = projection.pendingEnrichmentCount > 0 ? ` (${projection.pendingEnrichmentCount} pending of ${projection.rawInvocationCount} raw)` : projection.rawInvocationCount > 0 ? ` (${projection.rawInvocationCount} raw)` : "";
2550
+ return [`${projection.hint ?? ""}${counts}`.trim(), unpriced].filter(Boolean).join("; ") || void 0;
2551
+ }
2552
+ function formatUnpricedClause(unpricedInvocationCount, missingPricingCount) {
2553
+ if (unpricedInvocationCount <= 0) {
2554
+ return void 0;
2555
+ }
2556
+ const excluded = "(cost unavailable, excluded from totals)";
2557
+ const missingUsageCount = missingPricingCount === void 0 ? void 0 : unpricedInvocationCount - missingPricingCount;
2558
+ if (missingPricingCount !== void 0 && missingPricingCount > 0 && missingUsageCount) {
2559
+ return `${unpricedInvocationCount} invocations unpriced: ${missingPricingCount} reported usage but no pricing was available; ${missingUsageCount} recorded without usage evidence ${excluded}`;
2560
+ }
2561
+ const noun = unpricedInvocationCount === 1 ? "invocation" : "invocations";
2562
+ const missing = missingPricingCount === void 0 ? "without usage evidence or pricing" : missingPricingCount > 0 ? "reported usage but no pricing was available" : "without usage evidence";
2563
+ return `${unpricedInvocationCount} ${noun} ${missing} ${excluded}`;
2564
+ }
2565
+
2566
+ // src/telemetry/imported-cost-records.ts
2567
+ function loadImportedCostRecords(imported, local, projectSyncId, startMs, endExclusiveMs) {
2568
+ const scope = `r.scope_key = 'production'${projectSyncId ? " AND b.project_sync_id = ?" : ""}`;
2569
+ const projectArgs = projectSyncId ? [projectSyncId] : [];
2570
+ const bounds = [];
2571
+ const boundArgs = [];
2572
+ if (startMs !== void 0) {
2573
+ bounds.push("CAST(json_extract(r.payload_json, '$.timestamp') AS REAL) >= ?");
2574
+ boundArgs.push(startMs);
2575
+ }
2576
+ if (endExclusiveMs !== void 0) {
2577
+ bounds.push("CAST(json_extract(r.payload_json, '$.timestamp') AS REAL) < ?");
2578
+ boundArgs.push(endExclusiveMs);
2579
+ }
2580
+ const invocations = imported.prepare(`
2581
+ WITH latest AS (
2582
+ SELECT MAX(r.rowid) as rowId, MIN(r.rowid) as firstRowId
2583
+ FROM portability_records r
2584
+ JOIN portability_bundles b ON b.bundle_id = r.bundle_id
2585
+ WHERE ${scope} AND r.record_type = 'invocation'
2586
+ GROUP BY json_extract(r.payload_json, '$.invocationId')
2587
+ )
2588
+ SELECT r.record_type as recordType, r.payload_json as payloadJson,
2589
+ json_extract(r.payload_json, '$.invocationId') as invocationId
2590
+ FROM latest JOIN portability_records r ON r.rowid = latest.rowId
2591
+ WHERE 1 = 1 ${bounds.map((bound) => `AND ${bound}`).join(" ")}
2592
+ ORDER BY latest.firstRowId
2593
+ `).all(...projectArgs, ...boundArgs);
2594
+ const selectedIds = [
2595
+ ...new Set(
2596
+ invocations.map((row) => row.invocationId).filter((id) => typeof id === "string")
2597
+ )
2598
+ ];
2599
+ if (selectedIds.length === 0) return [];
2600
+ const localIds = new Set(
2601
+ local.prepare(`
2602
+ SELECT id FROM model_invocations WHERE id IN (SELECT value FROM json_each(?))
2603
+ `).all(JSON.stringify(selectedIds)).map((row) => row.id)
2604
+ );
2605
+ const remoteIds = selectedIds.filter((id) => !localIds.has(id));
2606
+ if (remoteIds.length === 0) return [];
2607
+ const evidence = imported.prepare(`
2608
+ SELECT r.record_type as recordType, r.payload_json as payloadJson
2609
+ FROM portability_records r
2610
+ JOIN portability_bundles b ON b.bundle_id = r.bundle_id
2611
+ WHERE ${scope} AND r.record_type IN ('usage', 'costEvaluation')
2612
+ AND json_extract(r.payload_json, '$.invocationId')
2613
+ IN (SELECT value FROM json_each(?))
2614
+ `).all(...projectArgs, JSON.stringify(remoteIds));
2615
+ return [
2616
+ ...invocations.filter(
2617
+ (row) => typeof row.invocationId === "string" && !localIds.has(row.invocationId)
2618
+ ),
2619
+ ...evidence
2620
+ ];
2621
+ }
2622
+
2623
+ // src/telemetry/invocation-quality.ts
2624
+ var SAFE_SQL_ALIAS = /^[A-Za-z_][A-Za-z0-9_]*$/u;
2625
+ function invocationProvenanceClauses(alias) {
2626
+ const prefix = sqlPrefix(alias);
2627
+ return [
2628
+ `${prefix}provider_id <> 'unknown'`,
2629
+ `(${prefix}execution_source IS NULL OR ${prefix}execution_source = 'live')`
2630
+ ];
2631
+ }
2632
+ function invocationMetricQualityClauses(alias) {
2633
+ const prefix = sqlPrefix(alias);
2634
+ return [
2635
+ `NOT (${prefix}total_time_ms IS NOT NULL AND ${prefix}total_time_ms < 1000 AND ${prefix}tokens_out IS NULL)`,
2636
+ `NOT (
2637
+ ${prefix}tokens_out IS NOT NULL
2638
+ AND ${prefix}tokens_out > 0
2639
+ AND ${prefix}time_first_token_ms IS NOT NULL
2640
+ AND ${prefix}time_last_token_ms IS NOT NULL
2641
+ AND ${prefix}time_last_token_ms > ${prefix}time_first_token_ms
2642
+ AND ((${prefix}tokens_out * 1000.0) / (${prefix}time_last_token_ms - ${prefix}time_first_token_ms)) > 1000
2643
+ )`
2644
+ ];
2645
+ }
2646
+ function invocationAnalyticsQualityClauses(alias) {
2647
+ return [...invocationProvenanceClauses(alias), ...invocationMetricQualityClauses(alias)];
2648
+ }
2649
+ function invocationProvenanceWhereSql(alias) {
2650
+ return clausesToAndSql(invocationProvenanceClauses(alias));
2651
+ }
2652
+ function isInvocationMetricQualityRow(row) {
2653
+ if (row.total_time_ms !== null && row.total_time_ms !== void 0 && row.total_time_ms < 1e3) {
2654
+ if (row.tokens_out === null || row.tokens_out === void 0) return false;
2655
+ }
2656
+ const tokensOut = row.tokens_out;
2657
+ const firstTokenMs = row.time_first_token_ms;
2658
+ const lastTokenMs = row.time_last_token_ms;
2659
+ if (typeof tokensOut === "number" && tokensOut > 0 && typeof firstTokenMs === "number" && typeof lastTokenMs === "number" && lastTokenMs > firstTokenMs) {
2660
+ const decodeTokensPerSecond = tokensOut * 1e3 / (lastTokenMs - firstTokenMs);
2661
+ if (decodeTokensPerSecond > 1e3) return false;
2662
+ }
2663
+ return true;
2664
+ }
2665
+ function clausesToAndSql(clauses) {
2666
+ if (clauses.length === 0) return "";
2667
+ return clauses.map((clause) => `AND ${clause}`).join("\n");
2668
+ }
2669
+ function sqlPrefix(alias) {
2670
+ if (alias === void 0) return "";
2671
+ if (!SAFE_SQL_ALIAS.test(alias)) {
2672
+ throw new Error(`Invalid SQL alias: ${alias}`);
2673
+ }
2674
+ return `${alias}.`;
2675
+ }
2676
+
2677
+ // src/telemetry/model-invocations-db.ts
2678
+ var ENRICHED_INVOCATIONS_SOURCE = "enriched_invocations";
2679
+ var EXECUTION_COST_COLUMNS = `
2680
+ e.invocation_id,
2681
+ i.timestamp,
2682
+ i.provider_id,
2683
+ i.model_id,
2684
+ e.usage_data,
2685
+ e.cost_snapshot,
2686
+ e.computed_cost_cents,
2687
+ e.billing_mode,
2688
+ e.pricing_version,
2689
+ e.pricing_source,
2690
+ e.task_id,
2691
+ e.competition_id,
2692
+ i.agent_role,
2693
+ e.variant_id
2694
+ `;
2695
+ function parseExecutionCostUsage(usageData) {
2696
+ return parseJsonWithContext(usageData, "Execution usage data");
2697
+ }
2698
+ function resolveExecutionCostSnapshot(row) {
2699
+ const costSnapshot = row.cost_snapshot ? parseJsonWithContext(row.cost_snapshot, "Cost snapshot") : null;
2700
+ return resolveSnapshotFirstCostSnapshot({
2701
+ costSnapshot,
2702
+ computedCostCents: row.computed_cost_cents,
2703
+ billingMode: row.billing_mode
2704
+ });
2705
+ }
2706
+ function projectExecutionCostRecord(row, usage, costSnapshot) {
2707
+ return {
2708
+ executionId: row.invocation_id,
2709
+ timestamp: new Date(row.timestamp),
2710
+ provider: row.provider_id,
2711
+ model: row.model_id,
2712
+ usage,
2713
+ pricingVersion: row.pricing_version || "unknown",
2714
+ pricingSource: row.pricing_source || "xml",
2715
+ costSnapshot,
2716
+ computedCostCents: row.computed_cost_cents ?? void 0,
2717
+ taskId: row.task_id ?? void 0,
2718
+ competitionId: row.competition_id ?? void 0,
2719
+ agentRole: row.agent_role ?? void 0,
2720
+ variantId: row.variant_id ?? void 0
2721
+ };
2722
+ }
2723
+ function createInvocationAnalyticsViews(db) {
2724
+ const views = [
2725
+ {
2726
+ name: "invocation_metrics_v",
2727
+ ddl: `
2728
+ CREATE VIEW invocation_metrics_v AS
2729
+ WITH ranked_usage AS (
2730
+ SELECT
2731
+ u.*,
2732
+ ROW_NUMBER() OVER (
2733
+ PARTITION BY u.invocation_id
2734
+ ORDER BY
2735
+ CASE u.event_semantics
2736
+ WHEN 'final' THEN 4
2737
+ WHEN 'cumulative' THEN 3
2738
+ WHEN 'logical_run_aggregate' THEN 2
2739
+ ELSE 1
2740
+ END DESC,
2741
+ u.created_at DESC,
2742
+ u.evidence_id DESC
2743
+ ) AS preference_rank,
2744
+ SUM(
2745
+ CASE WHEN u.event_semantics IN ('final', 'cumulative', 'logical_run_aggregate')
2746
+ THEN 1 ELSE 0 END
2747
+ ) OVER (PARTITION BY u.invocation_id) AS terminal_count
2748
+ FROM usage_evidence u
2749
+ ),
2750
+ usage_reduced AS (
2751
+ SELECT
2752
+ invocation_id,
2753
+ input_tokens,
2754
+ output_tokens,
2755
+ cache_read_tokens,
2756
+ cache_write_tokens,
2757
+ reasoning_tokens,
2758
+ tool_tokens,
2759
+ cache_read_included_in_input,
2760
+ cache_write_included_in_input,
2761
+ provenance AS usage_provenance,
2762
+ event_semantics,
2763
+ availability_json
2764
+ FROM ranked_usage
2765
+ WHERE terminal_count > 0 AND preference_rank = 1
2766
+
2767
+ UNION ALL
2768
+
2769
+ SELECT
2770
+ invocation_id,
2771
+ SUM(input_tokens),
2772
+ SUM(output_tokens),
2773
+ SUM(cache_read_tokens),
2774
+ SUM(cache_write_tokens),
2775
+ SUM(reasoning_tokens),
2776
+ SUM(tool_tokens),
2777
+ MAX(cache_read_included_in_input),
2778
+ MAX(cache_write_included_in_input),
2779
+ 'derived',
2780
+ 'delta',
2781
+ '{}'
2782
+ FROM ranked_usage
2783
+ WHERE terminal_count = 0
2784
+ GROUP BY invocation_id
2785
+ ),
2786
+ ranked_cost AS (
2787
+ SELECT
2788
+ c.*,
2789
+ ROW_NUMBER() OVER (
2790
+ PARTITION BY c.invocation_id
2791
+ ORDER BY c.created_at DESC, c.evaluation_id DESC
2792
+ ) AS cost_rank
2793
+ FROM cost_evaluations c
2794
+ )
2795
+ SELECT
2796
+ i.id AS invocation_id,
2797
+ i.project_id,
2798
+ i.lab_id,
2799
+ i.lab_run_id,
2800
+ i.run_id,
2801
+ i.task_id,
2802
+ i.competition_id,
2803
+ i.variant_id,
2804
+ i.attempt_id,
2805
+ i.attempt_ordinal,
2806
+ i.agent_role,
2807
+ i.provider_id,
2808
+ i.model_id,
2809
+ i.status,
2810
+ i.error_code,
2811
+ i.timestamp,
2812
+ i.time_request_sent_ms,
2813
+ i.time_first_token_ms,
2814
+ i.time_last_token_ms,
2815
+ i.time_to_first_token_ms,
2816
+ i.total_time_ms,
2817
+ COALESCE(u.input_tokens, i.tokens_in) AS input_tokens,
2818
+ COALESCE(u.output_tokens, i.tokens_out) AS output_tokens,
2819
+ u.cache_read_tokens,
2820
+ u.cache_write_tokens,
2821
+ u.reasoning_tokens,
2822
+ u.tool_tokens,
2823
+ u.cache_read_included_in_input,
2824
+ u.cache_write_included_in_input,
2825
+ u.usage_provenance,
2826
+ u.event_semantics,
2827
+ u.availability_json,
2828
+ COALESCE(c.api_equivalent_cost_usd, i.build_calculated_cost_usd) AS api_equivalent_cost_usd,
2829
+ COALESCE(c.provider_reported_cost_usd, i.provider_reported_cost_usd) AS provider_reported_cost_usd,
2830
+ c.comparison_delta_usd,
2831
+ COALESCE(
2832
+ c.status,
2833
+ CASE WHEN i.build_calculated_cost_usd IS NOT NULL THEN 'complete' ELSE 'unavailable' END
2834
+ ) AS cost_status,
2835
+ c.pricing_version,
2836
+ c.methodology_version
2837
+ FROM model_invocations i
2838
+ LEFT JOIN usage_reduced u ON u.invocation_id = i.id
2839
+ LEFT JOIN ranked_cost c ON c.invocation_id = i.id AND c.cost_rank = 1
2840
+ `
2841
+ },
2842
+ {
2843
+ name: "task_invocation_rollup_v",
2844
+ ddl: `
2845
+ CREATE VIEW task_invocation_rollup_v AS
2846
+ SELECT
2847
+ project_id,
2848
+ lab_id,
2849
+ lab_run_id,
2850
+ run_id,
2851
+ task_id,
2852
+ MAX(timestamp) AS last_invocation_ms,
2853
+ COUNT(*) AS invocation_count,
2854
+ SUM(CASE WHEN input_tokens IS NOT NULL AND output_tokens IS NOT NULL THEN 1 ELSE 0 END)
2855
+ AS usage_reported_count,
2856
+ SUM(
2857
+ CASE WHEN cost_status = 'complete' AND api_equivalent_cost_usd IS NOT NULL
2858
+ THEN 1 ELSE 0 END
2859
+ ) AS priced_count,
2860
+ MIN(time_request_sent_ms) AS provider_started_ms,
2861
+ MAX(time_last_token_ms) AS provider_finished_ms,
2862
+ SUM(total_time_ms) AS provider_sum_ms,
2863
+ SUM(input_tokens) AS input_tokens,
2864
+ SUM(output_tokens) AS output_tokens,
2865
+ SUM(cache_read_tokens) AS cache_read_tokens,
2866
+ SUM(cache_write_tokens) AS cache_write_tokens,
2867
+ SUM(reasoning_tokens) AS reasoning_tokens,
2868
+ SUM(tool_tokens) AS tool_tokens,
2869
+ SUM(api_equivalent_cost_usd) AS api_equivalent_cost_usd,
2870
+ SUM(provider_reported_cost_usd) AS provider_reported_cost_usd
2871
+ FROM invocation_metrics_v
2872
+ GROUP BY project_id, lab_id, lab_run_id, run_id, task_id
2873
+ `
2874
+ }
2875
+ ];
2876
+ const normalizeDdl = (value) => value.replace(/\s+/g, " ").trim().toLowerCase();
2877
+ const staleViews = views.filter((view) => {
2878
+ const existing = db.prepare("SELECT sql FROM sqlite_master WHERE type = 'view' AND name = ?").get(view.name);
2879
+ return !existing?.sql || normalizeDdl(existing.sql) !== normalizeDdl(view.ddl);
2880
+ });
2881
+ if (staleViews.length > 0) {
2882
+ try {
2883
+ db.exec("BEGIN IMMEDIATE");
2884
+ for (const view of staleViews) {
2885
+ db.exec(`DROP VIEW IF EXISTS ${view.name}`);
2886
+ db.exec(view.ddl);
2887
+ }
2888
+ db.exec("COMMIT");
2889
+ } catch (error) {
2890
+ try {
2891
+ db.exec("ROLLBACK");
2892
+ } catch {
2893
+ }
2894
+ emitWarning(
2895
+ "WS-003",
2896
+ {
2897
+ taskId: "system",
2898
+ subsystem: "telemetry",
2899
+ executionPhase: "setup"
2900
+ },
2901
+ {
2902
+ severity: "WARN" /* WARN */,
2903
+ metadata: {
2904
+ operation: "create_invocation_analytics_views",
2905
+ views: staleViews.map((view) => view.name).join(","),
2906
+ error: error instanceof Error ? error.message : String(error)
2907
+ }
2908
+ }
2909
+ );
2910
+ }
2911
+ }
2912
+ for (const view of views) {
2913
+ try {
2914
+ db.prepare(`SELECT * FROM ${view.name} LIMIT 0`).all();
2915
+ } catch (error) {
2916
+ emitWarning(
2917
+ "WS-003",
2918
+ {
2919
+ taskId: "system",
2920
+ subsystem: "telemetry",
2921
+ executionPhase: "setup"
2922
+ },
2923
+ {
2924
+ severity: "WARN" /* WARN */,
2925
+ metadata: {
2926
+ operation: "verify_invocation_analytics_view",
2927
+ view: view.name,
2928
+ error: error instanceof Error ? error.message : String(error)
2929
+ }
2930
+ }
2931
+ );
2932
+ }
2933
+ }
2934
+ }
2935
+ var ALLOWED_TABLE_NAMES = [
2936
+ "model_invocations",
2937
+ "registry_snapshots",
2938
+ "enriched_invocations",
2939
+ "usage_evidence",
2940
+ "cost_evaluations",
2941
+ "aggregates_hourly",
2942
+ "cost_aggregations_daily"
2943
+ ];
2944
+ var SAFE_SQL_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/u;
2945
+ var SAFE_COLUMN_DEFINITION = /^(TEXT|INTEGER|REAL|BLOB|NUMERIC)$/iu;
2946
+ function validateTableName(tableName) {
2947
+ if (!ALLOWED_TABLE_NAMES.includes(tableName)) {
2948
+ throw new Error(
2949
+ `Invalid table name: "${tableName}". Only the following tables are allowed: ${ALLOWED_TABLE_NAMES.join(", ")}`
2950
+ );
2951
+ }
2952
+ return tableName;
2953
+ }
2954
+ function validateSqlIdentifier(value, label) {
2955
+ if (!SAFE_SQL_IDENTIFIER.test(value)) {
2956
+ throw new Error(`Invalid SQL ${label}: ${value}`);
2957
+ }
2958
+ return value;
2959
+ }
2960
+ function validateColumnDefinition(value) {
2961
+ if (!SAFE_COLUMN_DEFINITION.test(value)) {
2962
+ throw new Error(`Invalid SQL column definition: ${value}`);
2963
+ }
2964
+ return value;
2965
+ }
2966
+ function snakeToCamel(str) {
2967
+ return str.replace(/_([a-z])/g, (_match, letter) => letter.toUpperCase());
2968
+ }
2969
+ function convertRowToCamelCase(row) {
2970
+ if (!row) return null;
2971
+ const result = {};
2972
+ for (const key in row) {
2973
+ result[snakeToCamel(key)] = row[key];
2974
+ }
2975
+ return result;
2976
+ }
2977
+ function sumNullableEvidence(rows, key) {
2978
+ const values = rows.map((row) => row[key]).filter((value) => typeof value === "number");
2979
+ return values.length === 0 ? null : values.reduce((sum, value) => sum + value, 0);
2980
+ }
2981
+ function reducePortableUsage(rows) {
2982
+ if (!rows || rows.length === 0) return void 0;
2983
+ const final = rows.filter(
2984
+ (row) => ["final", "cumulative", "logical_run_aggregate"].includes(String(row.eventSemantics))
2985
+ ).sort((left, right) => Number(left.createdAt ?? 0) - Number(right.createdAt ?? 0)).at(-1);
2986
+ if (final) return final;
2987
+ const summed = { ...rows.at(-1), eventSemantics: "delta" };
2988
+ for (const key of [
2989
+ "inputTokens",
2990
+ "outputTokens",
2991
+ "cacheReadTokens",
2992
+ "cacheWriteTokens",
2993
+ "reasoningTokens",
2994
+ "toolTokens"
2995
+ ]) {
2996
+ const values = rows.map((row) => row[key]).filter((value) => typeof value === "number");
2997
+ summed[key] = values.length === 0 ? null : values.reduce((sum, value) => sum + value, 0);
2998
+ }
2999
+ return summed;
3000
+ }
3001
+ var DAY_MS = 24 * 60 * 60 * 1e3;
3002
+ function utcDateStartMs(date) {
3003
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/u.exec(date);
3004
+ if (!match) {
3005
+ throw new Error(`Invalid date (expected YYYY-MM-DD): ${date}`);
3006
+ }
3007
+ const year = Number(match[1]);
3008
+ const month = Number(match[2]);
3009
+ const day = Number(match[3]);
3010
+ const ms = Date.UTC(year, month - 1, day);
3011
+ const check = new Date(ms);
3012
+ if (check.getUTCFullYear() !== year || check.getUTCMonth() !== month - 1 || check.getUTCDate() !== day) {
3013
+ throw new Error(`Invalid calendar date: ${date}`);
3014
+ }
3015
+ return ms;
3016
+ }
3017
+ var compatibleModelIndexByProvider = /* @__PURE__ */ new Map();
3018
+ registerProviderRegistryReloadInvalidator(() => {
3019
+ compatibleModelIndexByProvider.clear();
3020
+ });
3021
+ function addAlternate(index, alternate, modelId) {
3022
+ if (typeof alternate !== "string") return;
3023
+ const trimmed = alternate.trim();
3024
+ if (trimmed.length === 0) return;
3025
+ let owners = index.alternateToModels.get(trimmed);
3026
+ if (!owners) {
3027
+ owners = /* @__PURE__ */ new Set();
3028
+ index.alternateToModels.set(trimmed, owners);
3029
+ }
3030
+ owners.add(modelId);
3031
+ }
3032
+ function buildCompatibleModelIndex(providerId) {
3033
+ try {
3034
+ const config = ProviderRegistry.getProviderConfig(providerId);
3035
+ const index = {
3036
+ modelIds: /* @__PURE__ */ new Set(),
3037
+ alternateToModels: /* @__PURE__ */ new Map()
3038
+ };
3039
+ for (const model of config.models.available) {
3040
+ index.modelIds.add(model.id);
3041
+ addAlternate(index, model.id, model.id);
3042
+ addAlternate(index, model.full_name, model.id);
3043
+ addAlternate(index, model.canonical_id, model.id);
3044
+ addAlternate(index, model.routing_id, model.id);
3045
+ for (const routeId of listModelRouteIds(model)) addAlternate(index, routeId, model.id);
3046
+ }
3047
+ return index;
3048
+ } catch {
3049
+ return null;
3050
+ }
3051
+ }
3052
+ function getCompatibleModelIndex(providerId) {
3053
+ const trimmedProvider = providerId.trim();
3054
+ if (trimmedProvider.length === 0) return null;
3055
+ if (compatibleModelIndexByProvider.has(trimmedProvider)) {
3056
+ return compatibleModelIndexByProvider.get(trimmedProvider) ?? null;
3057
+ }
3058
+ const index = buildCompatibleModelIndex(trimmedProvider);
3059
+ compatibleModelIndexByProvider.set(trimmedProvider, index);
3060
+ return index;
3061
+ }
3062
+ function lookupCompatibleModelId(index, candidate) {
3063
+ if (index.modelIds.has(candidate)) {
3064
+ return candidate;
3065
+ }
3066
+ const owners = index.alternateToModels.get(candidate);
3067
+ if (owners && owners.size === 1) {
3068
+ return owners.values().next().value;
3069
+ }
3070
+ return null;
3071
+ }
3072
+ function stripOneTrailingContextSelector(modelId) {
3073
+ const match = modelId.match(/^(.*)(\[[^\]]+\])\s*$/u);
3074
+ if (!match || match[1] === void 0 || match[2] === void 0) {
3075
+ return null;
3076
+ }
3077
+ const base = match[1].trim();
3078
+ if (base.length === 0) {
3079
+ return null;
3080
+ }
3081
+ return base;
3082
+ }
3083
+ function resolveCompatibleInvocationModelId(providerId, modelId) {
3084
+ const trimmedModel = typeof modelId === "string" ? modelId.trim() : "";
3085
+ if (trimmedModel.length === 0) return modelId;
3086
+ const index = getCompatibleModelIndex(providerId);
3087
+ if (!index) return modelId;
3088
+ const direct = lookupCompatibleModelId(index, trimmedModel);
3089
+ if (direct !== null) {
3090
+ return direct;
3091
+ }
3092
+ const baseWithoutSelector = stripOneTrailingContextSelector(trimmedModel);
3093
+ if (baseWithoutSelector !== null && baseWithoutSelector !== trimmedModel) {
3094
+ const fromBase = lookupCompatibleModelId(index, baseWithoutSelector);
3095
+ if (fromBase !== null) {
3096
+ return fromBase;
3097
+ }
3098
+ }
3099
+ return modelId;
3100
+ }
3101
+ function invocationModelMatchesSameModel(providerId, storedModelId, filterModelId) {
3102
+ if (storedModelId === filterModelId) return true;
3103
+ const storedNorm = resolveCompatibleInvocationModelId(providerId, storedModelId);
3104
+ const filterNorm = resolveCompatibleInvocationModelId(providerId, filterModelId);
3105
+ return storedNorm === filterNorm;
3106
+ }
3107
+ var ModelInvocationsDB = class {
3108
+ // Exposed for worker access - ideally should add public query methods instead
3109
+ db;
3110
+ // Database instance type (better-sqlite3 or in-memory shim)
3111
+ dbPath;
3112
+ portabilityDbDir;
3113
+ projectSyncId;
3114
+ constructor(dbPath, options = {}) {
3115
+ if (!sqlite_wrapper_default) {
3116
+ throw new Error("Database implementation not available");
3117
+ }
3118
+ this.dbPath = dbPath;
3119
+ this.portabilityDbDir = options.portabilityDbDir;
3120
+ this.projectSyncId = options.projectSyncId;
3121
+ mkdirSync2(path5.dirname(dbPath), { recursive: true });
3122
+ this.db = new sqlite_wrapper_default(dbPath);
3123
+ this.initializeSchema();
3124
+ }
3125
+ /**
3126
+ * Initialize database schema with all required tables
3127
+ */
3128
+ initializeSchema() {
3129
+ applySqlitePragmas(this.db, { foreignKeys: true });
3130
+ this.db.exec(`
3131
+ -- Model invocations table
3132
+ CREATE TABLE IF NOT EXISTS model_invocations (
3133
+ id TEXT PRIMARY KEY,
3134
+ timestamp INTEGER NOT NULL,
3135
+ parent_workflow_id TEXT,
3136
+ provider_id TEXT NOT NULL,
3137
+ model_id TEXT NOT NULL,
3138
+ registry_version TEXT NOT NULL,
3139
+ tokens_in INTEGER,
3140
+ tokens_out INTEGER,
3141
+ time_request_sent_ms INTEGER NOT NULL,
3142
+ time_first_token_ms INTEGER,
3143
+ time_last_token_ms INTEGER,
3144
+ time_to_first_token_ms INTEGER,
3145
+ total_time_ms INTEGER,
3146
+ status TEXT NOT NULL,
3147
+ error_code TEXT,
3148
+ request_size_bytes INTEGER,
3149
+ agent_role TEXT,
3150
+ copilot_subcommand TEXT,
3151
+ execution_source TEXT,
3152
+ requested_model_id TEXT,
3153
+ execution_model_id TEXT,
3154
+ reported_model_id TEXT,
3155
+ backend TEXT,
3156
+ scope TEXT,
3157
+ context_selector TEXT,
3158
+ resolution_source TEXT,
3159
+ task_id TEXT,
3160
+ competition_id TEXT,
3161
+ variant_id TEXT
3162
+ ,run_id TEXT
3163
+ ,attempt_id TEXT
3164
+ ,attempt_ordinal INTEGER
3165
+ ,project_id TEXT
3166
+ ,lab_id TEXT
3167
+ ,lab_run_id TEXT
3168
+ ,provider_reported_cost_usd REAL
3169
+ ,build_calculated_cost_usd REAL
3170
+ );
3171
+
3172
+ CREATE INDEX IF NOT EXISTS idx_model_invocations_timestamp
3173
+ ON model_invocations(timestamp);
3174
+ CREATE INDEX IF NOT EXISTS idx_model_invocations_provider_model
3175
+ ON model_invocations(provider_id, model_id);
3176
+ CREATE INDEX IF NOT EXISTS idx_model_invocations_registry_version
3177
+ ON model_invocations(registry_version);
3178
+ CREATE INDEX IF NOT EXISTS idx_model_invocations_agent_role
3179
+ ON model_invocations(agent_role);
3180
+ -- Partial index for speed/latency analytics, which filter on
3181
+ -- status='success' (+ optional timestamp/provider/model) and order by
3182
+ -- timestamp. A PARTIAL index keyed only to successful rows is used
3183
+ -- instead of the full composite (status, timestamp, provider_id,
3184
+ -- model_id) deliberately: the full composite would add per-INSERT write
3185
+ -- cost on the hot invocation-write path for every row, whereas success
3186
+ -- rows are the only ones these read paths target. Validate that the
3187
+ -- speed query actually uses this index with EXPLAIN QUERY PLAN. Created
3188
+ -- idempotently like the other indexes above (no SCHEMA_VERSION bump).
3189
+ CREATE INDEX IF NOT EXISTS idx_model_invocations_success_speed
3190
+ ON model_invocations(timestamp DESC, provider_id, model_id)
3191
+ WHERE status = 'success';
3192
+
3193
+ -- Registry snapshots table
3194
+ CREATE TABLE IF NOT EXISTS registry_snapshots (
3195
+ registry_version TEXT PRIMARY KEY,
3196
+ created_at INTEGER NOT NULL,
3197
+ json_blob TEXT NOT NULL
3198
+ );
3199
+
3200
+ -- Enriched invocations table (hybrid storage: tokens + cost snapshot)
3201
+ CREATE TABLE IF NOT EXISTS enriched_invocations (
3202
+ id TEXT PRIMARY KEY,
3203
+ invocation_id TEXT NOT NULL UNIQUE,
3204
+ computed_cost_cents INTEGER,
3205
+ pricing_source TEXT NOT NULL,
3206
+ enriched_at INTEGER NOT NULL,
3207
+ billing_mode TEXT,
3208
+
3209
+ -- Hybrid Storage: Raw usage data (JSON) - immutable source of truth
3210
+ usage_data TEXT,
3211
+
3212
+ -- Hybrid Storage: Cost snapshot (JSON) - historical accuracy
3213
+ cost_snapshot TEXT,
3214
+
3215
+ -- Hybrid Storage: Pricing metadata for recalculation
3216
+ pricing_version TEXT,
3217
+
3218
+ -- Hybrid Storage: Context for cost attribution
3219
+ task_id TEXT,
3220
+ competition_id TEXT,
3221
+ variant_id TEXT,
3222
+
3223
+ FOREIGN KEY (invocation_id) REFERENCES model_invocations(id)
3224
+ );
3225
+
3226
+ CREATE INDEX IF NOT EXISTS idx_enriched_invocations_invocation_id
3227
+ ON enriched_invocations(invocation_id);
3228
+ CREATE INDEX IF NOT EXISTS idx_enriched_invocations_task_id
3229
+ ON enriched_invocations(task_id);
3230
+ CREATE INDEX IF NOT EXISTS idx_enriched_invocations_competition_id
3231
+ ON enriched_invocations(competition_id);
3232
+
3233
+ CREATE TABLE IF NOT EXISTS usage_evidence (
3234
+ evidence_id TEXT PRIMARY KEY,
3235
+ invocation_id TEXT NOT NULL,
3236
+ run_id TEXT NOT NULL,
3237
+ attempt_id TEXT NOT NULL,
3238
+ event_semantics TEXT NOT NULL,
3239
+ provenance TEXT NOT NULL,
3240
+ input_tokens INTEGER,
3241
+ output_tokens INTEGER,
3242
+ cache_read_tokens INTEGER,
3243
+ cache_write_tokens INTEGER,
3244
+ reasoning_tokens INTEGER,
3245
+ tool_tokens INTEGER,
3246
+ cache_read_included_in_input INTEGER,
3247
+ cache_write_included_in_input INTEGER,
3248
+ extra_dimensions_json TEXT,
3249
+ availability_json TEXT NOT NULL,
3250
+ parser_version TEXT NOT NULL,
3251
+ created_at INTEGER NOT NULL,
3252
+ FOREIGN KEY (invocation_id) REFERENCES model_invocations(id)
3253
+ );
3254
+ CREATE INDEX IF NOT EXISTS idx_usage_evidence_invocation
3255
+ ON usage_evidence(invocation_id);
3256
+ CREATE INDEX IF NOT EXISTS idx_usage_evidence_run
3257
+ ON usage_evidence(run_id, attempt_id);
3258
+
3259
+ CREATE TABLE IF NOT EXISTS cost_evaluations (
3260
+ evaluation_id TEXT PRIMARY KEY,
3261
+ invocation_id TEXT NOT NULL,
3262
+ evidence_id TEXT,
3263
+ pricing_version TEXT,
3264
+ methodology_version TEXT NOT NULL,
3265
+ api_equivalent_cost_usd REAL,
3266
+ provider_reported_cost_usd REAL,
3267
+ comparison_delta_usd REAL,
3268
+ currency TEXT NOT NULL,
3269
+ status TEXT NOT NULL,
3270
+ created_at INTEGER NOT NULL,
3271
+ FOREIGN KEY (invocation_id) REFERENCES model_invocations(id),
3272
+ FOREIGN KEY (evidence_id) REFERENCES usage_evidence(evidence_id)
3273
+ );
3274
+ CREATE INDEX IF NOT EXISTS idx_cost_evaluations_invocation
3275
+ ON cost_evaluations(invocation_id, created_at);
3276
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_cost_evaluations_methodology
3277
+ ON cost_evaluations(invocation_id, methodology_version, pricing_version);
3278
+
3279
+ -- Hourly aggregates table: retained schema only; nothing writes or reads it
3280
+ CREATE TABLE IF NOT EXISTS aggregates_hourly (
3281
+ bucket_start INTEGER NOT NULL,
3282
+ provider_id TEXT NOT NULL,
3283
+ model_id TEXT NOT NULL,
3284
+ total_invocations INTEGER NOT NULL,
3285
+ total_tokens_in INTEGER NOT NULL,
3286
+ total_tokens_out INTEGER NOT NULL,
3287
+ avg_cost_cents REAL NOT NULL,
3288
+ p95_latency_ms INTEGER NOT NULL,
3289
+ error_rate REAL NOT NULL,
3290
+ PRIMARY KEY (bucket_start, provider_id, model_id)
3291
+ );
3292
+
3293
+ -- Daily cost aggregates table (queried by getDailyCostAggregations)
3294
+ CREATE TABLE IF NOT EXISTS cost_aggregations_daily (
3295
+ date TEXT NOT NULL,
3296
+ provider TEXT NOT NULL,
3297
+ model TEXT NOT NULL,
3298
+ execution_count INTEGER NOT NULL DEFAULT 0,
3299
+ total_cost_usd REAL NOT NULL DEFAULT 0,
3300
+ total_input_tokens INTEGER NOT NULL DEFAULT 0,
3301
+ total_output_tokens INTEGER NOT NULL DEFAULT 0,
3302
+ avg_cost_per_execution REAL NOT NULL DEFAULT 0,
3303
+ PRIMARY KEY (date, provider, model)
3304
+ );
3305
+ CREATE INDEX IF NOT EXISTS idx_cost_aggregations_daily_date
3306
+ ON cost_aggregations_daily(date);
3307
+ `);
3308
+ this.ensureColumn("model_invocations", "copilot_subcommand", "TEXT");
3309
+ this.ensureColumn("model_invocations", "execution_source", "TEXT");
3310
+ this.ensureColumn("model_invocations", "requested_model_id", "TEXT");
3311
+ this.ensureColumn("model_invocations", "execution_model_id", "TEXT");
3312
+ this.ensureColumn("model_invocations", "reported_model_id", "TEXT");
3313
+ this.ensureColumn("model_invocations", "backend", "TEXT");
3314
+ this.ensureColumn("model_invocations", "scope", "TEXT");
3315
+ this.ensureColumn("model_invocations", "context_selector", "TEXT");
3316
+ this.ensureColumn("model_invocations", "resolution_source", "TEXT");
3317
+ this.ensureColumn("model_invocations", "task_id", "TEXT");
3318
+ this.ensureColumn("model_invocations", "competition_id", "TEXT");
3319
+ this.ensureColumn("model_invocations", "variant_id", "TEXT");
3320
+ this.ensureColumn("model_invocations", "run_id", "TEXT");
3321
+ this.ensureColumn("model_invocations", "attempt_id", "TEXT");
3322
+ this.ensureColumn("model_invocations", "attempt_ordinal", "INTEGER");
3323
+ this.ensureColumn("model_invocations", "project_id", "TEXT");
3324
+ this.ensureColumn("model_invocations", "lab_id", "TEXT");
3325
+ this.ensureColumn("model_invocations", "lab_run_id", "TEXT");
3326
+ this.ensureColumn("model_invocations", "provider_reported_cost_usd", "REAL");
3327
+ this.ensureColumn("model_invocations", "build_calculated_cost_usd", "REAL");
3328
+ this.ensureColumn("enriched_invocations", "billing_mode", "TEXT");
3329
+ this.ensureColumn("enriched_invocations", "usage_data", "TEXT");
3330
+ this.ensureColumn("enriched_invocations", "cost_snapshot", "TEXT");
3331
+ this.ensureColumn("enriched_invocations", "pricing_version", "TEXT");
3332
+ this.ensureColumn("enriched_invocations", "task_id", "TEXT");
3333
+ this.ensureColumn("enriched_invocations", "competition_id", "TEXT");
3334
+ this.ensureColumn("enriched_invocations", "variant_id", "TEXT");
3335
+ this.db.exec(`
3336
+ CREATE INDEX IF NOT EXISTS idx_model_invocations_task_time
3337
+ ON model_invocations(task_id, timestamp);
3338
+ CREATE INDEX IF NOT EXISTS idx_model_invocations_run
3339
+ ON model_invocations(run_id);
3340
+ CREATE INDEX IF NOT EXISTS idx_model_invocations_project_time
3341
+ ON model_invocations(project_id, timestamp);
3342
+ `);
3343
+ createInvocationAnalyticsViews(this.db);
3344
+ }
3345
+ /**
3346
+ * Ensures a column exists in a table, adding it if necessary.
3347
+ * Security: Table name is validated against whitelist to prevent SQL injection (CWE-89).
3348
+ * Column name and definition are parameterized through prepared statements where possible.
3349
+ *
3350
+ * @param table - The table name (must be in ALLOWED_TABLE_NAMES)
3351
+ * @param column - The column name to ensure exists
3352
+ * @param definition - The SQL column definition (e.g., 'TEXT', 'INTEGER NOT NULL')
3353
+ * @throws {Error} If the table name is not in the whitelist
3354
+ */
3355
+ ensureColumn(table, column, definition) {
3356
+ const validatedTable = validateTableName(table);
3357
+ const validatedColumn = validateSqlIdentifier(column, "column name");
3358
+ const validatedDefinition = validateColumnDefinition(definition);
3359
+ try {
3360
+ const infoStmt = this.db.prepare(`PRAGMA table_info(${validatedTable})`);
3361
+ const columns = infoStmt.all() ?? [];
3362
+ const hasColumn = columns.some((col) => col.name === validatedColumn);
3363
+ if (!hasColumn) {
3364
+ this.db.exec(
3365
+ `ALTER TABLE ${validatedTable} ADD COLUMN ${validatedColumn} ${validatedDefinition}`
3366
+ );
3367
+ }
3368
+ } catch (error) {
3369
+ if (!error || !(error instanceof Error) || !error.message.includes("Invalid table name")) {
3370
+ emitWarning(
3371
+ "WS-003",
3372
+ {
3373
+ taskId: "system",
3374
+ subsystem: "telemetry",
3375
+ executionPhase: "setup"
3376
+ },
3377
+ {
3378
+ severity: "WARN" /* WARN */,
3379
+ metadata: {
3380
+ operation: "ensure_column",
3381
+ table,
3382
+ column,
3383
+ error: error instanceof Error ? error.message : String(error)
3384
+ }
3385
+ }
3386
+ );
3387
+ } else {
3388
+ throw error;
3389
+ }
3390
+ }
3391
+ }
3392
+ /**
3393
+ * Insert a model invocation record
3394
+ */
3395
+ insertInvocation(invocation) {
3396
+ const stmt = this.db.prepare(`
3397
+ INSERT INTO model_invocations (
3398
+ id, timestamp, parent_workflow_id, provider_id, model_id,
3399
+ registry_version, tokens_in, tokens_out, time_request_sent_ms,
3400
+ time_first_token_ms, time_last_token_ms, time_to_first_token_ms,
3401
+ total_time_ms, status, error_code, request_size_bytes, agent_role,
3402
+ copilot_subcommand, execution_source,
3403
+ requested_model_id, execution_model_id, reported_model_id,
3404
+ backend, scope, context_selector, resolution_source,
3405
+ task_id, competition_id, variant_id, run_id, attempt_id, attempt_ordinal,
3406
+ project_id, lab_id, lab_run_id, provider_reported_cost_usd, build_calculated_cost_usd
3407
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
3408
+ `);
3409
+ stmt.run(
3410
+ invocation.id,
3411
+ invocation.timestamp,
3412
+ invocation.parentWorkflowId ?? null,
3413
+ invocation.providerId,
3414
+ invocation.modelId,
3415
+ invocation.registryVersion,
3416
+ invocation.tokensIn ?? null,
3417
+ invocation.tokensOut ?? null,
3418
+ invocation.timeRequestSentMs,
3419
+ invocation.timeFirstTokenMs ?? null,
3420
+ invocation.timeLastTokenMs ?? null,
3421
+ invocation.timeToFirstTokenMs ?? null,
3422
+ invocation.totalTimeMs ?? null,
3423
+ invocation.status,
3424
+ invocation.errorCode ?? null,
3425
+ invocation.requestSizeBytes ?? null,
3426
+ invocation.agentRole ?? null,
3427
+ invocation.copilotSubcommand ?? null,
3428
+ invocation.executionSource ?? null,
3429
+ invocation.requestedModelId ?? null,
3430
+ invocation.executionModelId ?? null,
3431
+ invocation.reportedModelId ?? null,
3432
+ invocation.backend ?? null,
3433
+ invocation.scope ?? null,
3434
+ invocation.contextSelector ?? null,
3435
+ invocation.resolutionSource ?? null,
3436
+ invocation.taskId ?? null,
3437
+ invocation.competitionId ?? invocation.taskId ?? null,
3438
+ invocation.variantId ?? null,
3439
+ invocation.runId ?? null,
3440
+ invocation.attemptId ?? invocation.id,
3441
+ invocation.attemptOrdinal ?? 1,
3442
+ invocation.projectId ?? null,
3443
+ invocation.labId ?? null,
3444
+ invocation.labRunId ?? null,
3445
+ invocation.providerReportedCostUsd ?? null,
3446
+ invocation.buildCalculatedCostUsd ?? null
3447
+ );
3448
+ }
3449
+ insertUsageEvidence(evidence) {
3450
+ this.db.prepare(
3451
+ `INSERT OR IGNORE INTO usage_evidence (
3452
+ evidence_id, invocation_id, run_id, attempt_id, event_semantics, provenance,
3453
+ input_tokens, output_tokens, cache_read_tokens, cache_write_tokens,
3454
+ reasoning_tokens, tool_tokens, cache_read_included_in_input,
3455
+ cache_write_included_in_input, extra_dimensions_json, availability_json,
3456
+ parser_version, created_at
3457
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
3458
+ ).run(
3459
+ evidence.evidenceId,
3460
+ evidence.invocationId,
3461
+ evidence.runId,
3462
+ evidence.attemptId,
3463
+ evidence.eventSemantics,
3464
+ evidence.provenance,
3465
+ evidence.inputTokens ?? null,
3466
+ evidence.outputTokens ?? null,
3467
+ evidence.cacheReadTokens ?? null,
3468
+ evidence.cacheWriteTokens ?? null,
3469
+ evidence.reasoningTokens ?? null,
3470
+ evidence.toolTokens ?? null,
3471
+ evidence.cacheReadIncludedInInput == null ? null : evidence.cacheReadIncludedInInput ? 1 : 0,
3472
+ evidence.cacheWriteIncludedInInput == null ? null : evidence.cacheWriteIncludedInInput ? 1 : 0,
3473
+ evidence.extraDimensionsJson ?? null,
3474
+ evidence.availabilityJson,
3475
+ evidence.parserVersion,
3476
+ evidence.createdAt
3477
+ );
3478
+ }
3479
+ getUsageEvidenceForInvocation(invocationId) {
3480
+ const rows = this.db.prepare(
3481
+ `SELECT * FROM usage_evidence
3482
+ WHERE invocation_id = ?
3483
+ ORDER BY created_at ASC, evidence_id ASC`
3484
+ ).all(invocationId);
3485
+ const evidence = [];
3486
+ for (const row of rows) {
3487
+ const converted = convertRowToCamelCase(row);
3488
+ if (!converted) continue;
3489
+ evidence.push({
3490
+ ...converted,
3491
+ cacheReadIncludedInInput: row.cache_read_included_in_input === null ? null : Number(row.cache_read_included_in_input) !== 0,
3492
+ cacheWriteIncludedInInput: row.cache_write_included_in_input === null ? null : Number(row.cache_write_included_in_input) !== 0
3493
+ });
3494
+ }
3495
+ if (evidence.length === 0) return null;
3496
+ const final = evidence.filter(
3497
+ (row) => ["final", "cumulative", "logical_run_aggregate"].includes(row.eventSemantics)
3498
+ ).at(-1);
3499
+ if (final) return final;
3500
+ const base = evidence.at(-1);
3501
+ return {
3502
+ ...base,
3503
+ inputTokens: sumNullableEvidence(evidence, "inputTokens"),
3504
+ outputTokens: sumNullableEvidence(evidence, "outputTokens"),
3505
+ cacheReadTokens: sumNullableEvidence(evidence, "cacheReadTokens"),
3506
+ cacheWriteTokens: sumNullableEvidence(evidence, "cacheWriteTokens"),
3507
+ reasoningTokens: sumNullableEvidence(evidence, "reasoningTokens"),
3508
+ toolTokens: sumNullableEvidence(evidence, "toolTokens"),
3509
+ eventSemantics: "delta"
3510
+ };
3511
+ }
3512
+ insertCostEvaluation(evaluation) {
3513
+ this.db.prepare(
3514
+ `INSERT OR IGNORE INTO cost_evaluations (
3515
+ evaluation_id, invocation_id, evidence_id, pricing_version, methodology_version,
3516
+ api_equivalent_cost_usd, provider_reported_cost_usd, comparison_delta_usd,
3517
+ currency, status, created_at
3518
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
3519
+ ).run(
3520
+ evaluation.evaluationId,
3521
+ evaluation.invocationId,
3522
+ evaluation.evidenceId ?? null,
3523
+ evaluation.pricingVersion ?? null,
3524
+ evaluation.methodologyVersion,
3525
+ evaluation.apiEquivalentCostUsd ?? null,
3526
+ evaluation.providerReportedCostUsd ?? null,
3527
+ evaluation.comparisonDeltaUsd ?? null,
3528
+ evaluation.currency,
3529
+ evaluation.status,
3530
+ evaluation.createdAt
3531
+ );
3532
+ }
3533
+ /**
3534
+ * Get a model invocation by ID
3535
+ */
3536
+ getInvocation(id) {
3537
+ const stmt = this.db.prepare(`
3538
+ SELECT * FROM model_invocations WHERE id = ?
3539
+ `);
3540
+ const row = stmt.get(id);
3541
+ return row ? convertRowToCamelCase(row) : null;
3542
+ }
3543
+ /**
3544
+ * Return the most recent live invocations used by performance reporting.
3545
+ *
3546
+ * The invocation database is the canonical execution-attempt store. Results
3547
+ * are returned oldest-first within the requested recent window so callers can
3548
+ * calculate throughput and render CSV chronologically.
3549
+ */
3550
+ getPerformanceInvocations(options) {
3551
+ const limit = Math.max(1, Math.trunc(options.limit));
3552
+ const filters = [];
3553
+ const params = [];
3554
+ filters.push(...invocationProvenanceClauses("i"));
3555
+ if (options.providerId) {
3556
+ filters.push("i.provider_id = ?");
3557
+ params.push(options.providerId);
3558
+ }
3559
+ if (options.variantId) {
3560
+ filters.push("i.variant_id = ?");
3561
+ params.push(options.variantId);
3562
+ }
3563
+ const where = filters.length > 0 ? `WHERE ${filters.join("\nAND ")}` : "";
3564
+ const rows = this.db.prepare(
3565
+ `SELECT i.*
3566
+ FROM model_invocations i
3567
+ ${where}
3568
+ ORDER BY i.timestamp DESC, i.id DESC
3569
+ LIMIT ?`
3570
+ ).all(...params, limit);
3571
+ return rows.reverse().map(
3572
+ (row) => convertRowToCamelCase(row)
3573
+ ).filter((row) => row !== null);
3574
+ }
3575
+ /**
3576
+ * Insert a registry snapshot
3577
+ */
3578
+ insertRegistrySnapshot(snapshot) {
3579
+ const stmt = this.db.prepare(`
3580
+ INSERT OR IGNORE INTO registry_snapshots (registry_version, created_at, json_blob)
3581
+ VALUES (?, ?, ?)
3582
+ `);
3583
+ stmt.run(snapshot.registryVersion, snapshot.createdAt, snapshot.jsonBlob);
3584
+ }
3585
+ /**
3586
+ * Get the most recently persisted registry snapshot, if any.
3587
+ */
3588
+ getLatestRegistrySnapshot() {
3589
+ const snapshots = this.getRegistrySnapshots(1);
3590
+ return snapshots[0] ?? null;
3591
+ }
3592
+ /**
3593
+ * List registry snapshots newest-first.
3594
+ *
3595
+ * Used by enrichment when an invocation's stamped registry_version is a
3596
+ * placeholder / stale id: prefer the exact version when present, then walk
3597
+ * other persisted snapshots so known providers still price.
3598
+ */
3599
+ getRegistrySnapshots(limit) {
3600
+ const boundedLimit = typeof limit === "number" && Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : void 0;
3601
+ const stmt = this.db.prepare(
3602
+ boundedLimit !== void 0 ? `
3603
+ SELECT * FROM registry_snapshots
3604
+ ORDER BY created_at DESC
3605
+ LIMIT ?
3606
+ ` : `
3607
+ SELECT * FROM registry_snapshots
3608
+ ORDER BY created_at DESC
3609
+ `
3610
+ );
3611
+ const rows = (boundedLimit !== void 0 ? stmt.all(boundedLimit) : stmt.all()) ?? [];
3612
+ return rows.map(
3613
+ (row) => convertRowToCamelCase(row)
3614
+ ).filter((snapshot) => snapshot !== null);
3615
+ }
3616
+ /**
3617
+ * Get the most recently persisted registry version, if any.
3618
+ */
3619
+ getLatestRegistryVersion() {
3620
+ return this.getLatestRegistrySnapshot()?.registryVersion ?? null;
3621
+ }
3622
+ /**
3623
+ * Get a registry snapshot by version
3624
+ */
3625
+ getRegistrySnapshot(registryVersion) {
3626
+ const stmt = this.db.prepare(`
3627
+ SELECT * FROM registry_snapshots WHERE registry_version = ?
3628
+ `);
3629
+ const row = stmt.get(registryVersion);
3630
+ return row ? convertRowToCamelCase(row) : null;
3631
+ }
3632
+ /**
3633
+ * Insert an enriched invocation record
3634
+ */
3635
+ insertEnrichedInvocation(enriched) {
3636
+ const stmt = this.db.prepare(`
3637
+ INSERT OR REPLACE INTO enriched_invocations (
3638
+ id, invocation_id, computed_cost_cents, pricing_source, enriched_at, billing_mode,
3639
+ usage_data, cost_snapshot, pricing_version, task_id, competition_id, variant_id
3640
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
3641
+ `);
3642
+ stmt.run(
3643
+ enriched.id,
3644
+ enriched.invocationId,
3645
+ enriched.computedCostCents ?? null,
3646
+ enriched.pricingSource,
3647
+ enriched.enrichedAt,
3648
+ enriched.billingMode ?? null,
3649
+ enriched.usageData ?? null,
3650
+ enriched.costSnapshot ?? null,
3651
+ enriched.pricingVersion ?? null,
3652
+ enriched.taskId ?? null,
3653
+ enriched.competitionId ?? null,
3654
+ enriched.variantId ?? null
3655
+ );
3656
+ }
3657
+ /**
3658
+ * Execute related telemetry writes atomically when the SQLite driver supports it.
3659
+ */
3660
+ transaction(operation) {
3661
+ if (!this.db.transaction) {
3662
+ return operation();
3663
+ }
3664
+ return this.db.transaction(operation)();
3665
+ }
3666
+ /**
3667
+ * Get unenriched invocations
3668
+ */
3669
+ getUnenrichedInvocations(limit = 100) {
3670
+ const stmt = this.db.prepare(`
3671
+ SELECT i.* FROM model_invocations i
3672
+ LEFT JOIN enriched_invocations e ON i.id = e.invocation_id
3673
+ WHERE e.id IS NULL
3674
+ ORDER BY i.timestamp ASC
3675
+ LIMIT ?
3676
+ `);
3677
+ const rows = stmt.all(limit) ?? [];
3678
+ return rows.map(
3679
+ (row) => convertRowToCamelCase(row)
3680
+ ).filter((x) => x !== null);
3681
+ }
3682
+ /**
3683
+ * Get cost breakdown by agent role for swarm executions
3684
+ * Returns per-role cost totals (snapshot-first cost precedence, non-metered
3685
+ * rows excluded) plus how many of the role's invocations were priced.
3686
+ */
3687
+ getRoleCostBreakdown(startTimestamp, endTimestamp) {
3688
+ let query = `
3689
+ SELECT
3690
+ i.agent_role as agent_role,
3691
+ COUNT(*) as total_invocations,
3692
+ COALESCE(SUM(i.tokens_in), 0) as total_tokens_in,
3693
+ COALESCE(SUM(i.tokens_out), 0) as total_tokens_out,
3694
+ COALESCE(SUM((${SNAPSHOT_FIRST_COST_CENTS_SQL})), 0) as total_cost_cents,
3695
+ SUM(CASE WHEN (${SNAPSHOT_FIRST_COST_CENTS_SQL}) IS NOT NULL THEN 1 ELSE 0 END) as priced_invocations
3696
+ FROM model_invocations i
3697
+ LEFT JOIN enriched_invocations e ON i.id = e.invocation_id
3698
+ WHERE i.agent_role IS NOT NULL
3699
+ `;
3700
+ const params = [];
3701
+ if (startTimestamp !== void 0) {
3702
+ query += " AND i.timestamp >= ?";
3703
+ params.push(startTimestamp);
3704
+ }
3705
+ if (endTimestamp !== void 0) {
3706
+ query += " AND i.timestamp <= ?";
3707
+ params.push(endTimestamp);
3708
+ }
3709
+ query += `
3710
+ GROUP BY i.agent_role
3711
+ ORDER BY total_cost_cents DESC, agent_role ASC
3712
+ `;
3713
+ const stmt = this.db.prepare(query);
3714
+ const rows = params.length > 0 ? stmt.all(...params) : stmt.all();
3715
+ return (rows ?? []).map(
3716
+ (row) => convertRowToCamelCase(row)
3717
+ ).filter(
3718
+ (x) => x !== null
3719
+ );
3720
+ }
3721
+ /**
3722
+ * Return TTFT and token-throughput statistics grouped by provider/model.
3723
+ *
3724
+ * Only streaming invocations (status='success', time_to_first_token_ms IS NOT NULL)
3725
+ * are included. Rows with total_time_ms <= 0 or null tokens_out are excluded from
3726
+ * the tokens/sec calculation to avoid divide-by-zero and noise.
3727
+ *
3728
+ * Percentiles are computed in JS from a sorted slice, so they are exact for the
3729
+ * dataset (not approximations). Safe to call on an empty or partially-populated DB.
3730
+ *
3731
+ * @param limit - Max provider/model groups to return, ordered by invocation count desc.
3732
+ */
3733
+ getTtftReport(limit = 20, options = {}) {
3734
+ return this.getTtftReportResult(limit, options).rows;
3735
+ }
3736
+ /** Return TTFT rows with explicit supplementary-query availability. */
3737
+ getTtftReportResult(limit = 20, options = {}) {
3738
+ let rows;
3739
+ try {
3740
+ const filters = ["time_to_first_token_ms IS NOT NULL", "status = 'success'"];
3741
+ const params = [];
3742
+ if (!options.includeRaw) {
3743
+ filters.push(...invocationAnalyticsQualityClauses());
3744
+ }
3745
+ if (options.providerId) {
3746
+ filters.push("provider_id = ?");
3747
+ params.push(options.providerId);
3748
+ }
3749
+ if (options.variantId) {
3750
+ filters.push("variant_id = ?");
3751
+ params.push(options.variantId);
3752
+ }
3753
+ const stmt = this.db.prepare(`
3754
+ SELECT
3755
+ provider_id,
3756
+ model_id,
3757
+ time_to_first_token_ms,
3758
+ tokens_out,
3759
+ total_time_ms
3760
+ FROM model_invocations
3761
+ WHERE ${filters.join("\nAND ")}
3762
+ ORDER BY provider_id, model_id, time_to_first_token_ms ASC
3763
+ `);
3764
+ rows = stmt.all(...params);
3765
+ } catch (error) {
3766
+ return {
3767
+ status: "unavailable",
3768
+ rows: [],
3769
+ unavailableReason: error instanceof Error ? error.message : String(error)
3770
+ };
3771
+ }
3772
+ if (rows.length === 0) return { status: "available", rows: [] };
3773
+ const groups = /* @__PURE__ */ new Map();
3774
+ for (const row of rows) {
3775
+ const compatibleModelId = resolveCompatibleInvocationModelId(row.provider_id, row.model_id);
3776
+ const key = `${row.provider_id}\0${compatibleModelId}`;
3777
+ let group = groups.get(key);
3778
+ if (!group) {
3779
+ group = { providerId: row.provider_id, modelId: compatibleModelId, rows: [] };
3780
+ groups.set(key, group);
3781
+ }
3782
+ group.rows.push(row);
3783
+ }
3784
+ const result = [];
3785
+ for (const group of groups.values()) {
3786
+ const groupRows = group.rows;
3787
+ const ttfts = groupRows.map((r) => r.time_to_first_token_ms).sort((a, b) => a - b);
3788
+ const n = ttfts.length;
3789
+ if (n === 0) continue;
3790
+ const p50 = ttfts[Math.min(Math.floor(n * 0.5), n - 1)] ?? null;
3791
+ const p95 = ttfts[Math.min(Math.floor(n * 0.95), n - 1)] ?? null;
3792
+ const avgTtft = Math.round(ttfts.reduce((s, v) => s + v, 0) / n);
3793
+ const tokensSecs = groupRows.filter(
3794
+ (r) => r.total_time_ms !== null && r.total_time_ms > 0 && r.tokens_out !== null && r.tokens_out > 0
3795
+ ).map((r) => r.tokens_out * 1e3 / r.total_time_ms);
3796
+ const avgTokensPerSec = tokensSecs.length > 0 ? Math.round(tokensSecs.reduce((s, v) => s + v, 0) / tokensSecs.length * 100) / 100 : null;
3797
+ result.push({
3798
+ providerId: group.providerId,
3799
+ modelId: group.modelId,
3800
+ streamingInvocations: n,
3801
+ p50TtftMs: p50,
3802
+ p95TtftMs: p95,
3803
+ avgTtftMs: avgTtft,
3804
+ avgTokensPerSec
3805
+ });
3806
+ }
3807
+ result.sort((a, b) => b.streamingInvocations - a.streamingInvocations);
3808
+ return { status: "available", rows: result.slice(0, limit) };
3809
+ }
3810
+ /**
3811
+ * Close database connection
3812
+ */
3813
+ close() {
3814
+ this.db.close();
3815
+ }
3816
+ /**
3817
+ * Backfill daily cost aggregates from model_invocations (optional enrichment).
3818
+ * Idempotent via INSERT OR REPLACE; safe to call before reads or in worker cycles.
3819
+ *
3820
+ * Source universe is every matching model_invocations row; enrichment is optional
3821
+ * via LEFT JOIN. Numeric cost totals are stored only for fully priced groups;
3822
+ * incomplete groups store 0 cost so partial known-member sums are never
3823
+ * persisted as totals. Coverage status for reads is derived by
3824
+ * {@link getDailyCostAggregations}.
3825
+ *
3826
+ * @returns Number of aggregate rows upserted, or 0 on failure (worker-compatible).
3827
+ */
3828
+ backfillDailyCostAggregations(startDate, endDate) {
3829
+ const result = this.tryBackfillDailyCostAggregations(startDate, endDate);
3830
+ return result.ok ? result.changes : 0;
3831
+ }
3832
+ /**
3833
+ * Internal backfill with explicit success/failure (does not launder errors to "0 changes").
3834
+ */
3835
+ tryBackfillDailyCostAggregations(startDate, endDate) {
3836
+ const params = [];
3837
+ let whereClause = "WHERE 1=1";
3838
+ if (startDate) {
3839
+ whereClause += " AND i.timestamp >= ?";
3840
+ params.push(utcDateStartMs(startDate));
3841
+ }
3842
+ if (endDate) {
3843
+ whereClause += " AND i.timestamp < ?";
3844
+ params.push(utcDateStartMs(endDate) + DAY_MS);
3845
+ }
3846
+ try {
3847
+ const stmt = this.db.prepare(`
3848
+ INSERT OR REPLACE INTO cost_aggregations_daily (
3849
+ date,
3850
+ provider,
3851
+ model,
3852
+ execution_count,
3853
+ total_cost_usd,
3854
+ total_input_tokens,
3855
+ total_output_tokens,
3856
+ avg_cost_per_execution
3857
+ )
3858
+ SELECT
3859
+ date(i.timestamp / 1000, 'unixepoch') as date,
3860
+ i.provider_id as provider,
3861
+ i.model_id as model,
3862
+ COUNT(*) as execution_count,
3863
+ CASE
3864
+ WHEN COUNT(*) > 0
3865
+ AND COUNT(*) = SUM(
3866
+ CASE WHEN (${SNAPSHOT_FIRST_COST_CENTS_SQL}) IS NOT NULL THEN 1 ELSE 0 END
3867
+ )
3868
+ THEN COALESCE(SUM((${SNAPSHOT_FIRST_COST_CENTS_SQL})), 0) / 100.0
3869
+ ELSE 0
3870
+ END as total_cost_usd,
3871
+ COALESCE(SUM(i.tokens_in), 0) as total_input_tokens,
3872
+ COALESCE(SUM(i.tokens_out), 0) as total_output_tokens,
3873
+ CASE
3874
+ WHEN COUNT(*) > 0
3875
+ AND COUNT(*) = SUM(
3876
+ CASE WHEN (${SNAPSHOT_FIRST_COST_CENTS_SQL}) IS NOT NULL THEN 1 ELSE 0 END
3877
+ )
3878
+ THEN COALESCE(SUM((${SNAPSHOT_FIRST_COST_CENTS_SQL})), 0) / 100.0 / COUNT(*)
3879
+ ELSE 0
3880
+ END as avg_cost_per_execution
3881
+ FROM model_invocations i
3882
+ LEFT JOIN enriched_invocations e ON e.invocation_id = i.id
3883
+ ${whereClause}
3884
+ GROUP BY date, provider, model
3885
+ `);
3886
+ const result = params.length > 0 ? stmt.run(...params) : stmt.run();
3887
+ return { ok: true, changes: result.changes ?? 0 };
3888
+ } catch (error) {
3889
+ const message = error instanceof Error ? error.message : String(error);
3890
+ emitWarning(
3891
+ "WS-003",
3892
+ {
3893
+ taskId: "system",
3894
+ subsystem: "telemetry",
3895
+ executionPhase: "execution"
3896
+ },
3897
+ {
3898
+ severity: "WARN" /* WARN */,
3899
+ metadata: {
3900
+ operation: "backfill_daily_cost_aggregations",
3901
+ error: message,
3902
+ startDate,
3903
+ endDate
3904
+ }
3905
+ }
3906
+ );
3907
+ return { ok: false, error: message };
3908
+ }
3909
+ }
3910
+ /**
3911
+ * Get daily cost aggregations for a specified date range.
3912
+ *
3913
+ * Status-bearing: numeric totals/averages exist only when every included
3914
+ * invocation has authoritative snapshot-first cost evidence. Mixed or
3915
+ * all-unpriced evidence is `incomplete` (counts retained, costs null).
3916
+ * Query/backfill failure is `error` (no totals). Empty complete means no data.
3917
+ *
3918
+ * Source universe is every matching model_invocations row (optional enrichment
3919
+ * via LEFT JOIN). Live and legacy tables are composed at the storage-native
3920
+ * date+provider+model key first: live replaces only its exact native key;
3921
+ * legacy-only native keys remain. Only then are merged native rows aggregated
3922
+ * to the requested date/provider/model grouping. A group total is numeric only
3923
+ * when every constituent native row is complete (no partial subtotals).
3924
+ * Ordering is preserved (date DESC, then provider/model ASC when grouped).
3925
+ */
3926
+ getDailyCostAggregations(startDate, endDate, groupBy) {
3927
+ const backfill = this.tryBackfillDailyCostAggregations(startDate, endDate);
3928
+ if (!backfill.ok) {
3929
+ return {
3930
+ status: "error",
3931
+ rows: [],
3932
+ executionCount: 0,
3933
+ pricedCount: 0,
3934
+ error: backfill.error,
3935
+ detail: "backfill_daily_cost_aggregations failed"
3936
+ };
3937
+ }
3938
+ try {
3939
+ const liveNative = this.queryLiveDailyCostCoverage(startDate, endDate);
3940
+ const importedNative = this.queryImportedDailyCostCoverage(startDate, endDate);
3941
+ const combinedLiveNative = this.combineLiveCoverage(liveNative, importedNative);
3942
+ const legacyNative = this.queryPreseededDailyCostAggregationsNative(startDate, endDate);
3943
+ if (combinedLiveNative.length === 0) {
3944
+ const rows2 = this.aggregateNativeDailyCostRows(legacyNative, groupBy);
3945
+ return this.buildDailyCostResultFromRows(rows2);
3946
+ }
3947
+ if (legacyNative.length === 0) {
3948
+ const rows2 = this.aggregateNativeDailyCostRows(
3949
+ this.buildDailyCostRowsFromLive(combinedLiveNative),
3950
+ groupBy
3951
+ );
3952
+ return this.buildDailyCostResultFromRows(rows2);
3953
+ }
3954
+ const mergedNative = this.mergeLiveAndLegacyAtNativeKey(combinedLiveNative, legacyNative);
3955
+ const rows = this.aggregateNativeDailyCostRows(mergedNative, groupBy);
3956
+ return this.buildDailyCostResultFromRows(rows);
3957
+ } catch (error) {
3958
+ const message = error instanceof Error ? error.message : String(error);
3959
+ emitWarning(
3960
+ "WS-003",
3961
+ {
3962
+ taskId: "system",
3963
+ subsystem: "telemetry",
3964
+ executionPhase: "execution"
3965
+ },
3966
+ {
3967
+ severity: "WARN" /* WARN */,
3968
+ metadata: {
3969
+ operation: "query_daily_cost_aggregations",
3970
+ error: message,
3971
+ startDate,
3972
+ endDate
3973
+ }
3974
+ }
3975
+ );
3976
+ return {
3977
+ status: "error",
3978
+ rows: [],
3979
+ executionCount: 0,
3980
+ pricedCount: 0,
3981
+ error: message,
3982
+ detail: "query_daily_cost_aggregations failed"
3983
+ };
3984
+ }
3985
+ }
3986
+ queryImportedDailyCostCoverage(startDate, endDate) {
3987
+ if (this.portabilityDbDir && !this.projectSyncId) {
3988
+ return [];
3989
+ }
3990
+ const portabilityPath = [
3991
+ this.portabilityDbDir ? path5.join(this.portabilityDbDir, "portability.db") : void 0,
3992
+ path5.join(path5.dirname(this.dbPath), "db", "portability.db"),
3993
+ path5.join(path5.dirname(this.dbPath), "portability.db")
3994
+ ].find((candidate) => Boolean(candidate && existsSync2(candidate)));
3995
+ if (!portabilityPath) return [];
3996
+ const imported = new sqlite_wrapper_default(portabilityPath);
3997
+ try {
3998
+ applySqlitePragmas(imported, { foreignKeys: true });
3999
+ const rows = loadImportedCostRecords(
4000
+ imported,
4001
+ this.db,
4002
+ this.projectSyncId,
4003
+ startDate ? utcDateStartMs(startDate) : void 0,
4004
+ endDate ? utcDateStartMs(endDate) + DAY_MS : void 0
4005
+ );
4006
+ const invocations = /* @__PURE__ */ new Map();
4007
+ const usage = /* @__PURE__ */ new Map();
4008
+ const evaluations = /* @__PURE__ */ new Map();
4009
+ for (const row of rows) {
4010
+ const payload = parseJsonWithContext(
4011
+ row.payloadJson,
4012
+ "portable cost payload"
4013
+ );
4014
+ const invocationId = typeof payload.invocationId === "string" ? payload.invocationId : void 0;
4015
+ if (!invocationId) continue;
4016
+ if (row.recordType === "invocation") invocations.set(invocationId, payload);
4017
+ if (row.recordType === "usage") {
4018
+ const current = usage.get(invocationId) ?? [];
4019
+ current.push(payload);
4020
+ usage.set(invocationId, current);
4021
+ }
4022
+ if (row.recordType === "costEvaluation") {
4023
+ const current = evaluations.get(invocationId);
4024
+ if (!current || Number(payload.createdAt ?? 0) >= Number(current.createdAt ?? 0)) {
4025
+ evaluations.set(invocationId, payload);
4026
+ }
4027
+ }
4028
+ }
4029
+ const native = /* @__PURE__ */ new Map();
4030
+ for (const [invocationId, invocation] of invocations) {
4031
+ const timestamp = Number(invocation.timestamp);
4032
+ if (!Number.isFinite(timestamp)) continue;
4033
+ const date = new Date(timestamp).toISOString().slice(0, 10);
4034
+ if (startDate && date < startDate || endDate && date > endDate) continue;
4035
+ const provider = String(invocation.provider ?? "unknown");
4036
+ const model = String(invocation.model ?? "unknown");
4037
+ const key = `${date}\0${provider}\0${model}`;
4038
+ const usagePayload = reducePortableUsage(usage.get(invocationId));
4039
+ const evaluation = evaluations.get(invocationId);
4040
+ const apiCost = typeof evaluation?.apiEquivalentCostUsd === "number" ? evaluation.apiEquivalentCostUsd : typeof invocation.inlineApiEquivalentCostUsd === "number" ? invocation.inlineApiEquivalentCostUsd : null;
4041
+ const inputTokens = typeof usagePayload?.inputTokens === "number" ? usagePayload.inputTokens : typeof invocation.inlineTokensIn === "number" ? invocation.inlineTokensIn : 0;
4042
+ const outputTokens = typeof usagePayload?.outputTokens === "number" ? usagePayload.outputTokens : typeof invocation.inlineTokensOut === "number" ? invocation.inlineTokensOut : 0;
4043
+ const existing = native.get(key) ?? {
4044
+ date,
4045
+ provider,
4046
+ model,
4047
+ execution_count: 0,
4048
+ priced_count: 0,
4049
+ total_cost_cents: 0,
4050
+ total_input_tokens: 0,
4051
+ total_output_tokens: 0
4052
+ };
4053
+ existing.execution_count += 1;
4054
+ existing.total_input_tokens += inputTokens;
4055
+ existing.total_output_tokens += outputTokens;
4056
+ if (apiCost !== null) {
4057
+ existing.priced_count += 1;
4058
+ if (existing.total_cost_cents !== null) {
4059
+ existing.total_cost_cents += apiCost * 100;
4060
+ }
4061
+ } else {
4062
+ existing.total_cost_cents = null;
4063
+ }
4064
+ native.set(key, existing);
4065
+ }
4066
+ return [...native.values()];
4067
+ } finally {
4068
+ imported.close();
4069
+ }
4070
+ }
4071
+ combineLiveCoverage(local, imported) {
4072
+ const combined = /* @__PURE__ */ new Map();
4073
+ for (const row of [...local, ...imported]) {
4074
+ const key = `${row.date}\0${row.provider}\0${row.model}`;
4075
+ const current = combined.get(key);
4076
+ if (!current) {
4077
+ combined.set(key, { ...row });
4078
+ continue;
4079
+ }
4080
+ current.execution_count += row.execution_count;
4081
+ current.priced_count += row.priced_count;
4082
+ current.total_input_tokens += row.total_input_tokens;
4083
+ current.total_output_tokens += row.total_output_tokens;
4084
+ current.total_cost_cents = current.total_cost_cents === null || row.total_cost_cents === null ? null : current.total_cost_cents + row.total_cost_cents;
4085
+ }
4086
+ return [...combined.values()];
4087
+ }
4088
+ /**
4089
+ * Storage-native merge identity: date + provider + model.
4090
+ */
4091
+ nativeDailyCostKey(row) {
4092
+ return `${row.date}\0${row.provider ?? ""}\0${row.model ?? ""}`;
4093
+ }
4094
+ /**
4095
+ * Stable key for daily aggregation under the requested grouping.
4096
+ */
4097
+ dailyCostAggregationKey(row, groupBy) {
4098
+ if (groupBy === "provider") {
4099
+ return `${row.date}\0${row.provider ?? ""}`;
4100
+ }
4101
+ if (groupBy === "model") {
4102
+ return `${row.date}\0${row.model ?? ""}`;
4103
+ }
4104
+ return row.date;
4105
+ }
4106
+ /**
4107
+ * Ordering: date DESC, then provider/model ASC when grouped (matches SQL ORDER BY).
4108
+ * At native resolution, order by date DESC, provider ASC, model ASC.
4109
+ */
4110
+ compareDailyCostAggregationRows(a, b, groupBy) {
4111
+ if (a.date !== b.date) {
4112
+ return a.date < b.date ? 1 : -1;
4113
+ }
4114
+ if (groupBy === "provider" || groupBy === "native") {
4115
+ const ap = a.provider ?? "";
4116
+ const bp = b.provider ?? "";
4117
+ if (ap !== bp) {
4118
+ return ap < bp ? -1 : 1;
4119
+ }
4120
+ }
4121
+ if (groupBy === "model" || groupBy === "native") {
4122
+ const am = a.model ?? "";
4123
+ const bm = b.model ?? "";
4124
+ if (am !== bm) {
4125
+ return am < bm ? -1 : 1;
4126
+ }
4127
+ }
4128
+ return 0;
4129
+ }
4130
+ /**
4131
+ * Live coverage at native date+provider+model resolution.
4132
+ * Source universe: model_invocations; enrichment optional via LEFT JOIN.
4133
+ */
4134
+ queryLiveDailyCostCoverage(startDate, endDate) {
4135
+ const params = [];
4136
+ let whereClause = "WHERE 1=1";
4137
+ if (startDate) {
4138
+ whereClause += " AND i.timestamp >= ?";
4139
+ params.push(utcDateStartMs(startDate));
4140
+ }
4141
+ if (endDate) {
4142
+ whereClause += " AND i.timestamp < ?";
4143
+ params.push(utcDateStartMs(endDate) + DAY_MS);
4144
+ }
4145
+ const query = `
4146
+ SELECT
4147
+ date(i.timestamp / 1000, 'unixepoch') as date,
4148
+ i.provider_id as provider,
4149
+ i.model_id as model,
4150
+ COUNT(*) as execution_count,
4151
+ SUM(CASE WHEN (${SNAPSHOT_FIRST_COST_CENTS_SQL}) IS NOT NULL THEN 1 ELSE 0 END) as priced_count,
4152
+ SUM(CASE WHEN ${NON_METERED_COST_SQL} THEN 1 ELSE 0 END) as non_metered_count,
4153
+ SUM((${SNAPSHOT_FIRST_COST_CENTS_SQL})) as total_cost_cents,
4154
+ COALESCE(SUM(i.tokens_in), 0) as total_input_tokens,
4155
+ COALESCE(SUM(i.tokens_out), 0) as total_output_tokens
4156
+ FROM model_invocations i
4157
+ LEFT JOIN enriched_invocations e ON e.invocation_id = i.id
4158
+ ${whereClause}
4159
+ GROUP BY date, i.provider_id, i.model_id
4160
+ ORDER BY date DESC, provider ASC, model ASC
4161
+ `;
4162
+ const stmt = this.db.prepare(query);
4163
+ const rows = params.length > 0 ? stmt.all(...params) : stmt.all();
4164
+ return rows.map((row) => this.normalizeLiveDailyCostCoverageRow(row));
4165
+ }
4166
+ normalizeLiveDailyCostCoverageRow(row) {
4167
+ if (row === null || typeof row !== "object") {
4168
+ return {
4169
+ date: "",
4170
+ provider: "",
4171
+ model: "",
4172
+ execution_count: 0,
4173
+ priced_count: 0,
4174
+ total_cost_cents: null,
4175
+ total_input_tokens: 0,
4176
+ total_output_tokens: 0
4177
+ };
4178
+ }
4179
+ const dateValue = Reflect.get(row, "date");
4180
+ const providerValue = Reflect.get(row, "provider");
4181
+ const modelValue = Reflect.get(row, "model");
4182
+ const totalCostCentsValue = Reflect.get(row, "total_cost_cents");
4183
+ const totalCostCents = totalCostCentsValue == null ? null : Number(totalCostCentsValue);
4184
+ return {
4185
+ date: typeof dateValue === "string" ? dateValue : String(dateValue ?? ""),
4186
+ provider: typeof providerValue === "string" ? providerValue : String(providerValue ?? ""),
4187
+ model: typeof modelValue === "string" ? modelValue : String(modelValue ?? ""),
4188
+ execution_count: Number(Reflect.get(row, "execution_count")) || 0,
4189
+ priced_count: Number(Reflect.get(row, "priced_count")) || 0,
4190
+ non_metered_count: Number(Reflect.get(row, "non_metered_count")) || 0,
4191
+ total_cost_cents: totalCostCents === null || Number.isNaN(totalCostCents) ? null : totalCostCents,
4192
+ total_input_tokens: Number(Reflect.get(row, "total_input_tokens")) || 0,
4193
+ total_output_tokens: Number(Reflect.get(row, "total_output_tokens")) || 0
4194
+ };
4195
+ }
4196
+ buildDailyCostRowsFromLive(live) {
4197
+ return live.map((row) => {
4198
+ const execCount = Number(row.execution_count) || 0;
4199
+ const priced = Number(row.priced_count) || 0;
4200
+ const rowComplete = execCount > 0 && priced + (row.non_metered_count ?? 0) === execCount;
4201
+ const totalCost = rowComplete ? (Number(row.total_cost_cents) || 0) / 100 : null;
4202
+ const avgCostPerExecution = rowComplete && execCount > 0 && totalCost !== null ? totalCost / execCount : null;
4203
+ return {
4204
+ date: row.date,
4205
+ provider: row.provider,
4206
+ model: row.model,
4207
+ executionCount: execCount,
4208
+ pricedCount: priced,
4209
+ totalTokensIn: Number(row.total_input_tokens) || 0,
4210
+ totalTokensOut: Number(row.total_output_tokens) || 0,
4211
+ totalCost,
4212
+ avgCostPerExecution,
4213
+ status: rowComplete ? "complete" : "incomplete"
4214
+ };
4215
+ });
4216
+ }
4217
+ buildDailyCostResultFromRows(rows) {
4218
+ let executionCount = 0;
4219
+ let pricedCount = 0;
4220
+ let anyIncomplete = false;
4221
+ for (const row of rows) {
4222
+ executionCount += row.executionCount;
4223
+ pricedCount += row.pricedCount;
4224
+ if (row.status === "incomplete") {
4225
+ anyIncomplete = true;
4226
+ }
4227
+ }
4228
+ return {
4229
+ status: anyIncomplete ? "incomplete" : "complete",
4230
+ rows,
4231
+ executionCount,
4232
+ pricedCount,
4233
+ ...anyIncomplete ? { detail: `incomplete cost evidence (${pricedCount}/${executionCount} priced)` } : {}
4234
+ };
4235
+ }
4236
+ /**
4237
+ * Compose live and legacy rows at storage-native date+provider+model keys.
4238
+ * Live is authoritative for its exact native key only; legacy-only keys remain.
4239
+ */
4240
+ mergeLiveAndLegacyAtNativeKey(live, legacyRows) {
4241
+ const liveRows = this.buildDailyCostRowsFromLive(live);
4242
+ const liveKeys = new Set(liveRows.map((row) => this.nativeDailyCostKey(row)));
4243
+ const legacyOnly = legacyRows.filter((row) => !liveKeys.has(this.nativeDailyCostKey(row)));
4244
+ return [...liveRows, ...legacyOnly];
4245
+ }
4246
+ /**
4247
+ * Aggregate storage-native rows to the requested date/provider/model grouping.
4248
+ * A group total/average is numeric only when every constituent native row is
4249
+ * complete; partial subtotals are never exposed.
4250
+ */
4251
+ aggregateNativeDailyCostRows(nativeRows, groupBy) {
4252
+ const groups = /* @__PURE__ */ new Map();
4253
+ for (const row of nativeRows) {
4254
+ const key = this.dailyCostAggregationKey(row, groupBy);
4255
+ let group = groups.get(key);
4256
+ if (!group) {
4257
+ group = {
4258
+ date: row.date,
4259
+ ...groupBy === "provider" ? { provider: row.provider } : {},
4260
+ ...groupBy === "model" ? { model: row.model } : {},
4261
+ members: []
4262
+ };
4263
+ groups.set(key, group);
4264
+ }
4265
+ group.members.push(row);
4266
+ }
4267
+ const aggregated = [];
4268
+ for (const group of groups.values()) {
4269
+ let executionCount = 0;
4270
+ let pricedCount = 0;
4271
+ let totalTokensIn = 0;
4272
+ let totalTokensOut = 0;
4273
+ let allComplete = true;
4274
+ let totalCostSum = 0;
4275
+ for (const member of group.members) {
4276
+ executionCount += member.executionCount;
4277
+ pricedCount += member.pricedCount;
4278
+ totalTokensIn += member.totalTokensIn;
4279
+ totalTokensOut += member.totalTokensOut;
4280
+ if (member.status !== "complete" || member.totalCost === null) {
4281
+ allComplete = false;
4282
+ } else {
4283
+ totalCostSum += member.totalCost;
4284
+ }
4285
+ }
4286
+ const complete = allComplete && executionCount > 0;
4287
+ const totalCost = complete ? totalCostSum : null;
4288
+ const avgCostPerExecution = complete && executionCount > 0 && totalCost !== null ? totalCost / executionCount : null;
4289
+ aggregated.push({
4290
+ date: group.date,
4291
+ provider: group.provider,
4292
+ model: group.model,
4293
+ executionCount,
4294
+ pricedCount,
4295
+ totalTokensIn,
4296
+ totalTokensOut,
4297
+ totalCost,
4298
+ avgCostPerExecution,
4299
+ status: complete ? "complete" : "incomplete"
4300
+ });
4301
+ }
4302
+ return aggregated.sort((a, b) => this.compareDailyCostAggregationRows(a, b, groupBy));
4303
+ }
4304
+ /**
4305
+ * Pre-seeded cost_aggregations_daily fixtures at storage-native resolution
4306
+ * (date, provider, model). Treated as complete; pricedCount equals
4307
+ * executionCount. Callers that also have live coverage must merge at the
4308
+ * native key so live remains authoritative for exact collisions only.
4309
+ */
4310
+ queryPreseededDailyCostAggregationsNative(startDate, endDate) {
4311
+ const params = [];
4312
+ let whereClause = " WHERE 1=1";
4313
+ if (startDate) {
4314
+ whereClause += " AND date >= ?";
4315
+ params.push(startDate);
4316
+ }
4317
+ if (endDate) {
4318
+ whereClause += " AND date <= ?";
4319
+ params.push(endDate);
4320
+ }
4321
+ const query = `
4322
+ SELECT
4323
+ date,
4324
+ provider,
4325
+ model,
4326
+ execution_count,
4327
+ total_cost_usd,
4328
+ total_input_tokens,
4329
+ total_output_tokens,
4330
+ avg_cost_per_execution
4331
+ FROM cost_aggregations_daily
4332
+ ${whereClause}
4333
+ ORDER BY date DESC, provider ASC, model ASC
4334
+ `;
4335
+ const stmt = this.db.prepare(query);
4336
+ const rawRows = params.length > 0 ? stmt.all(...params) : stmt.all();
4337
+ return rawRows.map((raw) => this.normalizePreseededDailyCostAggregateRow(raw));
4338
+ }
4339
+ normalizePreseededDailyCostAggregateRow(raw) {
4340
+ if (raw === null || typeof raw !== "object") {
4341
+ return {
4342
+ date: "",
4343
+ executionCount: 0,
4344
+ pricedCount: 0,
4345
+ totalTokensIn: 0,
4346
+ totalTokensOut: 0,
4347
+ totalCost: 0,
4348
+ avgCostPerExecution: 0,
4349
+ status: "complete"
4350
+ };
4351
+ }
4352
+ const dateValue = Reflect.get(raw, "date");
4353
+ const providerValue = Reflect.get(raw, "provider");
4354
+ const modelValue = Reflect.get(raw, "model");
4355
+ const avgValue = Reflect.get(raw, "avg_cost_per_execution");
4356
+ const execCount = Number(Reflect.get(raw, "execution_count")) || 0;
4357
+ const totalCost = Number(Reflect.get(raw, "total_cost_usd")) || 0;
4358
+ const avgFromRow = avgValue === null || avgValue === void 0 ? null : Number(avgValue);
4359
+ const avgCostPerExecution = avgFromRow !== null && !Number.isNaN(avgFromRow) ? avgFromRow : execCount > 0 ? totalCost / execCount : 0;
4360
+ const provider = typeof providerValue === "string" ? providerValue : void 0;
4361
+ const model = typeof modelValue === "string" ? modelValue : void 0;
4362
+ return {
4363
+ date: typeof dateValue === "string" ? dateValue : String(dateValue ?? ""),
4364
+ ...provider !== void 0 ? { provider } : {},
4365
+ ...model !== void 0 ? { model } : {},
4366
+ executionCount: execCount,
4367
+ pricedCount: execCount,
4368
+ totalTokensIn: Number(Reflect.get(raw, "total_input_tokens")) || 0,
4369
+ totalTokensOut: Number(Reflect.get(raw, "total_output_tokens")) || 0,
4370
+ totalCost,
4371
+ avgCostPerExecution,
4372
+ status: "complete"
4373
+ };
4374
+ }
4375
+ /**
4376
+ * Determine if telemetry contains non-metered billing records
4377
+ */
4378
+ hasNonMeteredBilling() {
4379
+ try {
4380
+ const stmt = this.db.prepare(`
4381
+ SELECT 1 FROM enriched_invocations
4382
+ WHERE billing_mode IS NOT NULL AND billing_mode != 'metered'
4383
+ LIMIT 1
4384
+ `);
4385
+ const row = stmt.get();
4386
+ return Boolean(row);
4387
+ } catch (error) {
4388
+ emitWarning(
4389
+ "WS-003",
4390
+ {
4391
+ taskId: "system",
4392
+ subsystem: "telemetry",
4393
+ executionPhase: "execution"
4394
+ },
4395
+ {
4396
+ severity: "WARN" /* WARN */,
4397
+ metadata: {
4398
+ operation: "query_non_metered_billing",
4399
+ error: error instanceof Error ? error.message : String(error)
4400
+ }
4401
+ }
4402
+ );
4403
+ return false;
4404
+ }
4405
+ }
4406
+ // ============================================================================
4407
+ // HYBRID STORAGE: Cost Tracking with Tokens + Snapshot
4408
+ // ============================================================================
4409
+ /**
4410
+ * Record an execution cost with hybrid storage (tokens + cost snapshot)
4411
+ * Stores both raw usage data (immutable) and cost snapshot (historical accuracy)
4412
+ */
4413
+ recordExecutionCost(record) {
4414
+ const stmt = this.db.prepare(`
4415
+ INSERT OR REPLACE INTO enriched_invocations (
4416
+ id,
4417
+ invocation_id,
4418
+ computed_cost_cents,
4419
+ pricing_source,
4420
+ enriched_at,
4421
+ billing_mode,
4422
+ usage_data,
4423
+ cost_snapshot,
4424
+ pricing_version,
4425
+ task_id,
4426
+ competition_id,
4427
+ variant_id
4428
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
4429
+ `);
4430
+ const enrichedId = `enrich_${Date.now()}_${randomBytes(8).toString("hex")}`;
4431
+ const enrichedAt = record.timestamp.getTime();
4432
+ stmt.run(
4433
+ enrichedId,
4434
+ record.executionId,
4435
+ record.costSnapshot.totalCostCents,
4436
+ record.pricingSource,
4437
+ enrichedAt,
4438
+ "metered",
4439
+ // Default to metered for new records
4440
+ JSON.stringify(record.usage),
4441
+ JSON.stringify(record.costSnapshot),
4442
+ record.pricingVersion,
4443
+ record.taskId ?? null,
4444
+ record.competitionId ?? null,
4445
+ record.variantId ?? null
4446
+ );
4447
+ }
4448
+ /**
4449
+ * Get execution cost record by execution ID
4450
+ * Reconstructs full ExecutionCostRecord from hybrid storage
4451
+ */
4452
+ getExecutionCost(executionId) {
4453
+ const stmt = this.db.prepare(`
4454
+ SELECT ${EXECUTION_COST_COLUMNS}
4455
+ FROM enriched_invocations e
4456
+ JOIN model_invocations i ON e.invocation_id = i.id
4457
+ WHERE e.invocation_id = ?
4458
+ `);
4459
+ const row = stmt.get(executionId);
4460
+ if (!row) {
4461
+ return null;
4462
+ }
4463
+ const usage = row.usage_data ? parseExecutionCostUsage(row.usage_data) : null;
4464
+ const resolvedCostSnapshot = resolveExecutionCostSnapshot(row);
4465
+ if (!usage || !resolvedCostSnapshot) {
4466
+ return null;
4467
+ }
4468
+ return projectExecutionCostRecord(row, usage, resolvedCostSnapshot);
4469
+ }
4470
+ /**
4471
+ * Recalculate cost for an execution using current pricing
4472
+ * Requires calculator function to be passed in to avoid circular dependency
4473
+ *
4474
+ * @param executionId - Execution ID to recalculate
4475
+ * @param calculateCost - Cost calculation function (provider, model, usage) => cost in cents
4476
+ * @returns New cost in cents, or null if execution not found or missing usage data
4477
+ */
4478
+ recalculateCost(executionId, calculateCost) {
4479
+ const record = this.getExecutionCost(executionId);
4480
+ if (!record) {
4481
+ return null;
4482
+ }
4483
+ try {
4484
+ const newCostCents = calculateCost(record.provider, record.model, record.usage);
4485
+ return newCostCents;
4486
+ } catch (error) {
4487
+ emitWarning(
4488
+ "WS-003",
4489
+ {
4490
+ taskId: "system",
4491
+ subsystem: "telemetry",
4492
+ executionPhase: "execution"
4493
+ },
4494
+ {
4495
+ severity: "WARN" /* WARN */,
4496
+ metadata: {
4497
+ operation: "recalculate_cost",
4498
+ executionId,
4499
+ error: error instanceof Error ? error.message : String(error)
4500
+ }
4501
+ }
4502
+ );
4503
+ return null;
4504
+ }
4505
+ }
4506
+ /**
4507
+ * Get all execution costs for a task
4508
+ */
4509
+ getTaskExecutionCosts(taskId) {
4510
+ const stmt = this.db.prepare(`
4511
+ SELECT ${EXECUTION_COST_COLUMNS}
4512
+ FROM enriched_invocations e
4513
+ JOIN model_invocations i ON e.invocation_id = i.id
4514
+ WHERE e.task_id = ?
4515
+ ORDER BY i.timestamp ASC
4516
+ `);
4517
+ const rows = stmt.all(taskId);
4518
+ const mappedRecords = rows.map((row) => {
4519
+ const usage = row.usage_data ? parseExecutionCostUsage(row.usage_data) : null;
4520
+ const resolvedCostSnapshot = resolveExecutionCostSnapshot(row);
4521
+ if (!usage || !resolvedCostSnapshot) {
4522
+ return null;
4523
+ }
4524
+ return projectExecutionCostRecord(row, usage, resolvedCostSnapshot);
4525
+ });
4526
+ return mappedRecords.filter((record) => record !== null);
4527
+ }
4528
+ /**
4529
+ * Get all execution costs for a competition
4530
+ */
4531
+ getCompetitionExecutionCosts(competitionId) {
4532
+ const stmt = this.db.prepare(`
4533
+ SELECT ${EXECUTION_COST_COLUMNS}
4534
+ FROM enriched_invocations e
4535
+ JOIN model_invocations i ON e.invocation_id = i.id
4536
+ WHERE e.competition_id = ?
4537
+ ORDER BY i.timestamp ASC
4538
+ `);
4539
+ const rows = stmt.all(competitionId);
4540
+ const mappedRecords = rows.map((row) => {
4541
+ const usage = row.usage_data ? parseExecutionCostUsage(row.usage_data) : null;
4542
+ const resolvedCostSnapshot = resolveExecutionCostSnapshot(row);
4543
+ if (!usage || !resolvedCostSnapshot) {
4544
+ return null;
4545
+ }
4546
+ return projectExecutionCostRecord(row, usage, resolvedCostSnapshot);
4547
+ });
4548
+ return mappedRecords.filter((record) => record !== null);
4549
+ }
4550
+ /**
4551
+ * Count invocations that have not yet been enriched with cost data.
4552
+ */
4553
+ countUnenrichedInvocations() {
4554
+ const stmt = this.db.prepare(`
4555
+ SELECT COUNT(*) as count
4556
+ FROM model_invocations i
4557
+ LEFT JOIN enriched_invocations e ON i.id = e.invocation_id
4558
+ WHERE e.id IS NULL
4559
+ `);
4560
+ const row = stmt.get();
4561
+ return row?.count ?? 0;
4562
+ }
4563
+ /**
4564
+ * Count enriched invocations since a given timestamp.
4565
+ */
4566
+ countEnrichedInvocationsSince(startTimestampMs) {
4567
+ const stmt = this.db.prepare(`
4568
+ SELECT COUNT(*) as count
4569
+ FROM enriched_invocations e
4570
+ JOIN model_invocations i ON e.invocation_id = i.id
4571
+ WHERE i.timestamp >= ?
4572
+ `);
4573
+ const row = stmt.get(startTimestampMs);
4574
+ return row?.count ?? 0;
4575
+ }
4576
+ /**
4577
+ * Count enriched invocations in the window whose per-token cost is
4578
+ * unavailable (no usage evidence, no registry snapshot, or no pricing for the
4579
+ * model). Enrichment leaves `computed_cost_cents` NULL with a NULL billing
4580
+ * mode for those rows, so cost readbacks can report how many invocations a
4581
+ * total does not account for instead of implying a complete $0.
4582
+ *
4583
+ * Seat-licensed (non-metered) rows also carry a NULL cost but are excluded:
4584
+ * their absence from per-token totals is by design, not missing evidence.
4585
+ */
4586
+ countUnpricedInvocationsSince(startTimestampMs) {
4587
+ const stmt = this.db.prepare(`
4588
+ SELECT COUNT(*) as count
4589
+ FROM enriched_invocations e
4590
+ JOIN model_invocations i ON e.invocation_id = i.id
4591
+ WHERE i.timestamp >= ?
4592
+ AND e.computed_cost_cents IS NULL
4593
+ AND (e.billing_mode IS NULL OR e.billing_mode = 'metered')
4594
+ `);
4595
+ const row = stmt.get(startTimestampMs);
4596
+ return row?.count ?? 0;
4597
+ }
4598
+ /**
4599
+ * Whether any invocation in the window reported non-zero token usage in a
4600
+ * dimension the cost formula prices.
4601
+ *
4602
+ * Cache reads and writes are billed usage, and providers report them only in
4603
+ * `usage_evidence` — the raw columns stay 0. Reading the raw columns alone
4604
+ * called a cache-only window "zero usage" and then described its real,
4605
+ * positive cost as $0.
4606
+ */
4607
+ hasTokenUsageSince(startTimestampMs) {
4608
+ const stmt = this.db.prepare(`
4609
+ SELECT 1
4610
+ FROM model_invocations i
4611
+ LEFT JOIN usage_evidence u ON u.invocation_id = i.id
4612
+ WHERE i.timestamp >= ?
4613
+ AND (
4614
+ COALESCE(i.tokens_in, 0) > 0
4615
+ OR COALESCE(i.tokens_out, 0) > 0
4616
+ OR COALESCE(u.input_tokens, 0) > 0
4617
+ OR COALESCE(u.output_tokens, 0) > 0
4618
+ OR COALESCE(u.cache_read_tokens, 0) > 0
4619
+ OR COALESCE(u.cache_write_tokens, 0) > 0
4620
+ )
4621
+ LIMIT 1
4622
+ `);
4623
+ return Boolean(stmt.get(startTimestampMs));
4624
+ }
4625
+ /**
4626
+ * How many unpriced invocations in the window did record token counts.
4627
+ *
4628
+ * An explicit 0 counts as recorded: such a row is unpriced because pricing
4629
+ * could not be resolved (no snapshot, provider, model, or rates), not because
4630
+ * usage was never reported, and cost output explains the two differently. A
4631
+ * count rather than a flag so a window holding both kinds reports each.
4632
+ */
4633
+ countUnpricedWithTokenEvidenceSince(startTimestampMs) {
4634
+ const stmt = this.db.prepare(`
4635
+ SELECT COUNT(DISTINCT i.id) as count
4636
+ FROM enriched_invocations e
4637
+ JOIN model_invocations i ON e.invocation_id = i.id
4638
+ LEFT JOIN usage_evidence u ON u.invocation_id = i.id
4639
+ WHERE i.timestamp >= ?
4640
+ AND e.computed_cost_cents IS NULL
4641
+ AND (e.billing_mode IS NULL OR e.billing_mode = 'metered')
4642
+ AND (
4643
+ i.tokens_in IS NOT NULL
4644
+ OR i.tokens_out IS NOT NULL
4645
+ OR u.input_tokens IS NOT NULL
4646
+ OR u.output_tokens IS NOT NULL
4647
+ OR u.cache_read_tokens IS NOT NULL
4648
+ OR u.cache_write_tokens IS NOT NULL
4649
+ )
4650
+ `);
4651
+ const row = stmt.get(startTimestampMs);
4652
+ return row?.count ?? 0;
4653
+ }
4654
+ /**
4655
+ * Count raw model invocations since a given timestamp (before cost enrichment).
4656
+ */
4657
+ countRawInvocationsSince(startTimestampMs) {
4658
+ const stmt = this.db.prepare(`
4659
+ SELECT COUNT(*) as count
4660
+ FROM model_invocations
4661
+ WHERE timestamp >= ?
4662
+ `);
4663
+ const row = stmt.get(startTimestampMs);
4664
+ return row?.count ?? 0;
4665
+ }
4666
+ /**
4667
+ * Get execution costs since a given timestamp
4668
+ */
4669
+ getExecutionCostsSince(startTimestampMs) {
4670
+ const stmt = this.db.prepare(`
4671
+ SELECT ${EXECUTION_COST_COLUMNS},
4672
+ i.tokens_in,
4673
+ i.tokens_out
4674
+ FROM enriched_invocations e
4675
+ JOIN model_invocations i ON e.invocation_id = i.id
4676
+ WHERE i.timestamp >= ?
4677
+ ORDER BY i.timestamp ASC
4678
+ `);
4679
+ const rows = stmt.all(startTimestampMs);
4680
+ const mappedRecords = rows.map((row) => {
4681
+ const usage = row.usage_data ? parseExecutionCostUsage(row.usage_data) : {
4682
+ inputTokens: row.tokens_in ?? 0,
4683
+ outputTokens: row.tokens_out ?? 0
4684
+ };
4685
+ const resolvedCostSnapshot = resolveExecutionCostSnapshot(row);
4686
+ if (!resolvedCostSnapshot) {
4687
+ return null;
4688
+ }
4689
+ return projectExecutionCostRecord(row, usage, resolvedCostSnapshot);
4690
+ });
4691
+ return mappedRecords.filter((record) => record !== null);
4692
+ }
4693
+ /**
4694
+ * Get total cost for a task from cost snapshots.
4695
+ *
4696
+ * Returns a status-bearing amount in cents (as stored on enriched rows).
4697
+ * Known zero is valid only when priced rows exist and sum to 0.
4698
+ * Empty match set, all-null evidence, and query failures do not collapse to bare 0.
4699
+ */
4700
+ getTaskTotalCost(taskId) {
4701
+ try {
4702
+ const stmt = this.db.prepare(`
4703
+ SELECT
4704
+ ${SNAPSHOT_FIRST_COST_CENTS_SQL} AS cost_cents
4705
+ FROM enriched_invocations e
4706
+ WHERE e.task_id = ?
4707
+ `);
4708
+ const rows = stmt.all(taskId);
4709
+ if (rows.length === 0) {
4710
+ return unavailableCostAmount(
4711
+ "cents",
4712
+ ENRICHED_INVOCATIONS_SOURCE,
4713
+ `no enriched cost rows for task=${taskId}`
4714
+ );
4715
+ }
4716
+ let totalCents = 0;
4717
+ let pricedCount = 0;
4718
+ for (const row of rows) {
4719
+ if (row.cost_cents === null || row.cost_cents === void 0) {
4720
+ continue;
4721
+ }
4722
+ if (!Number.isFinite(row.cost_cents)) {
4723
+ return unavailableCostAmount(
4724
+ "cents",
4725
+ ENRICHED_INVOCATIONS_SOURCE,
4726
+ `non-finite cost evidence for task=${taskId}`
4727
+ );
4728
+ }
4729
+ pricedCount += 1;
4730
+ totalCents += row.cost_cents;
4731
+ }
4732
+ if (pricedCount === 0) {
4733
+ return unavailableCostAmount(
4734
+ "cents",
4735
+ ENRICHED_INVOCATIONS_SOURCE,
4736
+ `null or missing cost evidence for task=${taskId}`
4737
+ );
4738
+ }
4739
+ if (pricedCount < rows.length) {
4740
+ return unavailableCostAmount(
4741
+ "cents",
4742
+ ENRICHED_INVOCATIONS_SOURCE,
4743
+ `partial cost evidence for task=${taskId} (${pricedCount}/${rows.length} priced)`
4744
+ );
4745
+ }
4746
+ return availableCostAmount(totalCents, "cents", ENRICHED_INVOCATIONS_SOURCE);
4747
+ } catch (error) {
4748
+ const message = error instanceof Error ? error.message : String(error);
4749
+ return errorCostAmount(
4750
+ "cents",
4751
+ ENRICHED_INVOCATIONS_SOURCE,
4752
+ message,
4753
+ `query failure for task=${taskId}`
4754
+ );
4755
+ }
4756
+ }
4757
+ /**
4758
+ * Get cost breakdown by provider for a competition.
4759
+ *
4760
+ * Status-bearing (C11): empty match set, null/partial evidence, and query
4761
+ * failures never collapse to an empty map or bare numeric zero. Known zero is
4762
+ * returned only when priced rows exist and sum to 0. Zero-valued providers are
4763
+ * retained rather than omitted.
4764
+ */
4765
+ getCompetitionCostBreakdown(competitionId) {
4766
+ try {
4767
+ const stmt = this.db.prepare(`
4768
+ SELECT
4769
+ i.provider_id,
4770
+ SUM(${SNAPSHOT_FIRST_COST_CENTS_SQL}) as total_cost_cents,
4771
+ COUNT(*) as row_count,
4772
+ SUM(CASE WHEN ${SNAPSHOT_FIRST_COST_CENTS_SQL} IS NOT NULL THEN 1 ELSE 0 END) as priced_count
4773
+ FROM enriched_invocations e
4774
+ JOIN model_invocations i ON e.invocation_id = i.id
4775
+ WHERE e.competition_id = ?
4776
+ GROUP BY i.provider_id
4777
+ `);
4778
+ const rows = stmt.all(competitionId);
4779
+ if (rows.length === 0) {
4780
+ const total2 = unavailableCostAmount(
4781
+ "cents",
4782
+ ENRICHED_INVOCATIONS_SOURCE,
4783
+ `no enriched cost rows for competition=${competitionId}`
4784
+ );
4785
+ return {
4786
+ status: "unavailable",
4787
+ byProvider: {},
4788
+ total: total2,
4789
+ source: ENRICHED_INVOCATIONS_SOURCE,
4790
+ detail: total2.detail
4791
+ };
4792
+ }
4793
+ const byProvider = {};
4794
+ let allAvailable = true;
4795
+ let totalCents = 0;
4796
+ const partialDetails = [];
4797
+ for (const row of rows) {
4798
+ const rowCount = row.row_count ?? 0;
4799
+ const pricedCount = row.priced_count ?? 0;
4800
+ const totalCostCents = row.total_cost_cents;
4801
+ if (rowCount === 0) {
4802
+ byProvider[row.provider_id] = unavailableCostAmount(
4803
+ "cents",
4804
+ ENRICHED_INVOCATIONS_SOURCE,
4805
+ `no rows for provider=${row.provider_id} competition=${competitionId}`
4806
+ );
4807
+ allAvailable = false;
4808
+ continue;
4809
+ }
4810
+ if (pricedCount === 0 || totalCostCents === null || totalCostCents === void 0) {
4811
+ byProvider[row.provider_id] = unavailableCostAmount(
4812
+ "cents",
4813
+ ENRICHED_INVOCATIONS_SOURCE,
4814
+ `null or missing cost evidence for provider=${row.provider_id} competition=${competitionId}`
4815
+ );
4816
+ allAvailable = false;
4817
+ partialDetails.push(`${row.provider_id}:null`);
4818
+ continue;
4819
+ }
4820
+ if (pricedCount < rowCount) {
4821
+ byProvider[row.provider_id] = unavailableCostAmount(
4822
+ "cents",
4823
+ ENRICHED_INVOCATIONS_SOURCE,
4824
+ `partial cost evidence for provider=${row.provider_id} competition=${competitionId} (${pricedCount}/${rowCount} priced)`
4825
+ );
4826
+ allAvailable = false;
4827
+ partialDetails.push(`${row.provider_id}:${pricedCount}/${rowCount}`);
4828
+ continue;
4829
+ }
4830
+ byProvider[row.provider_id] = availableCostAmount(
4831
+ totalCostCents,
4832
+ "cents",
4833
+ ENRICHED_INVOCATIONS_SOURCE
4834
+ );
4835
+ totalCents += totalCostCents;
4836
+ }
4837
+ if (!allAvailable) {
4838
+ const detail = partialDetails.length > 0 ? `partial or null cost evidence for competition=${competitionId} (${partialDetails.join(", ")})` : `incomplete cost evidence for competition=${competitionId}`;
4839
+ const total2 = unavailableCostAmount("cents", ENRICHED_INVOCATIONS_SOURCE, detail);
4840
+ return {
4841
+ status: "unavailable",
4842
+ byProvider,
4843
+ total: total2,
4844
+ source: ENRICHED_INVOCATIONS_SOURCE,
4845
+ detail
4846
+ };
4847
+ }
4848
+ const total = availableCostAmount(totalCents, "cents", ENRICHED_INVOCATIONS_SOURCE);
4849
+ return {
4850
+ status: "available",
4851
+ byProvider,
4852
+ total,
4853
+ source: ENRICHED_INVOCATIONS_SOURCE
4854
+ };
4855
+ } catch (error) {
4856
+ const message = error instanceof Error ? error.message : String(error);
4857
+ const total = errorCostAmount(
4858
+ "cents",
4859
+ ENRICHED_INVOCATIONS_SOURCE,
4860
+ message,
4861
+ `query failure for competition=${competitionId}`
4862
+ );
4863
+ return {
4864
+ status: "error",
4865
+ byProvider: {},
4866
+ total,
4867
+ source: ENRICHED_INVOCATIONS_SOURCE,
4868
+ error: message,
4869
+ detail: total.detail
4870
+ };
4871
+ }
4872
+ }
4873
+ };
4874
+ function generateInvocationId() {
4875
+ return `inv_${Date.now()}_${randomBytes(8).toString("hex")}`;
4876
+ }
4877
+ function resolveInvocationsDbPath(buildDir) {
4878
+ const dbDir = TelemetryPathResolver.getInstance().resolveDbDir(buildDir);
4879
+ const telemetryDir = path5.basename(dbDir) === "db" ? path5.dirname(dbDir) : dbDir;
4880
+ return path5.join(telemetryDir, "invocations.db");
4881
+ }
4882
+
4883
+ export {
4884
+ NON_METERED_COST_SQL,
4885
+ SNAPSHOT_FIRST_COST_CENTS_SQL,
4886
+ pricingSourceFromRow,
4887
+ billingSourceFromRow,
4888
+ pricingConfidenceFromRow,
4889
+ longContextFromRow,
4890
+ rateCardFromPricingRow,
4891
+ speedTiersFromRow,
4892
+ rateCardFromModelPricing,
4893
+ cursorModelCacheDir,
4894
+ discoverCursorModels,
4895
+ getCachedCursorModelIds,
4896
+ buildCanonicalMappings,
4897
+ getCachedCanonicalMappings,
4898
+ CURSOR_FALLBACK_MODELS,
4899
+ PassthroughResolver,
4900
+ resolvePassthroughPricing,
4901
+ CACHE_WRITE_LONG_TOKENS_DIMENSION,
4902
+ boundRequestEvidence,
4903
+ listPriceUsageFromRecord,
4904
+ computeListPrice,
4905
+ modelEvidenceAvailability,
4906
+ invocationProvenanceClauses,
4907
+ invocationAnalyticsQualityClauses,
4908
+ invocationProvenanceWhereSql,
4909
+ isInvocationMetricQualityRow,
4910
+ createInvocationAnalyticsViews,
4911
+ resolveCompatibleInvocationModelId,
4912
+ invocationModelMatchesSameModel,
4913
+ ModelInvocationsDB,
4914
+ generateInvocationId,
4915
+ resolveInvocationsDbPath,
4916
+ observedSpeedAvailability,
4917
+ requestEvidenceAvailability,
4918
+ requestedServiceTierAvailability,
4919
+ prepareInvocationRegistry,
4920
+ captureInvocationRegistryVersion,
4921
+ ensurePendingInvocationCostEnrichment,
4922
+ listInvocationProviderIdsSince,
4923
+ summarizeInvocationCostVisibility,
4924
+ availableCostAmount,
4925
+ unavailableCostAmount,
4926
+ errorCostAmount,
4927
+ isAvailableCostAmount,
4928
+ costAmountOrNull,
4929
+ projectCostVisibility,
4930
+ fromVisibilitySummary,
4931
+ formatCostProjectionNotice
4932
+ };
4933
+ //# sourceMappingURL=chunk-Q64IR2Z2.js.map