@poetic-ai/poetic 1.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/SECURITY.md +47 -0
- package/.nvmrc +1 -0
- package/.poetic/README.md +37 -0
- package/.poetic/providers/catalog.json +12051 -0
- package/.poetic/providers/pricing.json +1272 -0
- package/.poetic/providers/registry.json +3369 -0
- package/CHANGELOG.md +552 -0
- package/CODE_OF_CONDUCT.md +40 -0
- package/CONTRIBUTING.md +23 -0
- package/INSTALL.md +454 -0
- package/LICENSE +21 -0
- package/README.md +474 -0
- package/dist/BasicOptimizationCompetitionRunner-5H5TBYO4.js +153 -0
- package/dist/ExecutionTracker-UVONC4C6.js +16 -0
- package/dist/OptimizationConfig-7DFY2TST.js +17 -0
- package/dist/OptimizationEngine-JIMGP3LY.js +1597 -0
- package/dist/PromptStore-U6ZD3ZMQ.js +18 -0
- package/dist/actions-LUJPINLJ.js +322 -0
- package/dist/agent-ingest-3DJUJ7FU.js +53 -0
- package/dist/aggregator-DNCBINFO.js +12 -0
- package/dist/ai-judge-5QT5QJVU.js +156 -0
- package/dist/allowlist-grounding-RO6HKUFI.js +16 -0
- package/dist/allowlist-utils-23V7TJ3G.js +28 -0
- package/dist/anchored-turn-service-JULUCUIK.js +489 -0
- package/dist/api-transport-4TYTNLTJ.js +1087 -0
- package/dist/apply-completion-mode-AW2MOS3R.js +89 -0
- package/dist/artifact-migrator-23L45ISD.js +267 -0
- package/dist/ask-HWVV6QFR.js +135 -0
- package/dist/auth-5KEDKAPP.js +67 -0
- package/dist/auth-7TT3JINV.js +78 -0
- package/dist/auth-E6XUNZ5M.js +68 -0
- package/dist/auth-H3XDZE22.js +134 -0
- package/dist/auth-OC4E6633.js +71 -0
- package/dist/auth-S4CTX4GQ.js +44 -0
- package/dist/auth-YLH3S7EY.js +69 -0
- package/dist/auth-liveness-TDDEZY3T.js +46 -0
- package/dist/auto-optimizer-VPRWOWGU.js +80 -0
- package/dist/autoloop-cleanup-JOGKTLWA.js +109 -0
- package/dist/autoloop-evidence-gates-PCDALGVJ.js +38 -0
- package/dist/autoloop-gate-policy-Z7FVJVXV.js +175 -0
- package/dist/autoloop-helpers-2KTK43GL.js +22 -0
- package/dist/autoloop-ledger-W7QO62PX.js +96 -0
- package/dist/autoloop-ledger-subscriber-Z5RW5EIE.js +162 -0
- package/dist/autoloop-run-defaults-FERGRZGP.js +19 -0
- package/dist/autoloop-run-lock-CT2NWSV3.js +197 -0
- package/dist/autoloop-success-J2VKKBXQ.js +85 -0
- package/dist/autoloop-termination-summary-RN75F422.js +58 -0
- package/dist/autoloop-trajectory-XFIEXP5Z.js +282 -0
- package/dist/autoloop-wall-clock-T7ZVIQOC.js +12 -0
- package/dist/autonomous-loop-controller-PW4XVFGQ.js +4120 -0
- package/dist/backend-P3DAYRP7.js +29 -0
- package/dist/background-executor-CBN552V4.js +257 -0
- package/dist/backlog-execution-intent-U4XYPTV6.js +15 -0
- package/dist/backup-active-store-5WXG6SXT.js +17 -0
- package/dist/backup-telemetry-db-XGLXAA3N.js +254 -0
- package/dist/basic-competition-master-VCSBJVRN.js +344 -0
- package/dist/branch-archive-manager-RP6FP3ZC.js +254 -0
- package/dist/branch-cleanup-manager-RSHAI7YR.js +20 -0
- package/dist/build-gate-YHGRB4QB.js +71 -0
- package/dist/change-summary-WY7J5TOX.js +33 -0
- package/dist/chunk-23A26CSN.js +123 -0
- package/dist/chunk-27UTJD3M.js +265 -0
- package/dist/chunk-2A5CILN3.js +310 -0
- package/dist/chunk-2AQUWJPW.js +225 -0
- package/dist/chunk-2BZ53J6J.js +2344 -0
- package/dist/chunk-2DY5KNVM.js +276 -0
- package/dist/chunk-2EIA4RBE.js +659 -0
- package/dist/chunk-2IUXIGQD.js +1064 -0
- package/dist/chunk-2N42MS2Z.js +2193 -0
- package/dist/chunk-2QJ3H3L7.js +42 -0
- package/dist/chunk-2QLYTVYX.js +791 -0
- package/dist/chunk-2VSQMRCB.js +18 -0
- package/dist/chunk-2XQXYBM2.js +130 -0
- package/dist/chunk-2YFUIER7.js +426 -0
- package/dist/chunk-34CNP2HB.js +148 -0
- package/dist/chunk-35P3W3JX.js +456 -0
- package/dist/chunk-36PEBZAF.js +18 -0
- package/dist/chunk-3AOKYKA7.js +22 -0
- package/dist/chunk-3BCUNHKF.js +475 -0
- package/dist/chunk-3FLXLLB7.js +26 -0
- package/dist/chunk-3FOZ2VHV.js +100 -0
- package/dist/chunk-3G6FZCRF.js +106 -0
- package/dist/chunk-3M57DADF.js +44 -0
- package/dist/chunk-3S62LLWJ.js +543 -0
- package/dist/chunk-3ZPQLGV7.js +2904 -0
- package/dist/chunk-44N5WZZH.js +45 -0
- package/dist/chunk-45FPSA3B.js +125 -0
- package/dist/chunk-4BJ3O4RQ.js +929 -0
- package/dist/chunk-4BOP7T6L.js +2321 -0
- package/dist/chunk-4BTDUT2S.js +42 -0
- package/dist/chunk-4CSS7WVI.js +143 -0
- package/dist/chunk-4CUY7AFY.js +247 -0
- package/dist/chunk-4GSN5O5J.js +62 -0
- package/dist/chunk-4HN6DF7V.js +1027 -0
- package/dist/chunk-4IAPOG5V.js +415 -0
- package/dist/chunk-4IYIZQWV.js +48 -0
- package/dist/chunk-4NXPXE62.js +84 -0
- package/dist/chunk-4RCCF5BR.js +712 -0
- package/dist/chunk-4V7NAQ4R.js +19527 -0
- package/dist/chunk-4VJITV4P.js +445 -0
- package/dist/chunk-4YX7J6QL.js +1859 -0
- package/dist/chunk-527OBWQB.js +338 -0
- package/dist/chunk-5D4A7E3D.js +448 -0
- package/dist/chunk-5DLKYTQX.js +4003 -0
- package/dist/chunk-5EERLVZB.js +139 -0
- package/dist/chunk-5H72XE4H.js +256 -0
- package/dist/chunk-5HDP7XZG.js +91 -0
- package/dist/chunk-5LAMRH4X.js +969 -0
- package/dist/chunk-5LTN43AS.js +1972 -0
- package/dist/chunk-5RBHQYDB.js +2206 -0
- package/dist/chunk-5WCQRDRB.js +588 -0
- package/dist/chunk-5WRLK5KW.js +700 -0
- package/dist/chunk-5ZCXC23G.js +595 -0
- package/dist/chunk-6BM72ZMG.js +31 -0
- package/dist/chunk-6EDAJH2I.js +68 -0
- package/dist/chunk-6EQKFDBH.js +457 -0
- package/dist/chunk-6MFRHTRI.js +115 -0
- package/dist/chunk-6TZJRKNW.js +321 -0
- package/dist/chunk-6V7HGQFZ.js +20 -0
- package/dist/chunk-6VWVO2TU.js +3746 -0
- package/dist/chunk-6Y5TWI7H.js +146 -0
- package/dist/chunk-6Y5U7UFY.js +91 -0
- package/dist/chunk-6ZLAK3XG.js +14 -0
- package/dist/chunk-72XCRQED.js +34 -0
- package/dist/chunk-72YTZMAM.js +589 -0
- package/dist/chunk-73BBJOKA.js +263 -0
- package/dist/chunk-73NCTWKL.js +188 -0
- package/dist/chunk-77I6G4CO.js +1 -0
- package/dist/chunk-7DHRDWFS.js +3104 -0
- package/dist/chunk-7DNSNKJV.js +78 -0
- package/dist/chunk-7FMJVYBS.js +307 -0
- package/dist/chunk-7GDRQBQC.js +419 -0
- package/dist/chunk-7GPCEFUV.js +270 -0
- package/dist/chunk-7IL4A2PP.js +948 -0
- package/dist/chunk-7MMLMXO6.js +3679 -0
- package/dist/chunk-7QCZSLBH.js +1 -0
- package/dist/chunk-7R2WLDOS.js +62 -0
- package/dist/chunk-7T7RUJI7.js +949 -0
- package/dist/chunk-A2WKSVX7.js +43 -0
- package/dist/chunk-A3YGID55.js +133 -0
- package/dist/chunk-A722DEVA.js +14 -0
- package/dist/chunk-ACDIT7OP.js +1376 -0
- package/dist/chunk-ACKIPAOJ.js +302 -0
- package/dist/chunk-APPNGGN7.js +365 -0
- package/dist/chunk-APV6MK5E.js +137 -0
- package/dist/chunk-ASEP3M2W.js +968 -0
- package/dist/chunk-AT4TNPWW.js +2931 -0
- package/dist/chunk-ATVKCSGT.js +250 -0
- package/dist/chunk-AURFMVCD.js +899 -0
- package/dist/chunk-AX6GWE7V.js +47 -0
- package/dist/chunk-AY5EWJPF.js +385 -0
- package/dist/chunk-BAV2HJXS.js +194 -0
- package/dist/chunk-BBHRN366.js +89 -0
- package/dist/chunk-BBPSTTPA.js +1004 -0
- package/dist/chunk-BDATUDDJ.js +992 -0
- package/dist/chunk-BKE5PG5L.js +20414 -0
- package/dist/chunk-BLPHWMDK.js +797 -0
- package/dist/chunk-BSE4R6XD.js +322 -0
- package/dist/chunk-BXGMAU4A.js +396 -0
- package/dist/chunk-BXI4NXL3.js +507 -0
- package/dist/chunk-BZU2O43F.js +1125 -0
- package/dist/chunk-C3L6YQ7P.js +147 -0
- package/dist/chunk-C57KJOUQ.js +121 -0
- package/dist/chunk-CCV7BPSY.js +118 -0
- package/dist/chunk-CD7ISXA4.js +826 -0
- package/dist/chunk-CE5OZBYY.js +21 -0
- package/dist/chunk-CFBIG37O.js +154 -0
- package/dist/chunk-CJC6GZ46.js +457 -0
- package/dist/chunk-CKCDGXIV.js +408 -0
- package/dist/chunk-CKNX3TTP.js +10338 -0
- package/dist/chunk-CLKSETWE.js +10 -0
- package/dist/chunk-CP7H3HSP.js +1552 -0
- package/dist/chunk-CPLEWXNZ.js +66 -0
- package/dist/chunk-CQM3A35X.js +844 -0
- package/dist/chunk-CS6P76D3.js +103 -0
- package/dist/chunk-CTLUBNCW.js +622 -0
- package/dist/chunk-CUSRN6RJ.js +632 -0
- package/dist/chunk-D5EP5D2W.js +26 -0
- package/dist/chunk-D5RYINO7.js +412 -0
- package/dist/chunk-DBFS4HMH.js +751 -0
- package/dist/chunk-DC4ZYMXJ.js +115 -0
- package/dist/chunk-DCBZP442.js +9243 -0
- package/dist/chunk-DCYF7EKG.js +26 -0
- package/dist/chunk-DDMAFNUK.js +5351 -0
- package/dist/chunk-DF5SLDE4.js +966 -0
- package/dist/chunk-DJFW7SMX.js +348 -0
- package/dist/chunk-DQFLLBJW.js +121 -0
- package/dist/chunk-DSFM56OA.js +1203 -0
- package/dist/chunk-DTY4APYV.js +218 -0
- package/dist/chunk-DXH5JBRS.js +437 -0
- package/dist/chunk-E3NTPHEO.js +194 -0
- package/dist/chunk-E74LUIID.js +302 -0
- package/dist/chunk-EI57M5BI.js +194 -0
- package/dist/chunk-EJ4ZAWLV.js +305 -0
- package/dist/chunk-EJGCZGUF.js +1483 -0
- package/dist/chunk-EK2U3YEG.js +486 -0
- package/dist/chunk-ELMFYNV7.js +109 -0
- package/dist/chunk-ENRGKNR5.js +25 -0
- package/dist/chunk-ETYDXCK7.js +679 -0
- package/dist/chunk-EYRH3W74.js +239 -0
- package/dist/chunk-F23DIWNN.js +1024 -0
- package/dist/chunk-F5JO7HAS.js +94 -0
- package/dist/chunk-F7ER6ANO.js +174 -0
- package/dist/chunk-FATUVIIN.js +374 -0
- package/dist/chunk-FC23CTXV.js +322 -0
- package/dist/chunk-FD4ERYEG.js +22 -0
- package/dist/chunk-FH7WOJAE.js +79 -0
- package/dist/chunk-FHOWVDF5.js +1342 -0
- package/dist/chunk-FKAUPHMR.js +96 -0
- package/dist/chunk-FONNFJXA.js +137 -0
- package/dist/chunk-FQ7FSZRW.js +75 -0
- package/dist/chunk-FRG57FPM.js +240 -0
- package/dist/chunk-FUVP64ER.js +31 -0
- package/dist/chunk-FYOILAN5.js +6818 -0
- package/dist/chunk-G35UYQ6Z.js +69 -0
- package/dist/chunk-G36W2T2S.js +1143 -0
- package/dist/chunk-G3L2L5XZ.js +49 -0
- package/dist/chunk-G4THLFV3.js +148 -0
- package/dist/chunk-GAFJEYLK.js +29 -0
- package/dist/chunk-GFGYB2ZU.js +1689 -0
- package/dist/chunk-GG6KJYW6.js +85 -0
- package/dist/chunk-GHHFDU2U.js +55 -0
- package/dist/chunk-GHTZSQB7.js +57 -0
- package/dist/chunk-GK37ILCX.js +1231 -0
- package/dist/chunk-GKN3HMN4.js +2809 -0
- package/dist/chunk-GNVDBVJP.js +156 -0
- package/dist/chunk-GPEL3FBM.js +924 -0
- package/dist/chunk-GPSNGPZS.js +1604 -0
- package/dist/chunk-GSGD5E4C.js +30 -0
- package/dist/chunk-GSUQJJXQ.js +331 -0
- package/dist/chunk-GSV226X6.js +110 -0
- package/dist/chunk-H2AYQIHW.js +224 -0
- package/dist/chunk-H66DXFS5.js +1374 -0
- package/dist/chunk-H6G7QRMQ.js +35 -0
- package/dist/chunk-H7JAGHZT.js +59 -0
- package/dist/chunk-H7ZEFECA.js +316 -0
- package/dist/chunk-HAC6EHZZ.js +79 -0
- package/dist/chunk-HIO33P3G.js +19 -0
- package/dist/chunk-HMVQIPH3.js +547 -0
- package/dist/chunk-HOYBYAOA.js +257 -0
- package/dist/chunk-HX5P3CSP.js +1784 -0
- package/dist/chunk-I46EG2XQ.js +65 -0
- package/dist/chunk-I4NPDSCB.js +279 -0
- package/dist/chunk-I5WRT7ZR.js +514 -0
- package/dist/chunk-IC7I6YIJ.js +301 -0
- package/dist/chunk-IDCSOZVN.js +78 -0
- package/dist/chunk-IFCBYECK.js +112 -0
- package/dist/chunk-IFZCPEY2.js +66 -0
- package/dist/chunk-IKFP2KUR.js +48 -0
- package/dist/chunk-IRFBN336.js +878 -0
- package/dist/chunk-IUXNGKA6.js +152 -0
- package/dist/chunk-IV2RVAN7.js +1356 -0
- package/dist/chunk-IZYIQQ7X.js +53 -0
- package/dist/chunk-J3UUR7DE.js +210 -0
- package/dist/chunk-J54VUAY2.js +566 -0
- package/dist/chunk-J62KPGII.js +464 -0
- package/dist/chunk-J6P3FWWW.js +130 -0
- package/dist/chunk-JKBHJQMT.js +434 -0
- package/dist/chunk-JLS65NUQ.js +14 -0
- package/dist/chunk-JOSYEJKY.js +141 -0
- package/dist/chunk-JPKSXKNB.js +94 -0
- package/dist/chunk-JQNJB2BB.js +2440 -0
- package/dist/chunk-JRVNSWSQ.js +2626 -0
- package/dist/chunk-JSWMRQYG.js +28 -0
- package/dist/chunk-JZKNBBIB.js +152 -0
- package/dist/chunk-K54Y7MLK.js +160 -0
- package/dist/chunk-K65546RE.js +183 -0
- package/dist/chunk-KM3F7SYE.js +702 -0
- package/dist/chunk-KNDYMVWN.js +102 -0
- package/dist/chunk-KNT72VR6.js +132 -0
- package/dist/chunk-KP6YILMU.js +482 -0
- package/dist/chunk-KQEBSK3T.js +274 -0
- package/dist/chunk-KT6VHSPF.js +84 -0
- package/dist/chunk-KTMW65UX.js +14 -0
- package/dist/chunk-KZ5CJVBP.js +1813 -0
- package/dist/chunk-L4427KOE.js +26 -0
- package/dist/chunk-L6OM4A22.js +189 -0
- package/dist/chunk-L7E6XCRA.js +1042 -0
- package/dist/chunk-L7JZLXQL.js +173 -0
- package/dist/chunk-LAMKBTXX.js +40 -0
- package/dist/chunk-LDCF2MKN.js +132 -0
- package/dist/chunk-LEIIWPWQ.js +78 -0
- package/dist/chunk-LFTQVYBB.js +31 -0
- package/dist/chunk-LG3PDRTR.js +2872 -0
- package/dist/chunk-LGHII5ET.js +23 -0
- package/dist/chunk-LH5QY22X.js +1205 -0
- package/dist/chunk-LIVJ6266.js +438 -0
- package/dist/chunk-LKRAR3IH.js +276 -0
- package/dist/chunk-LO23NABI.js +3063 -0
- package/dist/chunk-LO3X5NLS.js +326 -0
- package/dist/chunk-LPITVM7M.js +339 -0
- package/dist/chunk-LR5IJIGV.js +810 -0
- package/dist/chunk-LRGMHROI.js +79 -0
- package/dist/chunk-LUVKFFWR.js +315 -0
- package/dist/chunk-LWCBNGH6.js +35 -0
- package/dist/chunk-LWTN3WRV.js +260 -0
- package/dist/chunk-M3HQYKQX.js +308 -0
- package/dist/chunk-M6GVSPEH.js +1016 -0
- package/dist/chunk-MF5HP6XV.js +221 -0
- package/dist/chunk-MHLCBZJB.js +328 -0
- package/dist/chunk-MN6PL4AN.js +120 -0
- package/dist/chunk-MOEY65Z6.js +5678 -0
- package/dist/chunk-MQ4WA34C.js +206 -0
- package/dist/chunk-MWHF5V7U.js +223 -0
- package/dist/chunk-MWHLBPNU.js +211 -0
- package/dist/chunk-MWK5UZQ3.js +1068 -0
- package/dist/chunk-MYLH2S4G.js +64 -0
- package/dist/chunk-MZ5WYKNA.js +28 -0
- package/dist/chunk-N4VTRA7K.js +131 -0
- package/dist/chunk-N5DX4JAK.js +12 -0
- package/dist/chunk-N65O4XTH.js +5011 -0
- package/dist/chunk-NEQUQHOX.js +223 -0
- package/dist/chunk-NGK6PEOG.js +25 -0
- package/dist/chunk-NJVD4PWE.js +1033 -0
- package/dist/chunk-NM6PMYY4.js +768 -0
- package/dist/chunk-NN4PJDHP.js +708 -0
- package/dist/chunk-NP2V3L7K.js +226 -0
- package/dist/chunk-NRLY536M.js +260 -0
- package/dist/chunk-NRNE225N.js +1270 -0
- package/dist/chunk-NS2K2GTP.js +605 -0
- package/dist/chunk-NS33V3IM.js +51 -0
- package/dist/chunk-NSHASMZZ.js +71 -0
- package/dist/chunk-NWOYOA6H.js +378 -0
- package/dist/chunk-NWRUSEXG.js +322 -0
- package/dist/chunk-NZGDAWIP.js +1905 -0
- package/dist/chunk-O47UOKU3.js +395 -0
- package/dist/chunk-O4G2W3ST.js +9831 -0
- package/dist/chunk-O6V4PNLO.js +63 -0
- package/dist/chunk-OA4GPILI.js +624 -0
- package/dist/chunk-OGMSYL6J.js +227 -0
- package/dist/chunk-OIS7J26S.js +319 -0
- package/dist/chunk-OMX5VL43.js +79 -0
- package/dist/chunk-ON3G73BU.js +264 -0
- package/dist/chunk-OPYFYTWQ.js +220 -0
- package/dist/chunk-OQ6K46CK.js +1734 -0
- package/dist/chunk-OYZYO7TV.js +47 -0
- package/dist/chunk-OZ4REHIN.js +4386 -0
- package/dist/chunk-P47VN5F4.js +282 -0
- package/dist/chunk-P5I4X7A7.js +353 -0
- package/dist/chunk-P6VQROCO.js +594 -0
- package/dist/chunk-P7CQGPLS.js +30 -0
- package/dist/chunk-PCAGC5MR.js +1763 -0
- package/dist/chunk-PFTQBXS6.js +5181 -0
- package/dist/chunk-PGSPX4SU.js +15 -0
- package/dist/chunk-PIKLF7BM.js +432 -0
- package/dist/chunk-PJWJ3SAV.js +22 -0
- package/dist/chunk-PKIFMV72.js +100 -0
- package/dist/chunk-PL37JDNA.js +189 -0
- package/dist/chunk-PTH5E5XO.js +195 -0
- package/dist/chunk-PW6AXLQC.js +4128 -0
- package/dist/chunk-PYITM4N2.js +79 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-Q3RO7N35.js +45 -0
- package/dist/chunk-Q3WIC6GQ.js +2670 -0
- package/dist/chunk-Q5XIXSHX.js +271 -0
- package/dist/chunk-QGJDMEOV.js +34 -0
- package/dist/chunk-QNOB37UH.js +58 -0
- package/dist/chunk-QOCOVLUH.js +462 -0
- package/dist/chunk-QTCJ5CC5.js +172 -0
- package/dist/chunk-QVFV5IP2.js +232 -0
- package/dist/chunk-QVZMFDYG.js +29 -0
- package/dist/chunk-R35PEUKH.js +511 -0
- package/dist/chunk-R4UWBC35.js +200 -0
- package/dist/chunk-R6MLCREW.js +44 -0
- package/dist/chunk-R6W23LKN.js +254 -0
- package/dist/chunk-R7XDDX2A.js +4007 -0
- package/dist/chunk-RDFRCT64.js +168 -0
- package/dist/chunk-RE5VZDFQ.js +58 -0
- package/dist/chunk-RGROYC2G.js +638 -0
- package/dist/chunk-RGWHUI5E.js +68 -0
- package/dist/chunk-RHVG6UNL.js +205 -0
- package/dist/chunk-RI4EX2QE.js +18 -0
- package/dist/chunk-RJAAKDRX.js +17 -0
- package/dist/chunk-ROMHOSMQ.js +185 -0
- package/dist/chunk-RWREC3KJ.js +517 -0
- package/dist/chunk-RYNETRDJ.js +118 -0
- package/dist/chunk-RZY7RKM5.js +500 -0
- package/dist/chunk-S2MNWFAG.js +52 -0
- package/dist/chunk-S2VQCZO4.js +22 -0
- package/dist/chunk-S54MKU6V.js +117 -0
- package/dist/chunk-S5OIDFRM.js +302 -0
- package/dist/chunk-SCW4ZF6R.js +150 -0
- package/dist/chunk-SLS2N4PH.js +2213 -0
- package/dist/chunk-SNLBO4BF.js +247 -0
- package/dist/chunk-SP3LFU6B.js +60 -0
- package/dist/chunk-SQQTDDQA.js +537 -0
- package/dist/chunk-STV6LYYE.js +45 -0
- package/dist/chunk-SURZ2WFE.js +326 -0
- package/dist/chunk-SZ7DL357.js +1047 -0
- package/dist/chunk-T5E4NQCM.js +5742 -0
- package/dist/chunk-TAJPCXSB.js +13 -0
- package/dist/chunk-TAOOYK3P.js +358 -0
- package/dist/chunk-TDSEX5CF.js +24 -0
- package/dist/chunk-TFTGP7Z7.js +174 -0
- package/dist/chunk-TGON4N4O.js +197 -0
- package/dist/chunk-TGQZHDY2.js +2373 -0
- package/dist/chunk-TJJZ2OKV.js +6783 -0
- package/dist/chunk-TRSVUPCX.js +25 -0
- package/dist/chunk-TSXGRLPR.js +107 -0
- package/dist/chunk-U2S3YEX3.js +224 -0
- package/dist/chunk-U45RTRGY.js +818 -0
- package/dist/chunk-U6C3VVWS.js +13 -0
- package/dist/chunk-UDMS5AD3.js +307 -0
- package/dist/chunk-UGX7GI37.js +94 -0
- package/dist/chunk-UH3JJSHK.js +1 -0
- package/dist/chunk-UHRBTZYY.js +208 -0
- package/dist/chunk-UHT2KRG5.js +193 -0
- package/dist/chunk-UI2F6DJ5.js +118 -0
- package/dist/chunk-UMAKSB4Z.js +284 -0
- package/dist/chunk-UR2IBGIM.js +521 -0
- package/dist/chunk-UUH2RVKS.js +585 -0
- package/dist/chunk-UVJ4PJDK.js +4533 -0
- package/dist/chunk-V53UG2OT.js +9924 -0
- package/dist/chunk-V72RMBM4.js +48 -0
- package/dist/chunk-VBNCKCCI.js +16 -0
- package/dist/chunk-VNSYKC25.js +24 -0
- package/dist/chunk-VPHVFK4A.js +1984 -0
- package/dist/chunk-VTLNJQ44.js +133 -0
- package/dist/chunk-VTW7HP3R.js +100 -0
- package/dist/chunk-VVGEPBPS.js +218 -0
- package/dist/chunk-VVLLL7I4.js +166 -0
- package/dist/chunk-VX3UMWB7.js +483 -0
- package/dist/chunk-VXF4Q3FW.js +20 -0
- package/dist/chunk-W3JMT2YY.js +712 -0
- package/dist/chunk-WDUORIHF.js +877 -0
- package/dist/chunk-WDVTY4U6.js +148 -0
- package/dist/chunk-WIFZCHEG.js +12566 -0
- package/dist/chunk-WIRN3F75.js +736 -0
- package/dist/chunk-WMPU2UOV.js +82 -0
- package/dist/chunk-WTOKKE2V.js +784 -0
- package/dist/chunk-WX5IRLF6.js +740 -0
- package/dist/chunk-WZ6U3O7I.js +1581 -0
- package/dist/chunk-XAONGNST.js +57 -0
- package/dist/chunk-XB5YLCLB.js +13 -0
- package/dist/chunk-XEP5NQ5Y.js +688 -0
- package/dist/chunk-XGYF2QMZ.js +351 -0
- package/dist/chunk-XHEKQENB.js +255 -0
- package/dist/chunk-XNO4FM6X.js +1124 -0
- package/dist/chunk-XPHKYHO4.js +878 -0
- package/dist/chunk-XPK2I4NC.js +405 -0
- package/dist/chunk-XPU2XADL.js +2175 -0
- package/dist/chunk-XQD2B6BB.js +507 -0
- package/dist/chunk-XT2ZQVTC.js +558 -0
- package/dist/chunk-XTXJGGUV.js +351 -0
- package/dist/chunk-XUXVDPSZ.js +50 -0
- package/dist/chunk-XV7NZL4B.js +57 -0
- package/dist/chunk-XZE4XARH.js +563 -0
- package/dist/chunk-XZYR5EDO.js +152 -0
- package/dist/chunk-Y3J4JMTU.js +61 -0
- package/dist/chunk-Y55LZN5U.js +52 -0
- package/dist/chunk-Y5VN7DMP.js +112 -0
- package/dist/chunk-Y67VAHN4.js +459 -0
- package/dist/chunk-YAXSKEUH.js +3544 -0
- package/dist/chunk-YB3JD4E6.js +975 -0
- package/dist/chunk-YESZJFK6.js +220 -0
- package/dist/chunk-YGXKOBQQ.js +292 -0
- package/dist/chunk-YMX76IOS.js +31 -0
- package/dist/chunk-YOTXESEH.js +257 -0
- package/dist/chunk-YTYIEZQY.js +119 -0
- package/dist/chunk-YVORHQ2S.js +579 -0
- package/dist/chunk-Z2CUQDAR.js +3429 -0
- package/dist/chunk-Z2KYOFHS.js +7000 -0
- package/dist/chunk-Z6PC7QX6.js +161 -0
- package/dist/chunk-ZDTGECTN.js +235 -0
- package/dist/chunk-ZIPWI2LY.js +151 -0
- package/dist/chunk-ZSKKMTQJ.js +554 -0
- package/dist/chunk-ZTT5IVPY.js +1160 -0
- package/dist/chunk-ZX65UI5W.js +78 -0
- package/dist/chunk-ZYG7GGKO.js +218 -0
- package/dist/chunk-ZZZUL4OF.js +38 -0
- package/dist/cli-utils-IXL26KJT.js +40 -0
- package/dist/cli-validation-WLXWLUJL.js +111 -0
- package/dist/commit-utils-P7CRJF5N.js +202 -0
- package/dist/compete-config-resolver-R5ULXMYH.js +76 -0
- package/dist/compete-request-62JESVET.js +364 -0
- package/dist/compete-results-QEPG6FZX.js +208 -0
- package/dist/competition-outcome-tracker-R32OOZ43.js +38 -0
- package/dist/config-JCOKXOQT.js +166 -0
- package/dist/config-audit-EL3GHXS7.js +360 -0
- package/dist/config-explain-TNILDEVJ.js +12 -0
- package/dist/config-loader-VPFPFO4B.js +38 -0
- package/dist/config-manager-7T4E2BQO.js +67 -0
- package/dist/config-validator-KKOHOW7O.js +72 -0
- package/dist/cost-YC42XUF3.js +53 -0
- package/dist/coverage-orchestrator-CISNKJC2.js +772 -0
- package/dist/coverage-scanner-YVD62MLH.js +12 -0
- package/dist/createCompetitionRunner-LJG6ZQSO.js +46 -0
- package/dist/data-migration-state-VJK64DLT.js +31 -0
- package/dist/db-migrator-XSEIOGWY.js +34 -0
- package/dist/discovery-JGYGDD25.js +25 -0
- package/dist/doctor-GJOYE7AE.js +186 -0
- package/dist/domain-analyzer-AWF3RP6E.js +29 -0
- package/dist/ensure-initialized-744DIZEZ.js +52 -0
- package/dist/entry.js +940 -0
- package/dist/environment-YS45U5ZI.js +81 -0
- package/dist/error-parser-core-PPZ36A4S.js +50 -0
- package/dist/escalation-ladder-MKTCEGSA.js +130 -0
- package/dist/evaluation-context-KV4GZNN7.js +35 -0
- package/dist/evidence-plan-resolver-GBSYSAPX.js +85 -0
- package/dist/execution-data-writer-3ODTBDLH.js +53 -0
- package/dist/execution-policy-applier-ZHD3DCF3.js +31 -0
- package/dist/execution-preflight-Z4Y64V3I.js +209 -0
- package/dist/execution-roles-R3DPPMD3.js +66 -0
- package/dist/exit-code-error-Z4SW2DVK.js +14 -0
- package/dist/external-temp-cleanup-N2RZH4QP.js +311 -0
- package/dist/factory-XBN7DFIM.js +149 -0
- package/dist/file-utils-XIVR2ZMA.js +47 -0
- package/dist/flywheel-autoloop-executor-UILGOFFK.js +249 -0
- package/dist/flywheel-competition-executor-JOBEXTRB.js +259 -0
- package/dist/flywheel-executor-6JFLA6J5.js +369 -0
- package/dist/flywheel-git-isolation-UC5NIEVU.js +48 -0
- package/dist/flywheel-manifest-5UC4VBMA.js +108 -0
- package/dist/flywheel-model-defaults-7CVKP53V.js +27 -0
- package/dist/flywheel-preflight-XEY5SHM6.js +104 -0
- package/dist/flywheel-resume-7HELH7QS.js +23 -0
- package/dist/flywheel-safety-7U7DPOAP.js +28 -0
- package/dist/flywheel-scope-decision-ZHKF6ZIN.js +11 -0
- package/dist/get-telemetry-logger-DPVWWIYY.js +73 -0
- package/dist/git-worktree-IY6V6DUO.js +18 -0
- package/dist/github-pr-manager-4LXEKJTA.js +211 -0
- package/dist/guidance-profile-config-E7XJL2VP.js +117 -0
- package/dist/guidance-profile-renderer-QK2YMAAP.js +21 -0
- package/dist/index-query-4JEUKA44.js +31 -0
- package/dist/index.js +108661 -0
- package/dist/instructions-6BURLQJC.js +64 -0
- package/dist/invoke-provider-auth-GRUYP44M.js +223 -0
- package/dist/judge-scoring.json +80 -0
- package/dist/judge-test.js +555 -0
- package/dist/lab-mode-OTWVHN63.js +39 -0
- package/dist/lab-utils-BJRFLGK4.js +23 -0
- package/dist/linux-host-class-UY4KVLFD.js +29 -0
- package/dist/llm-judge-executor-F7Z3HO77.js +133 -0
- package/dist/local-executor-CEHWE2PL.js +223 -0
- package/dist/logger-33TR6EE5.js +72 -0
- package/dist/loop-spec-XTLQAVZ7.js +191 -0
- package/dist/manager-2HJG5CLT.js +25 -0
- package/dist/matrix-prompt-builder-6UDN5X3F.js +497 -0
- package/dist/matrix-result-parser-7W35EUOV.js +15 -0
- package/dist/metadata-HQ43NDEK.js +22 -0
- package/dist/model-invocations-db-QBO4Y7WZ.js +38 -0
- package/dist/monitor-XGYBG2DW.js +72 -0
- package/dist/next-PB5ZXZKB.js +90 -0
- package/dist/next-ZBDK725T.js +69 -0
- package/dist/optimization-config-resolver-O6WKR47U.js +75 -0
- package/dist/package-3YCWIQQ5.js +10 -0
- package/dist/parallel-orchestrator-NL7YJJU2.js +297 -0
- package/dist/parallel-worker.js +279 -0
- package/dist/parse-git-status-PIAF3OTP.js +10 -0
- package/dist/path-security-ETTH6TZL.js +39 -0
- package/dist/pattern-bank-UNJR5ADV.js +15 -0
- package/dist/plan-backlog-list-fast-VAGYBV3J.js +270 -0
- package/dist/plan-backlog-show-fast-SG2CZPW3.js +406 -0
- package/dist/plan-sprint-list-fast-LURAX47V.js +162 -0
- package/dist/plan-sprint-status-fast-FJAODCHD.js +208 -0
- package/dist/plan-task-status-fast-HNYBMQI4.js +349 -0
- package/dist/plan-to-flywheel-Z4U3GWPN.js +456 -0
- package/dist/planning-backlog-3CQ5QQBQ.js +319 -0
- package/dist/poetic-root-V5DNXEAE.js +17 -0
- package/dist/preflight-OPANWMME.js +12 -0
- package/dist/process-registry-PDZJPTAS.js +27 -0
- package/dist/process-scanner-C22SYXYD.js +22 -0
- package/dist/processes-KMQUD7HY.js +29 -0
- package/dist/processes-json-fast-IBDXZ6WW.js +135 -0
- package/dist/profile-SE5EVXAP.js +149 -0
- package/dist/prompts-WZZPGWBX.js +168 -0
- package/dist/protected-branches-RNTEX7ET.js +57 -0
- package/dist/provider-aware-resource-manager-QBTZJMNP.js +18 -0
- package/dist/provider-matrix-N5X57V3Y.js +154 -0
- package/dist/provider-matrix-renderer-MZDWKKID.js +139 -0
- package/dist/provider-output-forwarding-DERID3TP.js +30 -0
- package/dist/provider-registry-J5NRBJKI.js +128 -0
- package/dist/provider-temp-cleanup-KYGLF6YU.js +14 -0
- package/dist/prune-engine-Q6E2DXBN.js +451 -0
- package/dist/quality-gate-YRHMQAEI.js +70 -0
- package/dist/quickstart-FIRLKGJI.js +62 -0
- package/dist/readiness-VZTMSG6J.js +96 -0
- package/dist/registry-YODFY77A.js +153 -0
- package/dist/repair-helpers-TTV5FEF4.js +117 -0
- package/dist/repo-root-2IGD4H7X.js +22 -0
- package/dist/resolution-engine-D6TX6FRG.js +127 -0
- package/dist/resolve-provider-cli-O2P6XZBZ.js +35 -0
- package/dist/restore-telemetry-db-BQL6DTQF.js +360 -0
- package/dist/result-streamer-WEHW476R.js +19 -0
- package/dist/routing-events-DHPUZDDF.js +27 -0
- package/dist/routing-history-store-UWCEXAP3.js +46 -0
- package/dist/routing-planner-F5WQEUOM.js +160 -0
- package/dist/run-poetic-tui-J6LWRJUP.js +13517 -0
- package/dist/run-simulation.js +376 -0
- package/dist/runner-CS2TNGHA.js +1092 -0
- package/dist/runner-HHH7ZN2Z.js +1571 -0
- package/dist/safety-OCC4KJJR.js +64 -0
- package/dist/safety-stanza-7SV3HGG3.js +60 -0
- package/dist/sandbox-32QXJEMV.js +81 -0
- package/dist/sandbox-RSO7VP6V.js +94 -0
- package/dist/schema-extensions.sql +234 -0
- package/dist/security-2KI5XNDG.js +17 -0
- package/dist/setup-bb-plugin-QAQWZCV5.js +738 -0
- package/dist/setup-claude-code-P7BTTSPO.js +317 -0
- package/dist/setup-temp-cleanup-GASCWZ2I.js +20 -0
- package/dist/shared-utils-D6U6UREP.js +37 -0
- package/dist/simple-artifact-goal-OTXNGZY6.js +25 -0
- package/dist/sprint-UIXPYOXQ.js +338 -0
- package/dist/sprint-bridge-4BHMSC3M.js +68 -0
- package/dist/sprint-execution-service-2B7WHL5L.js +311 -0
- package/dist/sprint-manager-IDQUDNQI.js +91 -0
- package/dist/sqlite-wrapper-UDWCTNLJ.js +17 -0
- package/dist/stale-cleanup-orchestrator-XWXV7KCZ.js +62 -0
- package/dist/storage-access-F6ALDN4S.js +22 -0
- package/dist/synthesis-XD6EDVHF.js +185 -0
- package/dist/task-classifier-LNLXSHOM.js +494 -0
- package/dist/task-list-fast-PTMA4LUC.js +147 -0
- package/dist/task-manager-XAT7GOWB.js +93 -0
- package/dist/task-splitter-U7DJ7W6A.js +79 -0
- package/dist/task-type-detector-OLVOMXZO.js +21 -0
- package/dist/task-type-resolver-KKNQYSB4.js +42 -0
- package/dist/telemetry-5HWBBXLX.js +131 -0
- package/dist/telemetry-health-tracker-DMSSGBZ2.js +11 -0
- package/dist/telemetry-path-resolver-7ICSG276.js +28 -0
- package/dist/telemetry-reliability-XUOZNZZ6.js +125 -0
- package/dist/token-estimator-MEQUL3EJ.js +37 -0
- package/dist/transports-KNSSV2V4.js +255 -0
- package/dist/unified-cost-tracker-4TFRBVPR.js +35 -0
- package/dist/unified-state-cleanup-LJPF2BOW.js +276 -0
- package/dist/universal-cost-calculator-BAJV35GA.js +42 -0
- package/dist/user-agents-GHUGCSKN.js +22 -0
- package/dist/variant-delivery-state-KLRNG6XA.js +33 -0
- package/dist/variant-state-cleanup-MZGGLQAJ.js +280 -0
- package/dist/variant-success-PJXWE3ZV.js +12 -0
- package/dist/variant-worker-KUWKBLAH.js +4196 -0
- package/dist/variant-worker.js +24 -0
- package/dist/variants-JE2FSAQS.js +233 -0
- package/dist/verification-toolchains-V24UHW6Q.js +28 -0
- package/dist/verify-commands-H3DTCSGI.js +37 -0
- package/dist/warning-EGQGUUIO.js +12 -0
- package/dist/work-edge-RWEDWJZF.js +49 -0
- package/dist/work-item-writer-WHAVZXAY.js +19 -0
- package/dist/worker-7SA2VS2Y.js +1435 -0
- package/dist/worker.js +19 -0
- package/dist/worktree-metrics-3DFUYVYE.js +26 -0
- package/dist/writeback-VZKHYHSN.js +44 -0
- package/docs/CLI_REFERENCE.md +7042 -0
- package/docs/PROVIDER_SETUP.md +1570 -0
- package/docs/README.md +96 -0
- package/docs/TROUBLESHOOTING.md +2232 -0
- package/docs/getting-started/QUICK_START.md +294 -0
- package/docs/getting-started/README.md +188 -0
- package/docs/getting-started/SETUP.md +52 -0
- package/docs/reference/AGENT_CONTEXT.md +38 -0
- package/docs/reference/PROVIDER_RELEASE_TIERS.md +34 -0
- package/docs/reference/README.md +381 -0
- package/docs/reference/SECURITY.md +262 -0
- package/integrations/bb-plugin-poetic/README.md +362 -0
- package/integrations/bb-plugin-poetic/app.css +341 -0
- package/integrations/bb-plugin-poetic/app.tsx +5059 -0
- package/integrations/bb-plugin-poetic/package.json +45 -0
- package/integrations/bb-plugin-poetic/server.ts +444 -0
- package/integrations/bb-plugin-poetic/src/adapter.ts +3312 -0
- package/integrations/bb-plugin-poetic/src/backlog-authoring-model.ts +150 -0
- package/integrations/bb-plugin-poetic/src/backlog-authoring-schema.ts +355 -0
- package/integrations/bb-plugin-poetic/src/backlog-authoring-service.ts +419 -0
- package/integrations/bb-plugin-poetic/src/backlog-authoring-view.ts +1026 -0
- package/integrations/bb-plugin-poetic/src/competition-defaults-schema.ts +122 -0
- package/integrations/bb-plugin-poetic/src/competition-defaults-service.ts +105 -0
- package/integrations/bb-plugin-poetic/src/competition-defaults-view.ts +138 -0
- package/integrations/bb-plugin-poetic/src/contract.ts +615 -0
- package/integrations/bb-plugin-poetic/src/finalize-model.ts +205 -0
- package/integrations/bb-plugin-poetic/src/finalize-schema.ts +158 -0
- package/integrations/bb-plugin-poetic/src/finalize-service.ts +382 -0
- package/integrations/bb-plugin-poetic/src/finalize-view.ts +159 -0
- package/integrations/bb-plugin-poetic/src/judge-operations-readback.ts +303 -0
- package/integrations/bb-plugin-poetic/src/judge-operations-schema.ts +33 -0
- package/integrations/bb-plugin-poetic/src/judge-operations-view.ts +459 -0
- package/integrations/bb-plugin-poetic/src/model.ts +1302 -0
- package/integrations/bb-plugin-poetic/src/monitor-service.ts +233 -0
- package/integrations/bb-plugin-poetic/src/panel-read-ux.ts +97 -0
- package/integrations/bb-plugin-poetic/src/patch-preview-schema.ts +166 -0
- package/integrations/bb-plugin-poetic/src/patch-preview-service.ts +480 -0
- package/integrations/bb-plugin-poetic/src/patch-preview-view.ts +109 -0
- package/integrations/bb-plugin-poetic/src/planning-readback.ts +204 -0
- package/integrations/bb-plugin-poetic/src/planning-service.ts +594 -0
- package/integrations/bb-plugin-poetic/src/planning-workspace-schema.ts +95 -0
- package/integrations/bb-plugin-poetic/src/planning-workspace.ts +323 -0
- package/integrations/bb-plugin-poetic/src/poll-handoff.ts +136 -0
- package/integrations/bb-plugin-poetic/src/project-target-server.ts +21 -0
- package/integrations/bb-plugin-poetic/src/project-target.ts +45 -0
- package/integrations/bb-plugin-poetic/src/reference-index.ts +192 -0
- package/integrations/bb-plugin-poetic/src/repository-schema.ts +30 -0
- package/integrations/bb-plugin-poetic/src/result-explorer-view.ts +597 -0
- package/integrations/bb-plugin-poetic/src/result-readback-model.ts +163 -0
- package/integrations/bb-plugin-poetic/src/result-readback-schema.ts +319 -0
- package/integrations/bb-plugin-poetic/src/result-readback-service.ts +333 -0
- package/integrations/bb-plugin-poetic/src/run-page-view.ts +177 -0
- package/integrations/bb-plugin-poetic/src/setup-config-schema.ts +291 -0
- package/integrations/bb-plugin-poetic/src/setup-config-service.ts +590 -0
- package/integrations/bb-plugin-poetic/src/setup-config-view.ts +413 -0
- package/integrations/bb-plugin-poetic/src/sprint-close-model.ts +150 -0
- package/integrations/bb-plugin-poetic/src/sprint-close-schema.ts +136 -0
- package/integrations/bb-plugin-poetic/src/sprint-close-service.ts +341 -0
- package/integrations/bb-plugin-poetic/src/sprint-close-view.ts +116 -0
- package/integrations/bb-plugin-poetic/src/sprint-composition-model.ts +714 -0
- package/integrations/bb-plugin-poetic/src/sprint-composition-readback.ts +231 -0
- package/integrations/bb-plugin-poetic/src/sprint-composition-schema.ts +601 -0
- package/integrations/bb-plugin-poetic/src/sprint-composition-service.ts +775 -0
- package/integrations/bb-plugin-poetic/src/sprint-composition-view.ts +1221 -0
- package/integrations/bb-plugin-poetic/src/task-authoring-model.ts +567 -0
- package/integrations/bb-plugin-poetic/src/task-authoring-schema.ts +635 -0
- package/integrations/bb-plugin-poetic/src/task-authoring-service.ts +842 -0
- package/integrations/bb-plugin-poetic/src/task-authoring-view.ts +1227 -0
- package/integrations/bb-plugin-poetic/src/task-detail-readback.ts +56 -0
- package/integrations/bb-plugin-poetic/src/task-detail-schema.ts +144 -0
- package/integrations/bb-plugin-poetic/src/task-navigation.ts +446 -0
- package/integrations/bb-plugin-poetic/src/workbench-route.ts +74 -0
- package/integrations/bb-plugin-poetic/tests/host-contract.test.ts +773 -0
- package/integrations/bb-plugin-poetic/tsconfig.json +18 -0
- package/integrations/bb-plugin-poetic/types/PROVENANCE.json +52 -0
- package/integrations/bb-plugin-poetic/types/bb-plugin-sdk-app.d.ts +1444 -0
- package/integrations/bb-plugin-poetic/types/bb-plugin-sdk.d.ts +13030 -0
- package/integrations/bb-plugin-poetic/vitest.host.config.ts +71 -0
- package/npm-shrinkwrap.json +4950 -0
- package/package.json +331 -0
- package/schemas/README.md +80 -0
- package/schemas/config-v1.schema.json +136 -0
- package/schemas/execution-config.schema.json +47 -0
- package/schemas/judge-scoring.schema.json +370 -0
- package/schemas/poetic.config.schema.json +2214 -0
- package/schemas/provider-config.schema.json +203 -0
- package/schemas/telemetry-config.schema.json +79 -0
- package/schemas/user-preferences.schema.json +141 -0
- package/scripts/assert-node-runtime.mjs +140 -0
- package/scripts/preflight-native.mjs +49 -0
- package/scripts/preinstall-node-check.mjs +78 -0
- package/scripts/setup-git-hooks.mjs +24 -0
- package/scripts/sync.sh +2722 -0
- package/scripts/write-node-launcher.sh +108 -0
- package/src/resources/gemini/slash-packs/default/plan.toml +15 -0
- package/src/resources/gemini/slash-packs/default/summary.toml +16 -0
- package/src/resources/gemini/slash-packs/default/tests.toml +16 -0
- package/templates/.poetic/README.md +37 -0
- package/templates/.poetic/agents/README.md +296 -0
- package/templates/.poetic/agents/api-documenter.md +147 -0
- package/templates/.poetic/agents/backend-architect.md +31 -0
- package/templates/.poetic/agents/code-reviewer.md +157 -0
- package/templates/.poetic/agents/data-scientist.md +179 -0
- package/templates/.poetic/agents/database-optimizer.md +145 -0
- package/templates/.poetic/agents/debugger.md +31 -0
- package/templates/.poetic/agents/deployment-engineer.md +164 -0
- package/templates/.poetic/agents/devops-troubleshooter.md +139 -0
- package/templates/.poetic/agents/frontend-developer.md +150 -0
- package/templates/.poetic/agents/javascript-pro.md +36 -0
- package/templates/.poetic/agents/performance-engineer.md +151 -0
- package/templates/.poetic/agents/python-pro.md +137 -0
- package/templates/.poetic/agents/test-automator.md +147 -0
- package/templates/.poetic/agents/typescript-pro.md +34 -0
- package/templates/.poetic/config/poetic.config.jsonc +69 -0
- package/templates/.poetic/config/project-context.template.json +6 -0
- package/templates/.poetic/config/task-type-aliases.presets/kanban.yaml +14 -0
- package/templates/.poetic/config/task-type-aliases.presets/scrum.yaml +17 -0
- package/templates/.poetic/config/task-type-aliases.presets/xp.yaml +12 -0
- package/templates/.poetic/config/task-type-aliases.yaml +28 -0
- package/templates/.poetic/gitignore.template +55 -0
- package/templates/.poetic/task-types/analysis.yaml +40 -0
- package/templates/.poetic/task-types/architecture.yaml +38 -0
- package/templates/.poetic/task-types/doc.yaml +35 -0
- package/templates/.poetic/task-types/feature.yaml +23 -0
- package/templates/.poetic/task-types/general.yaml +6 -0
- package/templates/.poetic/task-types/security.yaml +39 -0
- package/templates/AGENTS.template.md +99 -0
- package/templates/CLAUDE.template.md +1 -0
- package/templates/GEMINI.template.md +1 -0
- package/templates/builtin-workflows/code-review.yaml +73 -0
- package/templates/builtin-workflows/compete-streak.yaml +78 -0
- package/templates/builtin-workflows/hello-verify.yaml +10 -0
- package/templates/builtin-workflows/judge-regression.yaml +114 -0
- package/templates/builtin-workflows/skills/code-review/SKILL.md +60 -0
- package/templates/builtin-workflows/skills/hello-verify/SKILL.md +6 -0
- package/templates/guard-kit/GUARD_SETUP.md.template +255 -0
- package/templates/guard-kit/check.mjs.template +777 -0
- package/templates/guard-kit/config.json.template +6 -0
- package/templates/guard-kit/poetic-guard.yml.template +189 -0
- package/templates/profiles/README.md +56 -0
- package/templates/profiles/frontier-claude.json +27 -0
- package/templates/profiles/frontier-codex.json +26 -0
|
@@ -0,0 +1,2809 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseJsonWithContext
|
|
3
|
+
} from "./chunk-QVZMFDYG.js";
|
|
4
|
+
import {
|
|
5
|
+
sqlite_wrapper_default
|
|
6
|
+
} from "./chunk-45FPSA3B.js";
|
|
7
|
+
import {
|
|
8
|
+
applySqlitePragmas
|
|
9
|
+
} from "./chunk-QGJDMEOV.js";
|
|
10
|
+
import {
|
|
11
|
+
ProviderRegistry,
|
|
12
|
+
ProviderRegistryConfigSchema,
|
|
13
|
+
registerProviderRegistryReloadInvalidator
|
|
14
|
+
} from "./chunk-AT4TNPWW.js";
|
|
15
|
+
import {
|
|
16
|
+
getPackageRoot
|
|
17
|
+
} from "./chunk-XZE4XARH.js";
|
|
18
|
+
import {
|
|
19
|
+
emitWarning
|
|
20
|
+
} from "./chunk-EJGCZGUF.js";
|
|
21
|
+
|
|
22
|
+
// src/telemetry/model-invocations-db.ts
|
|
23
|
+
import { randomBytes } from "crypto";
|
|
24
|
+
import path3 from "path";
|
|
25
|
+
|
|
26
|
+
// src/telemetry/cost-precedence.ts
|
|
27
|
+
var SNAPSHOT_FIRST_COST_CENTS_SQL = `
|
|
28
|
+
CASE
|
|
29
|
+
WHEN e.cost_snapshot IS NOT NULL AND json_valid(e.cost_snapshot) THEN
|
|
30
|
+
COALESCE(
|
|
31
|
+
-- REAL (not INTEGER): snapshot costs carry sub-cent precision; an
|
|
32
|
+
-- integer cast truncates small invocations (< 1 cent) to zero.
|
|
33
|
+
CAST(json_extract(e.cost_snapshot, '$.totalCostCents') AS REAL),
|
|
34
|
+
e.computed_cost_cents
|
|
35
|
+
)
|
|
36
|
+
ELSE e.computed_cost_cents
|
|
37
|
+
END
|
|
38
|
+
`;
|
|
39
|
+
function resolveSnapshotFirstCostCents(input) {
|
|
40
|
+
const snapshotCost = input.costSnapshot?.totalCostCents;
|
|
41
|
+
if (typeof snapshotCost === "number" && Number.isFinite(snapshotCost)) {
|
|
42
|
+
return snapshotCost;
|
|
43
|
+
}
|
|
44
|
+
const computedCost = input.computedCostCents;
|
|
45
|
+
return typeof computedCost === "number" && Number.isFinite(computedCost) ? computedCost : null;
|
|
46
|
+
}
|
|
47
|
+
function finiteNumberOrZero(value) {
|
|
48
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
49
|
+
}
|
|
50
|
+
function resolveSnapshotFirstCostSnapshot(input) {
|
|
51
|
+
const totalCostCents = resolveSnapshotFirstCostCents(input);
|
|
52
|
+
if (totalCostCents === null) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const snapshot = input.costSnapshot;
|
|
56
|
+
return {
|
|
57
|
+
inputCostCents: finiteNumberOrZero(snapshot?.inputCostCents),
|
|
58
|
+
outputCostCents: finiteNumberOrZero(snapshot?.outputCostCents),
|
|
59
|
+
cacheCostCents: finiteNumberOrZero(snapshot?.cacheCostCents),
|
|
60
|
+
toolCostCents: finiteNumberOrZero(snapshot?.toolCostCents),
|
|
61
|
+
infrastructureCostCents: finiteNumberOrZero(snapshot?.infrastructureCostCents),
|
|
62
|
+
totalCostCents,
|
|
63
|
+
currency: typeof snapshot?.currency === "string" && snapshot.currency ? snapshot.currency : "USD"
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// src/telemetry/invocation-cost-enrichment.ts
|
|
68
|
+
import fs2 from "fs";
|
|
69
|
+
import path2 from "path";
|
|
70
|
+
import yaml2 from "js-yaml";
|
|
71
|
+
|
|
72
|
+
// src/core/config/model-registry.ts
|
|
73
|
+
import * as crypto from "crypto";
|
|
74
|
+
import * as fs from "fs";
|
|
75
|
+
import * as path from "path";
|
|
76
|
+
import yaml from "js-yaml";
|
|
77
|
+
import { z } from "zod";
|
|
78
|
+
var BillingModeSchema = z.enum(["metered", "flat_rate", "non_metered"]);
|
|
79
|
+
var CostSchemaZ = z.object({
|
|
80
|
+
prompt_per_1k: z.number().min(0).optional(),
|
|
81
|
+
completion_per_1k: z.number().min(0).optional(),
|
|
82
|
+
extra_fees: z.record(z.string(), z.number()).optional(),
|
|
83
|
+
billing_mode: BillingModeSchema.optional(),
|
|
84
|
+
billing_source: z.string().optional(),
|
|
85
|
+
pricing_source: z.string().optional(),
|
|
86
|
+
pricing_confidence: z.enum(["high", "medium", "low"]).optional(),
|
|
87
|
+
flat_rate_per_seat_month: z.number().min(0).optional(),
|
|
88
|
+
notes: z.string().optional()
|
|
89
|
+
}).superRefine((value, ctx) => {
|
|
90
|
+
const billingMode = value.billing_mode ?? "metered";
|
|
91
|
+
if (billingMode === "metered") {
|
|
92
|
+
if (typeof value.prompt_per_1k !== "number") {
|
|
93
|
+
ctx.addIssue({
|
|
94
|
+
code: z.ZodIssueCode.custom,
|
|
95
|
+
message: "Metered billing requires prompt_per_1k",
|
|
96
|
+
path: ["prompt_per_1k"]
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
if (typeof value.completion_per_1k !== "number") {
|
|
100
|
+
ctx.addIssue({
|
|
101
|
+
code: z.ZodIssueCode.custom,
|
|
102
|
+
message: "Metered billing requires completion_per_1k",
|
|
103
|
+
path: ["completion_per_1k"]
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
var ModelDefZ = z.object({
|
|
109
|
+
id: z.string().min(1),
|
|
110
|
+
display_name: z.string().min(1),
|
|
111
|
+
capabilities: z.array(z.string()),
|
|
112
|
+
costSchema: CostSchemaZ.optional()
|
|
113
|
+
});
|
|
114
|
+
var ProviderFrontmatterZ = z.object({
|
|
115
|
+
id: z.string().min(1),
|
|
116
|
+
display_name: z.string().min(1),
|
|
117
|
+
models: z.array(ModelDefZ).min(1, "At least one model is required"),
|
|
118
|
+
auth_hint: z.string().optional()
|
|
119
|
+
});
|
|
120
|
+
var IGNORED_PROVIDER_FILES = /* @__PURE__ */ new Set(["valid-provider.md", "minimal-provider.md"]);
|
|
121
|
+
function parseFrontmatter(content) {
|
|
122
|
+
const match = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/);
|
|
123
|
+
if (!match) {
|
|
124
|
+
throw new Error("Invalid frontmatter: must start with --- and have --- separator");
|
|
125
|
+
}
|
|
126
|
+
const [, yamlStr, markdown] = match;
|
|
127
|
+
const data = yaml.load(yamlStr);
|
|
128
|
+
return { data, content: markdown || "" };
|
|
129
|
+
}
|
|
130
|
+
function loadProviderFile(filePath) {
|
|
131
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
132
|
+
const { data } = parseFrontmatter(content);
|
|
133
|
+
const normalized = normalizeProviderFrontmatter(data, filePath);
|
|
134
|
+
const frontmatter = ProviderFrontmatterZ.parse(normalized);
|
|
135
|
+
return {
|
|
136
|
+
providerId: frontmatter.id,
|
|
137
|
+
displayName: frontmatter.display_name,
|
|
138
|
+
models: frontmatter.models.map((m) => ({
|
|
139
|
+
modelId: m.id,
|
|
140
|
+
displayName: m.display_name,
|
|
141
|
+
capabilities: m.capabilities,
|
|
142
|
+
costSchema: m.costSchema
|
|
143
|
+
})),
|
|
144
|
+
authHint: frontmatter.auth_hint
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function computeRegistryVersion(registry) {
|
|
148
|
+
const sortedProviders = [...registry.providers].sort((a, b) => a.providerId.localeCompare(b.providerId)).map((provider) => ({
|
|
149
|
+
authHint: provider.authHint,
|
|
150
|
+
displayName: provider.displayName,
|
|
151
|
+
models: [...provider.models || []].sort((a, b) => a.modelId.localeCompare(b.modelId)).map((model) => ({
|
|
152
|
+
capabilities: [...model.capabilities].sort(),
|
|
153
|
+
costSchema: model.costSchema,
|
|
154
|
+
displayName: model.displayName,
|
|
155
|
+
modelId: model.modelId
|
|
156
|
+
})),
|
|
157
|
+
providerId: provider.providerId
|
|
158
|
+
}));
|
|
159
|
+
const canonical = JSON.stringify(sortedProviders);
|
|
160
|
+
return crypto.createHash("sha256").update(canonical).digest("hex");
|
|
161
|
+
}
|
|
162
|
+
function loadProviderDirectory(providerDir) {
|
|
163
|
+
if (!fs.existsSync(providerDir)) {
|
|
164
|
+
throw new Error(`Providers directory not found: ${providerDir}`);
|
|
165
|
+
}
|
|
166
|
+
const providerMap = /* @__PURE__ */ new Map();
|
|
167
|
+
const files = fs.readdirSync(providerDir).filter((f) => f.endsWith(".md") && !IGNORED_PROVIDER_FILES.has(f));
|
|
168
|
+
if (files.length === 0) {
|
|
169
|
+
throw new Error(`No provider manifests found in ${providerDir}`);
|
|
170
|
+
}
|
|
171
|
+
for (const file of files) {
|
|
172
|
+
try {
|
|
173
|
+
const provider = loadProviderFile(path.join(providerDir, file));
|
|
174
|
+
providerMap.set(provider.providerId, provider);
|
|
175
|
+
} catch (error) {
|
|
176
|
+
throw new Error(
|
|
177
|
+
`Failed to load provider ${file}: ${error instanceof Error ? error.message : String(error)}`
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return Array.from(providerMap.values());
|
|
182
|
+
}
|
|
183
|
+
function normalizeProviderFrontmatter(data, filePath) {
|
|
184
|
+
const providerId = getFirstString(data, ["id", "provider_id"]);
|
|
185
|
+
if (!providerId) {
|
|
186
|
+
throw new Error(`Provider manifest ${filePath} is missing required id/provider_id field`);
|
|
187
|
+
}
|
|
188
|
+
const displayName = getFirstString(data, ["display_name", "name"]) ?? providerId;
|
|
189
|
+
const authHint = getFirstString(data, ["auth_hint", "auth", "authHint"]);
|
|
190
|
+
const rawModels = Array.isArray(data.models) ? data.models : [];
|
|
191
|
+
const models = rawModels.map((model, index) => normalizeModel(model, filePath, index));
|
|
192
|
+
return {
|
|
193
|
+
id: providerId,
|
|
194
|
+
display_name: displayName,
|
|
195
|
+
auth_hint: authHint,
|
|
196
|
+
models
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
function normalizeModel(model, filePath, index) {
|
|
200
|
+
if (!model || typeof model !== "object") {
|
|
201
|
+
throw new Error(`Model entry at index ${index} in ${filePath} must be an object`);
|
|
202
|
+
}
|
|
203
|
+
const id = getFirstString(model, ["id", "model_id"]);
|
|
204
|
+
if (!id) {
|
|
205
|
+
throw new Error(
|
|
206
|
+
`Model entry at index ${index} in ${filePath} is missing required id/model_id field`
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
const displayName = getFirstString(model, ["display_name", "name"]) ?? id;
|
|
210
|
+
const capabilitiesRaw = Array.isArray(model.capabilities) ? model.capabilities : [];
|
|
211
|
+
const capabilities = capabilitiesRaw.map((capability) => String(capability));
|
|
212
|
+
const costSchema = normalizeCostSchema(
|
|
213
|
+
model.costSchema,
|
|
214
|
+
model.cost
|
|
215
|
+
);
|
|
216
|
+
return {
|
|
217
|
+
id,
|
|
218
|
+
display_name: displayName,
|
|
219
|
+
capabilities,
|
|
220
|
+
costSchema
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
function normalizeCostSchema(costSchema, legacyCost) {
|
|
224
|
+
const normalized = {};
|
|
225
|
+
if (costSchema && typeof costSchema === "object") {
|
|
226
|
+
const prompt = parseCurrency(
|
|
227
|
+
costSchema.prompt_per_1k ?? costSchema.promptPer1k
|
|
228
|
+
);
|
|
229
|
+
const completion = parseCurrency(
|
|
230
|
+
costSchema.completion_per_1k ?? costSchema.completionPer1k
|
|
231
|
+
);
|
|
232
|
+
const extraFees = parseExtraFees(
|
|
233
|
+
costSchema.extra_fees ?? costSchema.extraFees
|
|
234
|
+
);
|
|
235
|
+
const billingMode = parseBillingMode(
|
|
236
|
+
costSchema.billing_mode ?? costSchema.billingMode ?? costSchema.pricing_mode
|
|
237
|
+
);
|
|
238
|
+
const flatRate = parseCurrency(
|
|
239
|
+
costSchema.flat_rate_per_seat_month ?? costSchema.flat_rate ?? costSchema.seat_price
|
|
240
|
+
);
|
|
241
|
+
const billingSource = parseOptionalString(
|
|
242
|
+
costSchema.billing_source ?? costSchema.billingSource
|
|
243
|
+
);
|
|
244
|
+
const pricingSource = parseOptionalString(
|
|
245
|
+
costSchema.pricing_source ?? costSchema.pricingSource
|
|
246
|
+
);
|
|
247
|
+
const pricingConfidence = parsePricingConfidence(
|
|
248
|
+
costSchema.pricing_confidence ?? costSchema.pricingConfidence
|
|
249
|
+
);
|
|
250
|
+
const notes = parseOptionalString(
|
|
251
|
+
costSchema.notes ?? costSchema.description
|
|
252
|
+
);
|
|
253
|
+
if (prompt !== void 0) {
|
|
254
|
+
normalized.prompt_per_1k = prompt;
|
|
255
|
+
}
|
|
256
|
+
if (completion !== void 0) {
|
|
257
|
+
normalized.completion_per_1k = completion;
|
|
258
|
+
}
|
|
259
|
+
if (extraFees) {
|
|
260
|
+
normalized.extra_fees = extraFees;
|
|
261
|
+
}
|
|
262
|
+
if (billingMode) {
|
|
263
|
+
normalized.billing_mode = billingMode;
|
|
264
|
+
}
|
|
265
|
+
if (flatRate !== void 0) {
|
|
266
|
+
normalized.flat_rate_per_seat_month = flatRate;
|
|
267
|
+
}
|
|
268
|
+
if (billingSource) {
|
|
269
|
+
normalized.billing_source = billingSource;
|
|
270
|
+
}
|
|
271
|
+
if (pricingSource) {
|
|
272
|
+
normalized.pricing_source = pricingSource;
|
|
273
|
+
}
|
|
274
|
+
if (pricingConfidence) {
|
|
275
|
+
normalized.pricing_confidence = pricingConfidence;
|
|
276
|
+
}
|
|
277
|
+
if (notes) {
|
|
278
|
+
normalized.notes = notes;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (legacyCost && typeof legacyCost === "object") {
|
|
282
|
+
const prompt = parseCurrency(
|
|
283
|
+
legacyCost.prompt_per_1k_tokens ?? legacyCost.prompt_per_1k ?? legacyCost.promptPer1k
|
|
284
|
+
);
|
|
285
|
+
const completion = parseCurrency(
|
|
286
|
+
legacyCost.completion_per_1k_tokens ?? legacyCost.completion_per_1k ?? legacyCost.completionPer1k
|
|
287
|
+
);
|
|
288
|
+
const extraFees = {};
|
|
289
|
+
const cacheRead = parseCurrency(
|
|
290
|
+
legacyCost.cache_read_per_1k_tokens ?? legacyCost.cache_read_per_1k
|
|
291
|
+
);
|
|
292
|
+
const cacheWrite = parseCurrency(
|
|
293
|
+
legacyCost.cache_write_per_1k_tokens ?? legacyCost.cache_write_per_1k
|
|
294
|
+
);
|
|
295
|
+
if (cacheRead !== void 0) {
|
|
296
|
+
extraFees.cacheReadPer1k = cacheRead;
|
|
297
|
+
}
|
|
298
|
+
if (cacheWrite !== void 0) {
|
|
299
|
+
extraFees.cacheWritePer1k = cacheWrite;
|
|
300
|
+
}
|
|
301
|
+
if (prompt !== void 0 && normalized.prompt_per_1k === void 0) {
|
|
302
|
+
normalized.prompt_per_1k = prompt;
|
|
303
|
+
}
|
|
304
|
+
if (completion !== void 0 && normalized.completion_per_1k === void 0) {
|
|
305
|
+
normalized.completion_per_1k = completion;
|
|
306
|
+
}
|
|
307
|
+
const normalizedExtraFees = Object.keys(extraFees).length > 0 ? extraFees : void 0;
|
|
308
|
+
if (normalizedExtraFees && !normalized.extra_fees) {
|
|
309
|
+
normalized.extra_fees = normalizedExtraFees;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (Object.keys(normalized).length === 0) {
|
|
313
|
+
return void 0;
|
|
314
|
+
}
|
|
315
|
+
if (!normalized.billing_mode) {
|
|
316
|
+
normalized.billing_mode = "metered";
|
|
317
|
+
}
|
|
318
|
+
if (normalized.billing_mode === "metered" && (typeof normalized.prompt_per_1k !== "number" || typeof normalized.completion_per_1k !== "number")) {
|
|
319
|
+
return void 0;
|
|
320
|
+
}
|
|
321
|
+
return normalized;
|
|
322
|
+
}
|
|
323
|
+
function parseCurrency(value) {
|
|
324
|
+
if (typeof value === "number") {
|
|
325
|
+
return value;
|
|
326
|
+
}
|
|
327
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
328
|
+
const parsed = Number(value);
|
|
329
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
330
|
+
}
|
|
331
|
+
return void 0;
|
|
332
|
+
}
|
|
333
|
+
function parseExtraFees(value) {
|
|
334
|
+
if (!value || typeof value !== "object") {
|
|
335
|
+
return void 0;
|
|
336
|
+
}
|
|
337
|
+
const entries = {};
|
|
338
|
+
for (const [key, raw] of Object.entries(value)) {
|
|
339
|
+
const parsed = parseCurrency(raw);
|
|
340
|
+
if (parsed !== void 0) {
|
|
341
|
+
entries[key] = parsed;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return Object.keys(entries).length > 0 ? entries : void 0;
|
|
345
|
+
}
|
|
346
|
+
function parseBillingMode(value) {
|
|
347
|
+
if (typeof value !== "string") {
|
|
348
|
+
return void 0;
|
|
349
|
+
}
|
|
350
|
+
const normalized = value.trim().toLowerCase();
|
|
351
|
+
if (!normalized) {
|
|
352
|
+
return void 0;
|
|
353
|
+
}
|
|
354
|
+
if (["flat", "flat_rate", "flat-rate", "seat", "seat_licensed"].includes(normalized)) {
|
|
355
|
+
return "flat_rate";
|
|
356
|
+
}
|
|
357
|
+
if (["non_metered", "non-metered", "unmetered", "license"].includes(normalized)) {
|
|
358
|
+
return "non_metered";
|
|
359
|
+
}
|
|
360
|
+
if (["metered", "per_token", "per-token"].includes(normalized)) {
|
|
361
|
+
return "metered";
|
|
362
|
+
}
|
|
363
|
+
return void 0;
|
|
364
|
+
}
|
|
365
|
+
function parsePricingConfidence(value) {
|
|
366
|
+
if (typeof value !== "string") {
|
|
367
|
+
return void 0;
|
|
368
|
+
}
|
|
369
|
+
const normalized = value.trim().toLowerCase();
|
|
370
|
+
if (normalized === "high" || normalized === "medium" || normalized === "low") {
|
|
371
|
+
return normalized;
|
|
372
|
+
}
|
|
373
|
+
return void 0;
|
|
374
|
+
}
|
|
375
|
+
function parseOptionalString(value) {
|
|
376
|
+
if (typeof value === "string") {
|
|
377
|
+
const trimmed = value.trim();
|
|
378
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
379
|
+
}
|
|
380
|
+
return void 0;
|
|
381
|
+
}
|
|
382
|
+
function getFirstString(source, keys) {
|
|
383
|
+
if (!source || typeof source !== "object") {
|
|
384
|
+
return void 0;
|
|
385
|
+
}
|
|
386
|
+
for (const key of keys) {
|
|
387
|
+
const value = source[key];
|
|
388
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
389
|
+
return value.trim();
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return void 0;
|
|
393
|
+
}
|
|
394
|
+
function buildRegistry(providers) {
|
|
395
|
+
const sorted = [...providers].sort((a, b) => a.providerId.localeCompare(b.providerId));
|
|
396
|
+
const registry = {
|
|
397
|
+
providers: sorted,
|
|
398
|
+
registryVersion: ""
|
|
399
|
+
// Placeholder, will be computed
|
|
400
|
+
};
|
|
401
|
+
const versionData = { ...registry, registryVersion: "" };
|
|
402
|
+
registry.registryVersion = computeRegistryVersion(versionData);
|
|
403
|
+
return registry;
|
|
404
|
+
}
|
|
405
|
+
function saveRegistry(registry, filePath) {
|
|
406
|
+
const dir = path.dirname(filePath);
|
|
407
|
+
if (!fs.existsSync(dir)) {
|
|
408
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
409
|
+
}
|
|
410
|
+
const tempPath = path.join(dir, `${path.basename(filePath)}.tmp-${process.pid}-${Date.now()}`);
|
|
411
|
+
fs.writeFileSync(tempPath, JSON.stringify(registry, null, 2));
|
|
412
|
+
fs.renameSync(tempPath, filePath);
|
|
413
|
+
}
|
|
414
|
+
function loadRegistry(filePath) {
|
|
415
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
416
|
+
return parseJsonWithContext(content, `model registry: ${filePath}`);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// src/telemetry/worker.ts
|
|
420
|
+
function resolveRegistryModelCandidates(providerId, modelId) {
|
|
421
|
+
const trimmed = modelId.trim();
|
|
422
|
+
if (trimmed.length === 0) {
|
|
423
|
+
return [];
|
|
424
|
+
}
|
|
425
|
+
const candidates = /* @__PURE__ */ new Set([trimmed]);
|
|
426
|
+
let mappings = {};
|
|
427
|
+
try {
|
|
428
|
+
mappings = ProviderRegistry.getMappings(providerId);
|
|
429
|
+
} catch {
|
|
430
|
+
mappings = {};
|
|
431
|
+
}
|
|
432
|
+
const directMapped = mappings[trimmed];
|
|
433
|
+
if (directMapped) {
|
|
434
|
+
candidates.add(directMapped);
|
|
435
|
+
} else {
|
|
436
|
+
const lowered = trimmed.toLowerCase();
|
|
437
|
+
for (const [alias, target] of Object.entries(mappings)) {
|
|
438
|
+
if (alias.toLowerCase() === lowered && target) {
|
|
439
|
+
candidates.add(target);
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return Array.from(candidates);
|
|
445
|
+
}
|
|
446
|
+
function roundCostCents(value) {
|
|
447
|
+
return Math.round(value * 1e6) / 1e6;
|
|
448
|
+
}
|
|
449
|
+
function findRegistryModel(provider, modelId) {
|
|
450
|
+
const candidates = resolveRegistryModelCandidates(provider.providerId, modelId);
|
|
451
|
+
for (const candidate of candidates) {
|
|
452
|
+
const model = provider.models.find((entry) => entry.modelId === candidate);
|
|
453
|
+
if (model) {
|
|
454
|
+
return { model, resolvedModelId: candidate };
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
459
|
+
function optionalAttributionId(value) {
|
|
460
|
+
if (typeof value !== "string") return null;
|
|
461
|
+
const trimmed = value.trim();
|
|
462
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
463
|
+
}
|
|
464
|
+
function findProviderAndModel(registry, providerId, modelId) {
|
|
465
|
+
const provider = registry.providers.find((p) => p.providerId === providerId);
|
|
466
|
+
if (!provider) return null;
|
|
467
|
+
const resolvedModel = findRegistryModel(provider, modelId);
|
|
468
|
+
if (!resolvedModel) return null;
|
|
469
|
+
return { provider, ...resolvedModel };
|
|
470
|
+
}
|
|
471
|
+
function collectRegistrySnapshotsForEnrichment(db, preferredRegistryVersion) {
|
|
472
|
+
const ordered = [];
|
|
473
|
+
const seen = /* @__PURE__ */ new Set();
|
|
474
|
+
const preferred = db.getRegistrySnapshot(preferredRegistryVersion);
|
|
475
|
+
if (preferred) {
|
|
476
|
+
ordered.push(preferred);
|
|
477
|
+
seen.add(preferred.registryVersion);
|
|
478
|
+
}
|
|
479
|
+
for (const snapshot of db.getRegistrySnapshots()) {
|
|
480
|
+
if (seen.has(snapshot.registryVersion)) continue;
|
|
481
|
+
ordered.push(snapshot);
|
|
482
|
+
seen.add(snapshot.registryVersion);
|
|
483
|
+
}
|
|
484
|
+
return ordered;
|
|
485
|
+
}
|
|
486
|
+
var TelemetryWorker = class _TelemetryWorker {
|
|
487
|
+
constructor(db, options = {}) {
|
|
488
|
+
this.db = db;
|
|
489
|
+
this.options = options;
|
|
490
|
+
}
|
|
491
|
+
db;
|
|
492
|
+
options;
|
|
493
|
+
warn(message) {
|
|
494
|
+
if (this.options.diagnostics === "silent") return;
|
|
495
|
+
console.warn(message);
|
|
496
|
+
}
|
|
497
|
+
error(message, detail) {
|
|
498
|
+
if (this.options.diagnostics === "silent") return;
|
|
499
|
+
console.error(message, detail);
|
|
500
|
+
}
|
|
501
|
+
static interpolatePercentile(sortedValues, percentile) {
|
|
502
|
+
if (sortedValues.length === 0) {
|
|
503
|
+
return 0;
|
|
504
|
+
}
|
|
505
|
+
const clampedPercentile = Math.min(1, Math.max(0, percentile));
|
|
506
|
+
const rank = (sortedValues.length - 1) * clampedPercentile;
|
|
507
|
+
const lowerIndex = Math.floor(rank);
|
|
508
|
+
const upperIndex = Math.ceil(rank);
|
|
509
|
+
const lower = sortedValues[lowerIndex] ?? 0;
|
|
510
|
+
const upper = sortedValues[upperIndex] ?? lower;
|
|
511
|
+
if (lowerIndex === upperIndex) {
|
|
512
|
+
return lower;
|
|
513
|
+
}
|
|
514
|
+
const weight = rank - lowerIndex;
|
|
515
|
+
return lower + (upper - lower) * weight;
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Enrich unenriched invocations with cost data
|
|
519
|
+
* Returns number of rows enriched
|
|
520
|
+
*/
|
|
521
|
+
async enrichInvocations(overrides) {
|
|
522
|
+
const unenriched = this.db.getUnenrichedInvocations(1e3);
|
|
523
|
+
let enrichedCount = 0;
|
|
524
|
+
for (const invocation of unenriched) {
|
|
525
|
+
try {
|
|
526
|
+
const snapshots = collectRegistrySnapshotsForEnrichment(
|
|
527
|
+
this.db,
|
|
528
|
+
invocation.registryVersion
|
|
529
|
+
);
|
|
530
|
+
if (snapshots.length === 0) {
|
|
531
|
+
this.warn(`Registry snapshot not found for version ${invocation.registryVersion}`);
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
let resolved = null;
|
|
535
|
+
let providerSeen = false;
|
|
536
|
+
for (const snapshot of snapshots) {
|
|
537
|
+
const registry = parseJsonWithContext(
|
|
538
|
+
snapshot.jsonBlob,
|
|
539
|
+
"Model registry snapshot"
|
|
540
|
+
);
|
|
541
|
+
if (registry.providers.some((p) => p.providerId === invocation.providerId)) {
|
|
542
|
+
providerSeen = true;
|
|
543
|
+
}
|
|
544
|
+
const candidate = findProviderAndModel(
|
|
545
|
+
registry,
|
|
546
|
+
invocation.providerId,
|
|
547
|
+
invocation.modelId
|
|
548
|
+
);
|
|
549
|
+
if (candidate) {
|
|
550
|
+
resolved = candidate;
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
if (!resolved) {
|
|
555
|
+
if (!providerSeen) {
|
|
556
|
+
this.warn(`Provider ${invocation.providerId} not found in registry`);
|
|
557
|
+
} else {
|
|
558
|
+
this.warn(
|
|
559
|
+
`Model ${invocation.modelId} not found for provider ${invocation.providerId}`
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
continue;
|
|
563
|
+
}
|
|
564
|
+
const { model, resolvedModelId } = resolved;
|
|
565
|
+
let pricingSource = model.costSchema?.pricing_source ?? "frontmatter";
|
|
566
|
+
let pricingConfidence = model.costSchema?.pricing_confidence;
|
|
567
|
+
let billingSource = model.costSchema?.billing_source;
|
|
568
|
+
let promptPer1k = model.costSchema?.prompt_per_1k ?? 0;
|
|
569
|
+
let completionPer1k = model.costSchema?.completion_per_1k ?? 0;
|
|
570
|
+
let extraFees = model.costSchema?.extra_fees ?? {};
|
|
571
|
+
let billingMode = model.costSchema?.billing_mode ?? "metered";
|
|
572
|
+
const overrideKey = `${invocation.providerId}:${invocation.modelId}`;
|
|
573
|
+
const resolvedOverrideKey = `${invocation.providerId}:${resolvedModelId}`;
|
|
574
|
+
const override = overrides?.[overrideKey] ?? (resolvedOverrideKey !== overrideKey ? overrides?.[resolvedOverrideKey] : void 0);
|
|
575
|
+
if (override) {
|
|
576
|
+
pricingSource = `override:${overrides?.[overrideKey] ? overrideKey : resolvedOverrideKey}`;
|
|
577
|
+
promptPer1k = override.promptPer1k;
|
|
578
|
+
completionPer1k = override.completionPer1k;
|
|
579
|
+
extraFees = override.extraFees ?? {};
|
|
580
|
+
billingMode = "metered";
|
|
581
|
+
pricingConfidence = void 0;
|
|
582
|
+
billingSource = void 0;
|
|
583
|
+
}
|
|
584
|
+
const tokensIn = invocation.tokensIn ?? 0;
|
|
585
|
+
const tokensOut = invocation.tokensOut ?? 0;
|
|
586
|
+
let costCents = null;
|
|
587
|
+
if (billingMode === "metered") {
|
|
588
|
+
costCents = roundCostCents(
|
|
589
|
+
tokensIn / 1e3 * promptPer1k * 100 + tokensOut / 1e3 * completionPer1k * 100 + Object.values(extraFees).reduce(
|
|
590
|
+
(sum, fee) => sum + (typeof fee === "number" ? fee * 100 : 0),
|
|
591
|
+
0
|
|
592
|
+
)
|
|
593
|
+
);
|
|
594
|
+
} else if (!model.costSchema?.pricing_source && !override) {
|
|
595
|
+
pricingSource = `non_metered:${billingMode}`;
|
|
596
|
+
}
|
|
597
|
+
const originalModelId = typeof invocation.requestedModelId === "string" && invocation.requestedModelId.trim().length > 0 ? invocation.requestedModelId : invocation.modelId;
|
|
598
|
+
const usageData = JSON.stringify({
|
|
599
|
+
inputTokens: tokensIn,
|
|
600
|
+
outputTokens: tokensOut,
|
|
601
|
+
originalModelId,
|
|
602
|
+
canonicalModelId: resolvedModelId
|
|
603
|
+
});
|
|
604
|
+
const inputCostCents = billingMode === "metered" ? roundCostCents(tokensIn / 1e3 * promptPer1k * 100) : 0;
|
|
605
|
+
const outputCostCents = billingMode === "metered" ? roundCostCents(tokensOut / 1e3 * completionPer1k * 100) : 0;
|
|
606
|
+
const costSnapshot = JSON.stringify({
|
|
607
|
+
inputCostCents,
|
|
608
|
+
outputCostCents,
|
|
609
|
+
cacheCostCents: 0,
|
|
610
|
+
toolCostCents: 0,
|
|
611
|
+
infrastructureCostCents: 0,
|
|
612
|
+
totalCostCents: costCents ?? 0,
|
|
613
|
+
currency: "USD",
|
|
614
|
+
pricingSource,
|
|
615
|
+
...pricingConfidence ? { pricingConfidence } : {},
|
|
616
|
+
billingMode,
|
|
617
|
+
...billingSource ? { billingSource } : {},
|
|
618
|
+
originalModelId,
|
|
619
|
+
canonicalModelId: resolvedModelId,
|
|
620
|
+
pricingVersion: invocation.registryVersion
|
|
621
|
+
});
|
|
622
|
+
const taskId = optionalAttributionId(invocation.taskId);
|
|
623
|
+
const competitionId = optionalAttributionId(invocation.competitionId) ?? taskId;
|
|
624
|
+
const variantId = optionalAttributionId(invocation.variantId);
|
|
625
|
+
const enrichedId = generateInvocationId();
|
|
626
|
+
const enriched = {
|
|
627
|
+
id: enrichedId,
|
|
628
|
+
invocationId: invocation.id,
|
|
629
|
+
computedCostCents: costCents,
|
|
630
|
+
pricingSource,
|
|
631
|
+
enrichedAt: Date.now(),
|
|
632
|
+
billingMode,
|
|
633
|
+
usageData,
|
|
634
|
+
costSnapshot,
|
|
635
|
+
pricingVersion: invocation.registryVersion,
|
|
636
|
+
taskId,
|
|
637
|
+
competitionId,
|
|
638
|
+
variantId
|
|
639
|
+
};
|
|
640
|
+
this.db.insertEnrichedInvocation(enriched);
|
|
641
|
+
enrichedCount++;
|
|
642
|
+
} catch (error) {
|
|
643
|
+
this.error(
|
|
644
|
+
`Failed to enrich invocation ${invocation.id}:`,
|
|
645
|
+
error instanceof Error ? error.message : String(error)
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
return enrichedCount;
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Aggregate enriched invocations into hourly buckets
|
|
653
|
+
* Returns number of aggregates updated
|
|
654
|
+
*/
|
|
655
|
+
aggregateHourly() {
|
|
656
|
+
const rows = this.db.db.prepare(
|
|
657
|
+
`
|
|
658
|
+
SELECT
|
|
659
|
+
i.provider_id AS provider_id,
|
|
660
|
+
i.model_id AS model_id,
|
|
661
|
+
CAST(i.timestamp / 3600000 AS INTEGER) * 3600000 AS bucket_start,
|
|
662
|
+
i.tokens_in AS tokens_in,
|
|
663
|
+
i.tokens_out AS tokens_out,
|
|
664
|
+
i.total_time_ms AS total_time_ms,
|
|
665
|
+
i.status AS status,
|
|
666
|
+
e.billing_mode AS billing_mode,
|
|
667
|
+
e.computed_cost_cents AS computed_cost_cents
|
|
668
|
+
FROM model_invocations i
|
|
669
|
+
LEFT JOIN enriched_invocations e ON i.id = e.invocation_id
|
|
670
|
+
`
|
|
671
|
+
).all();
|
|
672
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
673
|
+
for (const row of rows) {
|
|
674
|
+
const key = `${row.provider_id}\0${row.model_id}\0${row.bucket_start}`;
|
|
675
|
+
let acc = buckets.get(key);
|
|
676
|
+
if (!acc) {
|
|
677
|
+
acc = {
|
|
678
|
+
providerId: row.provider_id,
|
|
679
|
+
modelId: row.model_id,
|
|
680
|
+
bucketStart: row.bucket_start,
|
|
681
|
+
totalInvocations: 0,
|
|
682
|
+
totalTokensIn: 0,
|
|
683
|
+
totalTokensOut: 0,
|
|
684
|
+
errorCount: 0,
|
|
685
|
+
timings: [],
|
|
686
|
+
costSum: 0,
|
|
687
|
+
costCount: 0
|
|
688
|
+
};
|
|
689
|
+
buckets.set(key, acc);
|
|
690
|
+
}
|
|
691
|
+
acc.totalInvocations += 1;
|
|
692
|
+
if (typeof row.tokens_in === "number") {
|
|
693
|
+
acc.totalTokensIn += row.tokens_in;
|
|
694
|
+
}
|
|
695
|
+
if (typeof row.tokens_out === "number") {
|
|
696
|
+
acc.totalTokensOut += row.tokens_out;
|
|
697
|
+
}
|
|
698
|
+
if (row.status !== "success") {
|
|
699
|
+
acc.errorCount += 1;
|
|
700
|
+
}
|
|
701
|
+
if (typeof row.total_time_ms === "number" && Number.isFinite(row.total_time_ms)) {
|
|
702
|
+
acc.timings.push(row.total_time_ms);
|
|
703
|
+
}
|
|
704
|
+
const billingQualifies = row.billing_mode === null || row.billing_mode === "metered";
|
|
705
|
+
if (billingQualifies && typeof row.computed_cost_cents === "number") {
|
|
706
|
+
acc.costSum += row.computed_cost_cents;
|
|
707
|
+
acc.costCount += 1;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
let aggregatedCount = 0;
|
|
711
|
+
for (const acc of buckets.values()) {
|
|
712
|
+
const sortedTimings = acc.timings.slice().sort((a, b) => a - b);
|
|
713
|
+
const p95LatencyMs = _TelemetryWorker.interpolatePercentile(sortedTimings, 0.95);
|
|
714
|
+
const avgCostCents = acc.costCount > 0 ? acc.costSum / acc.costCount : 0;
|
|
715
|
+
const errorRate = acc.errorCount * 100 / acc.totalInvocations;
|
|
716
|
+
const aggregate = {
|
|
717
|
+
bucketStart: acc.bucketStart,
|
|
718
|
+
providerId: acc.providerId,
|
|
719
|
+
modelId: acc.modelId,
|
|
720
|
+
totalInvocations: acc.totalInvocations,
|
|
721
|
+
totalTokensIn: acc.totalTokensIn,
|
|
722
|
+
totalTokensOut: acc.totalTokensOut,
|
|
723
|
+
avgCostCents,
|
|
724
|
+
p95LatencyMs,
|
|
725
|
+
errorRate
|
|
726
|
+
};
|
|
727
|
+
this.db.upsertHourlyAggregate(aggregate);
|
|
728
|
+
aggregatedCount++;
|
|
729
|
+
}
|
|
730
|
+
return aggregatedCount;
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* Aggregate enriched invocations into daily cost buckets.
|
|
734
|
+
* Returns number of daily aggregate rows upserted.
|
|
735
|
+
*/
|
|
736
|
+
aggregateDaily() {
|
|
737
|
+
return this.db.backfillDailyCostAggregations();
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* Run full enrichment and aggregation cycle
|
|
741
|
+
*/
|
|
742
|
+
async runCycle(overrides) {
|
|
743
|
+
const enrichedCount = await this.enrichInvocations(overrides);
|
|
744
|
+
const aggregatedCount = this.aggregateHourly();
|
|
745
|
+
const dailyAggregatedCount = this.aggregateDaily();
|
|
746
|
+
return { enrichedCount, aggregatedCount, dailyAggregatedCount };
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
|
|
750
|
+
// src/telemetry/invocation-cost-enrichment.ts
|
|
751
|
+
var PENDING_ENRICHMENT_HINT = "Invocations exist in this window but cost enrichment is pending or incomplete.";
|
|
752
|
+
var NO_INVOCATIONS_HINT = "No model invocations recorded in this window.";
|
|
753
|
+
var ZERO_USAGE_HINT = "Invocations were recorded but reported zero token usage; per-token cost is $0.";
|
|
754
|
+
function resolveProvidersDirectory(cwd = process.cwd()) {
|
|
755
|
+
const packageRoot = getPackageRoot();
|
|
756
|
+
const artifactsDir = path2.join(cwd, ".poetic", "providers");
|
|
757
|
+
if (fs2.existsSync(path2.join(artifactsDir, "registry.json"))) {
|
|
758
|
+
return artifactsDir;
|
|
759
|
+
}
|
|
760
|
+
const candidates = [
|
|
761
|
+
path2.join(cwd, "providers"),
|
|
762
|
+
path2.join(cwd, ".poetic", "config"),
|
|
763
|
+
path2.join(packageRoot, ".poetic", "providers"),
|
|
764
|
+
path2.join(packageRoot, "providers"),
|
|
765
|
+
path2.join(packageRoot, ".poetic", "config")
|
|
766
|
+
];
|
|
767
|
+
for (const candidate of candidates) {
|
|
768
|
+
if (!fs2.existsSync(candidate)) continue;
|
|
769
|
+
if (fs2.existsSync(path2.join(candidate, "registry.json")) || fs2.existsSync(path2.join(candidate, "providers.yaml")) || fs2.readdirSync(candidate, { withFileTypes: true }).some((entry) => entry.name.endsWith(".md"))) {
|
|
770
|
+
return candidate;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
return null;
|
|
774
|
+
}
|
|
775
|
+
function per1kCost(modelPricing, input) {
|
|
776
|
+
if (!modelPricing) return void 0;
|
|
777
|
+
const per1k = input ? modelPricing.input_per_1k_tokens : modelPricing.output_per_1k_tokens;
|
|
778
|
+
if (typeof per1k === "number") return per1k;
|
|
779
|
+
const per1m = input ? modelPricing.input_per_1m_tokens : modelPricing.output_per_1m_tokens;
|
|
780
|
+
return typeof per1m === "number" ? per1m / 1e3 : void 0;
|
|
781
|
+
}
|
|
782
|
+
function stringMeta(value) {
|
|
783
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
784
|
+
}
|
|
785
|
+
function confidenceMeta(value) {
|
|
786
|
+
const raw = stringMeta(value)?.toLowerCase();
|
|
787
|
+
return raw === "high" || raw === "medium" || raw === "low" ? raw : void 0;
|
|
788
|
+
}
|
|
789
|
+
function pricingMetadata(pricing) {
|
|
790
|
+
const meta = pricing?._meta && typeof pricing._meta === "object" ? pricing._meta : {};
|
|
791
|
+
return {
|
|
792
|
+
...stringMeta(meta.pricing_source) ? { pricing_source: stringMeta(meta.pricing_source) } : {},
|
|
793
|
+
...confidenceMeta(meta.pricing_confidence) ? { pricing_confidence: confidenceMeta(meta.pricing_confidence) } : {},
|
|
794
|
+
...stringMeta(meta.billing_source) ? { billing_source: stringMeta(meta.billing_source) } : {}
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
function buildRegistryFromProvidersYaml(filePath) {
|
|
798
|
+
const parsed = ProviderRegistryConfigSchema.parse(yaml2.load(fs2.readFileSync(filePath, "utf-8")));
|
|
799
|
+
const providers = Object.entries(parsed.providers).map(
|
|
800
|
+
([providerId, provider]) => ({
|
|
801
|
+
providerId,
|
|
802
|
+
displayName: provider.display_name,
|
|
803
|
+
models: provider.models.available.map((model) => {
|
|
804
|
+
const pricing = provider.pricing?.models?.[model.id];
|
|
805
|
+
const inputPer1k = per1kCost(pricing, true);
|
|
806
|
+
const outputPer1k = per1kCost(pricing, false);
|
|
807
|
+
const metadata = pricingMetadata(pricing);
|
|
808
|
+
return {
|
|
809
|
+
modelId: model.id,
|
|
810
|
+
displayName: model.display_name,
|
|
811
|
+
capabilities: model.capabilities,
|
|
812
|
+
...pricing?.non_metered ? {
|
|
813
|
+
costSchema: {
|
|
814
|
+
billing_mode: "non_metered",
|
|
815
|
+
...metadata,
|
|
816
|
+
notes: pricing.notes
|
|
817
|
+
}
|
|
818
|
+
} : inputPer1k !== void 0 || outputPer1k !== void 0 ? {
|
|
819
|
+
costSchema: {
|
|
820
|
+
prompt_per_1k: inputPer1k ?? 0,
|
|
821
|
+
completion_per_1k: outputPer1k ?? 0,
|
|
822
|
+
billing_mode: "metered",
|
|
823
|
+
...metadata,
|
|
824
|
+
notes: pricing?.notes
|
|
825
|
+
}
|
|
826
|
+
} : {}
|
|
827
|
+
};
|
|
828
|
+
})
|
|
829
|
+
})
|
|
830
|
+
);
|
|
831
|
+
return buildRegistry(providers);
|
|
832
|
+
}
|
|
833
|
+
function loadBundledRegistry(providersDir) {
|
|
834
|
+
const registryJsonPath = path2.join(providersDir, "registry.json");
|
|
835
|
+
if (fs2.existsSync(registryJsonPath)) {
|
|
836
|
+
return loadRegistry(registryJsonPath);
|
|
837
|
+
}
|
|
838
|
+
const providersYamlPath = path2.join(providersDir, "providers.yaml");
|
|
839
|
+
if (fs2.existsSync(providersYamlPath)) {
|
|
840
|
+
return buildRegistryFromProvidersYaml(providersYamlPath);
|
|
841
|
+
}
|
|
842
|
+
return buildRegistry(loadProviderDirectory(providersDir));
|
|
843
|
+
}
|
|
844
|
+
function ensureRegistrySnapshot(db) {
|
|
845
|
+
const providersDir = resolveProvidersDirectory();
|
|
846
|
+
const existingSnapshot = db.getLatestRegistrySnapshot();
|
|
847
|
+
if (!providersDir) {
|
|
848
|
+
return Boolean(existingSnapshot);
|
|
849
|
+
}
|
|
850
|
+
try {
|
|
851
|
+
const registry = loadBundledRegistry(providersDir);
|
|
852
|
+
db.insertRegistrySnapshot({
|
|
853
|
+
registryVersion: registry.registryVersion,
|
|
854
|
+
createdAt: Date.now(),
|
|
855
|
+
jsonBlob: JSON.stringify(registry)
|
|
856
|
+
});
|
|
857
|
+
return true;
|
|
858
|
+
} catch {
|
|
859
|
+
return Boolean(existingSnapshot);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
function ensureRegistrySnapshotForPendingEnrichment(db, options) {
|
|
863
|
+
const existingSnapshot = db.getLatestRegistrySnapshot();
|
|
864
|
+
if (options.refreshRegistrySnapshot === false && existingSnapshot) {
|
|
865
|
+
return true;
|
|
866
|
+
}
|
|
867
|
+
return ensureRegistrySnapshot(db);
|
|
868
|
+
}
|
|
869
|
+
async function ensurePendingInvocationCostEnrichment(db, options = {}) {
|
|
870
|
+
const pendingCount = db.countUnenrichedInvocations();
|
|
871
|
+
if (pendingCount === 0) {
|
|
872
|
+
return { enrichedCount: 0, registrySnapshotEnsured: Boolean(db.getLatestRegistrySnapshot()) };
|
|
873
|
+
}
|
|
874
|
+
const registrySnapshotEnsured = ensureRegistrySnapshotForPendingEnrichment(db, options);
|
|
875
|
+
if (!registrySnapshotEnsured) {
|
|
876
|
+
return { enrichedCount: 0, registrySnapshotEnsured: false };
|
|
877
|
+
}
|
|
878
|
+
const worker = new TelemetryWorker(db, { diagnostics: "silent" });
|
|
879
|
+
const enrichedCount = options.aggregate === false ? await worker.enrichInvocations() : (await worker.runCycle()).enrichedCount;
|
|
880
|
+
return {
|
|
881
|
+
enrichedCount,
|
|
882
|
+
registrySnapshotEnsured: true
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
function summarizeInvocationCostVisibility(db, startTimestampMs) {
|
|
886
|
+
const rawInvocationCount = db.countRawInvocationsSince(startTimestampMs);
|
|
887
|
+
const enrichedInvocationCount = db.countEnrichedInvocationsSince(startTimestampMs);
|
|
888
|
+
const pendingEnrichmentCount = Math.max(0, rawInvocationCount - enrichedInvocationCount);
|
|
889
|
+
if (rawInvocationCount === 0) {
|
|
890
|
+
return {
|
|
891
|
+
rawInvocationCount,
|
|
892
|
+
enrichedInvocationCount,
|
|
893
|
+
pendingEnrichmentCount,
|
|
894
|
+
visibility: "no_invocations",
|
|
895
|
+
hint: NO_INVOCATIONS_HINT
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
if (pendingEnrichmentCount > 0) {
|
|
899
|
+
return {
|
|
900
|
+
rawInvocationCount,
|
|
901
|
+
enrichedInvocationCount,
|
|
902
|
+
pendingEnrichmentCount,
|
|
903
|
+
visibility: "pending_enrichment",
|
|
904
|
+
hint: PENDING_ENRICHMENT_HINT
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
const hasTokenUsage = db.hasTokenUsageSince(startTimestampMs);
|
|
908
|
+
if (!hasTokenUsage) {
|
|
909
|
+
return {
|
|
910
|
+
rawInvocationCount,
|
|
911
|
+
enrichedInvocationCount,
|
|
912
|
+
pendingEnrichmentCount,
|
|
913
|
+
visibility: "zero_usage",
|
|
914
|
+
hint: ZERO_USAGE_HINT
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
return {
|
|
918
|
+
rawInvocationCount,
|
|
919
|
+
enrichedInvocationCount,
|
|
920
|
+
pendingEnrichmentCount,
|
|
921
|
+
visibility: "enriched"
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
function resolveInvocationRegistryVersion(db) {
|
|
925
|
+
return db.getLatestRegistryVersion() ?? "v1";
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// src/telemetry/cost-projection.ts
|
|
929
|
+
function availableCostAmount(amount, unit, source, detail) {
|
|
930
|
+
return {
|
|
931
|
+
amount,
|
|
932
|
+
unit,
|
|
933
|
+
status: "available",
|
|
934
|
+
source,
|
|
935
|
+
...detail ? { detail } : {}
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
function unavailableCostAmount(unit, source, detail) {
|
|
939
|
+
return {
|
|
940
|
+
amount: null,
|
|
941
|
+
unit,
|
|
942
|
+
status: "unavailable",
|
|
943
|
+
source,
|
|
944
|
+
...detail ? { detail } : {}
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
function errorCostAmount(unit, source, error, detail) {
|
|
948
|
+
return {
|
|
949
|
+
amount: null,
|
|
950
|
+
unit,
|
|
951
|
+
status: "error",
|
|
952
|
+
source,
|
|
953
|
+
error,
|
|
954
|
+
...detail ? { detail } : {}
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
function isAvailableCostAmount(value) {
|
|
958
|
+
return value.status === "available";
|
|
959
|
+
}
|
|
960
|
+
function costAmountOrNull(value) {
|
|
961
|
+
return value.status === "available" ? value.amount : null;
|
|
962
|
+
}
|
|
963
|
+
var PENDING_HINT = "Invocations exist in this window but cost enrichment is pending; totals may under-report until enrichment completes.";
|
|
964
|
+
var FAILED_HINT = "Invocations exist in this window but cost enrichment could not run (no pricing snapshot available); showing raw counts only.";
|
|
965
|
+
var RAW_ZERO_USAGE_HINT = "Invocations were recorded but reported zero token usage; per-token cost is $0.";
|
|
966
|
+
var NO_INVOCATIONS_HINT2 = "No model invocations recorded in this window.";
|
|
967
|
+
function mapVisibilityToState(summary, attempt) {
|
|
968
|
+
switch (summary.visibility) {
|
|
969
|
+
case "enriched":
|
|
970
|
+
return "enriched";
|
|
971
|
+
case "zero_usage":
|
|
972
|
+
return "raw";
|
|
973
|
+
case "no_invocations":
|
|
974
|
+
return "enriched";
|
|
975
|
+
case "pending_enrichment":
|
|
976
|
+
if (attempt && attempt.registrySnapshotEnsured === false) {
|
|
977
|
+
return "failed";
|
|
978
|
+
}
|
|
979
|
+
return "pending";
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
function resolveHint(state, summary) {
|
|
983
|
+
switch (state) {
|
|
984
|
+
case "pending":
|
|
985
|
+
return PENDING_HINT;
|
|
986
|
+
case "failed":
|
|
987
|
+
return FAILED_HINT;
|
|
988
|
+
case "raw":
|
|
989
|
+
return summary.visibility === "zero_usage" ? RAW_ZERO_USAGE_HINT : summary.hint;
|
|
990
|
+
case "enriched":
|
|
991
|
+
return summary.rawInvocationCount === 0 ? NO_INVOCATIONS_HINT2 : void 0;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
function projectCostVisibility(db, startTimestampMs, attempt) {
|
|
995
|
+
const summary = summarizeInvocationCostVisibility(db, startTimestampMs);
|
|
996
|
+
return fromVisibilitySummary(summary, attempt);
|
|
997
|
+
}
|
|
998
|
+
function fromVisibilitySummary(summary, attempt) {
|
|
999
|
+
const state = mapVisibilityToState(summary, attempt);
|
|
1000
|
+
const totalsMayUnderReport = state === "pending" || state === "failed";
|
|
1001
|
+
const hint = resolveHint(state, summary);
|
|
1002
|
+
return {
|
|
1003
|
+
rawInvocationCount: summary.rawInvocationCount,
|
|
1004
|
+
enrichedInvocationCount: summary.enrichedInvocationCount,
|
|
1005
|
+
pendingEnrichmentCount: summary.pendingEnrichmentCount,
|
|
1006
|
+
state,
|
|
1007
|
+
visibility: summary.visibility,
|
|
1008
|
+
totalsMayUnderReport,
|
|
1009
|
+
...hint ? { hint } : {}
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
function formatCostProjectionNotice(projection) {
|
|
1013
|
+
if (projection.state === "enriched" && projection.rawInvocationCount === 0) {
|
|
1014
|
+
return NO_INVOCATIONS_HINT2;
|
|
1015
|
+
}
|
|
1016
|
+
if (projection.state === "enriched") {
|
|
1017
|
+
return void 0;
|
|
1018
|
+
}
|
|
1019
|
+
const counts = projection.pendingEnrichmentCount > 0 ? ` (${projection.pendingEnrichmentCount} pending of ${projection.rawInvocationCount} raw)` : projection.rawInvocationCount > 0 ? ` (${projection.rawInvocationCount} raw)` : "";
|
|
1020
|
+
return `${projection.hint ?? ""}${counts}`.trim() || void 0;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
// src/telemetry/invocation-quality.ts
|
|
1024
|
+
var SAFE_SQL_ALIAS = /^[A-Za-z_][A-Za-z0-9_]*$/u;
|
|
1025
|
+
function invocationProvenanceClauses(alias) {
|
|
1026
|
+
const prefix = sqlPrefix(alias);
|
|
1027
|
+
return [
|
|
1028
|
+
`${prefix}provider_id <> 'unknown'`,
|
|
1029
|
+
`(${prefix}execution_source IS NULL OR ${prefix}execution_source = 'live')`
|
|
1030
|
+
];
|
|
1031
|
+
}
|
|
1032
|
+
function invocationMetricQualityClauses(alias) {
|
|
1033
|
+
const prefix = sqlPrefix(alias);
|
|
1034
|
+
return [
|
|
1035
|
+
`NOT (${prefix}total_time_ms IS NOT NULL AND ${prefix}total_time_ms < 1000 AND ${prefix}tokens_out IS NULL)`,
|
|
1036
|
+
`NOT (
|
|
1037
|
+
${prefix}tokens_out IS NOT NULL
|
|
1038
|
+
AND ${prefix}tokens_out > 0
|
|
1039
|
+
AND ${prefix}time_first_token_ms IS NOT NULL
|
|
1040
|
+
AND ${prefix}time_last_token_ms IS NOT NULL
|
|
1041
|
+
AND ${prefix}time_last_token_ms > ${prefix}time_first_token_ms
|
|
1042
|
+
AND ((${prefix}tokens_out * 1000.0) / (${prefix}time_last_token_ms - ${prefix}time_first_token_ms)) > 1000
|
|
1043
|
+
)`
|
|
1044
|
+
];
|
|
1045
|
+
}
|
|
1046
|
+
function invocationAnalyticsQualityClauses(alias) {
|
|
1047
|
+
return [...invocationProvenanceClauses(alias), ...invocationMetricQualityClauses(alias)];
|
|
1048
|
+
}
|
|
1049
|
+
function invocationProvenanceWhereSql(alias) {
|
|
1050
|
+
return clausesToAndSql(invocationProvenanceClauses(alias));
|
|
1051
|
+
}
|
|
1052
|
+
function invocationAnalyticsQualityWhereSql(alias) {
|
|
1053
|
+
return clausesToAndSql(invocationAnalyticsQualityClauses(alias));
|
|
1054
|
+
}
|
|
1055
|
+
function isInvocationMetricQualityRow(row) {
|
|
1056
|
+
if (row.total_time_ms !== null && row.total_time_ms !== void 0 && row.total_time_ms < 1e3) {
|
|
1057
|
+
if (row.tokens_out === null || row.tokens_out === void 0) return false;
|
|
1058
|
+
}
|
|
1059
|
+
const tokensOut = row.tokens_out;
|
|
1060
|
+
const firstTokenMs = row.time_first_token_ms;
|
|
1061
|
+
const lastTokenMs = row.time_last_token_ms;
|
|
1062
|
+
if (typeof tokensOut === "number" && tokensOut > 0 && typeof firstTokenMs === "number" && typeof lastTokenMs === "number" && lastTokenMs > firstTokenMs) {
|
|
1063
|
+
const decodeTokensPerSecond = tokensOut * 1e3 / (lastTokenMs - firstTokenMs);
|
|
1064
|
+
if (decodeTokensPerSecond > 1e3) return false;
|
|
1065
|
+
}
|
|
1066
|
+
return true;
|
|
1067
|
+
}
|
|
1068
|
+
function clausesToAndSql(clauses) {
|
|
1069
|
+
if (clauses.length === 0) return "";
|
|
1070
|
+
return clauses.map((clause) => `AND ${clause}`).join("\n");
|
|
1071
|
+
}
|
|
1072
|
+
function sqlPrefix(alias) {
|
|
1073
|
+
if (alias === void 0) return "";
|
|
1074
|
+
if (!SAFE_SQL_ALIAS.test(alias)) {
|
|
1075
|
+
throw new Error(`Invalid SQL alias: ${alias}`);
|
|
1076
|
+
}
|
|
1077
|
+
return `${alias}.`;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
// src/telemetry/model-invocations-db.ts
|
|
1081
|
+
var ENRICHED_INVOCATIONS_SOURCE = "enriched_invocations";
|
|
1082
|
+
var ALLOWED_TABLE_NAMES = [
|
|
1083
|
+
"model_invocations",
|
|
1084
|
+
"registry_snapshots",
|
|
1085
|
+
"enriched_invocations",
|
|
1086
|
+
"aggregates_hourly",
|
|
1087
|
+
"cost_aggregations_daily"
|
|
1088
|
+
];
|
|
1089
|
+
var SAFE_SQL_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/u;
|
|
1090
|
+
var SAFE_COLUMN_DEFINITION = /^(TEXT|INTEGER|REAL|BLOB|NUMERIC)$/iu;
|
|
1091
|
+
function validateTableName(tableName) {
|
|
1092
|
+
if (!ALLOWED_TABLE_NAMES.includes(tableName)) {
|
|
1093
|
+
throw new Error(
|
|
1094
|
+
`Invalid table name: "${tableName}". Only the following tables are allowed: ${ALLOWED_TABLE_NAMES.join(", ")}`
|
|
1095
|
+
);
|
|
1096
|
+
}
|
|
1097
|
+
return tableName;
|
|
1098
|
+
}
|
|
1099
|
+
function validateSqlIdentifier(value, label) {
|
|
1100
|
+
if (!SAFE_SQL_IDENTIFIER.test(value)) {
|
|
1101
|
+
throw new Error(`Invalid SQL ${label}: ${value}`);
|
|
1102
|
+
}
|
|
1103
|
+
return value;
|
|
1104
|
+
}
|
|
1105
|
+
function validateColumnDefinition(value) {
|
|
1106
|
+
if (!SAFE_COLUMN_DEFINITION.test(value)) {
|
|
1107
|
+
throw new Error(`Invalid SQL column definition: ${value}`);
|
|
1108
|
+
}
|
|
1109
|
+
return value;
|
|
1110
|
+
}
|
|
1111
|
+
function snakeToCamel(str) {
|
|
1112
|
+
return str.replace(/_([a-z])/g, (_match, letter) => letter.toUpperCase());
|
|
1113
|
+
}
|
|
1114
|
+
function convertRowToCamelCase(row) {
|
|
1115
|
+
if (!row) return null;
|
|
1116
|
+
const result = {};
|
|
1117
|
+
for (const key in row) {
|
|
1118
|
+
result[snakeToCamel(key)] = row[key];
|
|
1119
|
+
}
|
|
1120
|
+
return result;
|
|
1121
|
+
}
|
|
1122
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
1123
|
+
function utcDateStartMs(date) {
|
|
1124
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/u.exec(date);
|
|
1125
|
+
if (!match) {
|
|
1126
|
+
throw new Error(`Invalid date (expected YYYY-MM-DD): ${date}`);
|
|
1127
|
+
}
|
|
1128
|
+
const year = Number(match[1]);
|
|
1129
|
+
const month = Number(match[2]);
|
|
1130
|
+
const day = Number(match[3]);
|
|
1131
|
+
const ms = Date.UTC(year, month - 1, day);
|
|
1132
|
+
const check = new Date(ms);
|
|
1133
|
+
if (check.getUTCFullYear() !== year || check.getUTCMonth() !== month - 1 || check.getUTCDate() !== day) {
|
|
1134
|
+
throw new Error(`Invalid calendar date: ${date}`);
|
|
1135
|
+
}
|
|
1136
|
+
return ms;
|
|
1137
|
+
}
|
|
1138
|
+
var compatibleModelIndexByProvider = /* @__PURE__ */ new Map();
|
|
1139
|
+
registerProviderRegistryReloadInvalidator(() => {
|
|
1140
|
+
compatibleModelIndexByProvider.clear();
|
|
1141
|
+
});
|
|
1142
|
+
function addAlternate(index, alternate, modelId) {
|
|
1143
|
+
if (typeof alternate !== "string") return;
|
|
1144
|
+
const trimmed = alternate.trim();
|
|
1145
|
+
if (trimmed.length === 0) return;
|
|
1146
|
+
let owners = index.alternateToModels.get(trimmed);
|
|
1147
|
+
if (!owners) {
|
|
1148
|
+
owners = /* @__PURE__ */ new Set();
|
|
1149
|
+
index.alternateToModels.set(trimmed, owners);
|
|
1150
|
+
}
|
|
1151
|
+
owners.add(modelId);
|
|
1152
|
+
}
|
|
1153
|
+
function buildCompatibleModelIndex(providerId) {
|
|
1154
|
+
try {
|
|
1155
|
+
const config = ProviderRegistry.getProviderConfig(providerId);
|
|
1156
|
+
const index = {
|
|
1157
|
+
modelIds: /* @__PURE__ */ new Set(),
|
|
1158
|
+
alternateToModels: /* @__PURE__ */ new Map()
|
|
1159
|
+
};
|
|
1160
|
+
for (const model of config.models.available) {
|
|
1161
|
+
index.modelIds.add(model.id);
|
|
1162
|
+
addAlternate(index, model.id, model.id);
|
|
1163
|
+
addAlternate(index, model.full_name, model.id);
|
|
1164
|
+
addAlternate(index, model.canonical_id, model.id);
|
|
1165
|
+
addAlternate(index, model.routing_id, model.id);
|
|
1166
|
+
const bedrock = model.bedrock;
|
|
1167
|
+
if (bedrock) {
|
|
1168
|
+
addAlternate(index, bedrock.base_model_id, model.id);
|
|
1169
|
+
addAlternate(index, bedrock.global, model.id);
|
|
1170
|
+
addAlternate(index, bedrock.us, model.id);
|
|
1171
|
+
addAlternate(index, bedrock.eu, model.id);
|
|
1172
|
+
addAlternate(index, bedrock.jp, model.id);
|
|
1173
|
+
addAlternate(index, bedrock.apac, model.id);
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
return index;
|
|
1177
|
+
} catch {
|
|
1178
|
+
return null;
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
function getCompatibleModelIndex(providerId) {
|
|
1182
|
+
const trimmedProvider = providerId.trim();
|
|
1183
|
+
if (trimmedProvider.length === 0) return null;
|
|
1184
|
+
if (compatibleModelIndexByProvider.has(trimmedProvider)) {
|
|
1185
|
+
return compatibleModelIndexByProvider.get(trimmedProvider) ?? null;
|
|
1186
|
+
}
|
|
1187
|
+
const index = buildCompatibleModelIndex(trimmedProvider);
|
|
1188
|
+
compatibleModelIndexByProvider.set(trimmedProvider, index);
|
|
1189
|
+
return index;
|
|
1190
|
+
}
|
|
1191
|
+
function lookupCompatibleModelId(index, candidate) {
|
|
1192
|
+
if (index.modelIds.has(candidate)) {
|
|
1193
|
+
return candidate;
|
|
1194
|
+
}
|
|
1195
|
+
const owners = index.alternateToModels.get(candidate);
|
|
1196
|
+
if (owners && owners.size === 1) {
|
|
1197
|
+
return owners.values().next().value;
|
|
1198
|
+
}
|
|
1199
|
+
return null;
|
|
1200
|
+
}
|
|
1201
|
+
function stripOneTrailingContextSelector(modelId) {
|
|
1202
|
+
const match = modelId.match(/^(.*)(\[[^\]]+\])\s*$/u);
|
|
1203
|
+
if (!match || match[1] === void 0 || match[2] === void 0) {
|
|
1204
|
+
return null;
|
|
1205
|
+
}
|
|
1206
|
+
const base = match[1].trim();
|
|
1207
|
+
if (base.length === 0) {
|
|
1208
|
+
return null;
|
|
1209
|
+
}
|
|
1210
|
+
return base;
|
|
1211
|
+
}
|
|
1212
|
+
function resolveCompatibleInvocationModelId(providerId, modelId) {
|
|
1213
|
+
const trimmedModel = typeof modelId === "string" ? modelId.trim() : "";
|
|
1214
|
+
if (trimmedModel.length === 0) return modelId;
|
|
1215
|
+
const index = getCompatibleModelIndex(providerId);
|
|
1216
|
+
if (!index) return modelId;
|
|
1217
|
+
const direct = lookupCompatibleModelId(index, trimmedModel);
|
|
1218
|
+
if (direct !== null) {
|
|
1219
|
+
return direct;
|
|
1220
|
+
}
|
|
1221
|
+
const baseWithoutSelector = stripOneTrailingContextSelector(trimmedModel);
|
|
1222
|
+
if (baseWithoutSelector !== null && baseWithoutSelector !== trimmedModel) {
|
|
1223
|
+
const fromBase = lookupCompatibleModelId(index, baseWithoutSelector);
|
|
1224
|
+
if (fromBase !== null) {
|
|
1225
|
+
return fromBase;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
return modelId;
|
|
1229
|
+
}
|
|
1230
|
+
function invocationModelMatchesSameModel(providerId, storedModelId, filterModelId) {
|
|
1231
|
+
if (storedModelId === filterModelId) return true;
|
|
1232
|
+
const storedNorm = resolveCompatibleInvocationModelId(providerId, storedModelId);
|
|
1233
|
+
const filterNorm = resolveCompatibleInvocationModelId(providerId, filterModelId);
|
|
1234
|
+
return storedNorm === filterNorm;
|
|
1235
|
+
}
|
|
1236
|
+
var ModelInvocationsDB = class {
|
|
1237
|
+
// Exposed for worker access - ideally should add public query methods instead
|
|
1238
|
+
db;
|
|
1239
|
+
// Database instance type (better-sqlite3 or in-memory shim)
|
|
1240
|
+
dbPath;
|
|
1241
|
+
constructor(dbPath) {
|
|
1242
|
+
if (!sqlite_wrapper_default) {
|
|
1243
|
+
throw new Error("Database implementation not available");
|
|
1244
|
+
}
|
|
1245
|
+
this.dbPath = dbPath;
|
|
1246
|
+
this.db = new sqlite_wrapper_default(dbPath);
|
|
1247
|
+
this.initializeSchema();
|
|
1248
|
+
}
|
|
1249
|
+
/**
|
|
1250
|
+
* Initialize database schema with all required tables
|
|
1251
|
+
*/
|
|
1252
|
+
initializeSchema() {
|
|
1253
|
+
applySqlitePragmas(this.db, { foreignKeys: true });
|
|
1254
|
+
this.db.exec(`
|
|
1255
|
+
-- Model invocations table
|
|
1256
|
+
CREATE TABLE IF NOT EXISTS model_invocations (
|
|
1257
|
+
id TEXT PRIMARY KEY,
|
|
1258
|
+
timestamp INTEGER NOT NULL,
|
|
1259
|
+
parent_workflow_id TEXT,
|
|
1260
|
+
provider_id TEXT NOT NULL,
|
|
1261
|
+
model_id TEXT NOT NULL,
|
|
1262
|
+
registry_version TEXT NOT NULL,
|
|
1263
|
+
tokens_in INTEGER,
|
|
1264
|
+
tokens_out INTEGER,
|
|
1265
|
+
time_request_sent_ms INTEGER NOT NULL,
|
|
1266
|
+
time_first_token_ms INTEGER,
|
|
1267
|
+
time_last_token_ms INTEGER,
|
|
1268
|
+
time_to_first_token_ms INTEGER,
|
|
1269
|
+
total_time_ms INTEGER,
|
|
1270
|
+
status TEXT NOT NULL,
|
|
1271
|
+
error_code TEXT,
|
|
1272
|
+
request_size_bytes INTEGER,
|
|
1273
|
+
agent_role TEXT,
|
|
1274
|
+
copilot_subcommand TEXT,
|
|
1275
|
+
execution_source TEXT,
|
|
1276
|
+
requested_model_id TEXT,
|
|
1277
|
+
execution_model_id TEXT,
|
|
1278
|
+
reported_model_id TEXT,
|
|
1279
|
+
backend TEXT,
|
|
1280
|
+
scope TEXT,
|
|
1281
|
+
context_selector TEXT,
|
|
1282
|
+
resolution_source TEXT,
|
|
1283
|
+
task_id TEXT,
|
|
1284
|
+
competition_id TEXT,
|
|
1285
|
+
variant_id TEXT
|
|
1286
|
+
);
|
|
1287
|
+
|
|
1288
|
+
CREATE INDEX IF NOT EXISTS idx_model_invocations_timestamp
|
|
1289
|
+
ON model_invocations(timestamp);
|
|
1290
|
+
CREATE INDEX IF NOT EXISTS idx_model_invocations_provider_model
|
|
1291
|
+
ON model_invocations(provider_id, model_id);
|
|
1292
|
+
CREATE INDEX IF NOT EXISTS idx_model_invocations_registry_version
|
|
1293
|
+
ON model_invocations(registry_version);
|
|
1294
|
+
CREATE INDEX IF NOT EXISTS idx_model_invocations_agent_role
|
|
1295
|
+
ON model_invocations(agent_role);
|
|
1296
|
+
-- Partial index for speed/latency analytics, which filter on
|
|
1297
|
+
-- status='success' (+ optional timestamp/provider/model) and order by
|
|
1298
|
+
-- timestamp. A PARTIAL index keyed only to successful rows is used
|
|
1299
|
+
-- instead of the full composite (status, timestamp, provider_id,
|
|
1300
|
+
-- model_id) deliberately: the full composite would add per-INSERT write
|
|
1301
|
+
-- cost on the hot invocation-write path for every row, whereas success
|
|
1302
|
+
-- rows are the only ones these read paths target. Validate that the
|
|
1303
|
+
-- speed query actually uses this index with EXPLAIN QUERY PLAN. Created
|
|
1304
|
+
-- idempotently like the other indexes above (no SCHEMA_VERSION bump).
|
|
1305
|
+
CREATE INDEX IF NOT EXISTS idx_model_invocations_success_speed
|
|
1306
|
+
ON model_invocations(timestamp DESC, provider_id, model_id)
|
|
1307
|
+
WHERE status = 'success';
|
|
1308
|
+
|
|
1309
|
+
-- Registry snapshots table
|
|
1310
|
+
CREATE TABLE IF NOT EXISTS registry_snapshots (
|
|
1311
|
+
registry_version TEXT PRIMARY KEY,
|
|
1312
|
+
created_at INTEGER NOT NULL,
|
|
1313
|
+
json_blob TEXT NOT NULL
|
|
1314
|
+
);
|
|
1315
|
+
|
|
1316
|
+
-- Enriched invocations table (hybrid storage: tokens + cost snapshot)
|
|
1317
|
+
CREATE TABLE IF NOT EXISTS enriched_invocations (
|
|
1318
|
+
id TEXT PRIMARY KEY,
|
|
1319
|
+
invocation_id TEXT NOT NULL UNIQUE,
|
|
1320
|
+
computed_cost_cents INTEGER,
|
|
1321
|
+
pricing_source TEXT NOT NULL,
|
|
1322
|
+
enriched_at INTEGER NOT NULL,
|
|
1323
|
+
billing_mode TEXT,
|
|
1324
|
+
|
|
1325
|
+
-- Hybrid Storage: Raw usage data (JSON) - immutable source of truth
|
|
1326
|
+
usage_data TEXT,
|
|
1327
|
+
|
|
1328
|
+
-- Hybrid Storage: Cost snapshot (JSON) - historical accuracy
|
|
1329
|
+
cost_snapshot TEXT,
|
|
1330
|
+
|
|
1331
|
+
-- Hybrid Storage: Pricing metadata for recalculation
|
|
1332
|
+
pricing_version TEXT,
|
|
1333
|
+
|
|
1334
|
+
-- Hybrid Storage: Context for cost attribution
|
|
1335
|
+
task_id TEXT,
|
|
1336
|
+
competition_id TEXT,
|
|
1337
|
+
variant_id TEXT,
|
|
1338
|
+
|
|
1339
|
+
FOREIGN KEY (invocation_id) REFERENCES model_invocations(id)
|
|
1340
|
+
);
|
|
1341
|
+
|
|
1342
|
+
CREATE INDEX IF NOT EXISTS idx_enriched_invocations_invocation_id
|
|
1343
|
+
ON enriched_invocations(invocation_id);
|
|
1344
|
+
CREATE INDEX IF NOT EXISTS idx_enriched_invocations_task_id
|
|
1345
|
+
ON enriched_invocations(task_id);
|
|
1346
|
+
CREATE INDEX IF NOT EXISTS idx_enriched_invocations_competition_id
|
|
1347
|
+
ON enriched_invocations(competition_id);
|
|
1348
|
+
|
|
1349
|
+
-- Hourly aggregates table
|
|
1350
|
+
CREATE TABLE IF NOT EXISTS aggregates_hourly (
|
|
1351
|
+
bucket_start INTEGER NOT NULL,
|
|
1352
|
+
provider_id TEXT NOT NULL,
|
|
1353
|
+
model_id TEXT NOT NULL,
|
|
1354
|
+
total_invocations INTEGER NOT NULL,
|
|
1355
|
+
total_tokens_in INTEGER NOT NULL,
|
|
1356
|
+
total_tokens_out INTEGER NOT NULL,
|
|
1357
|
+
avg_cost_cents REAL NOT NULL,
|
|
1358
|
+
p95_latency_ms INTEGER NOT NULL,
|
|
1359
|
+
error_rate REAL NOT NULL,
|
|
1360
|
+
PRIMARY KEY (bucket_start, provider_id, model_id)
|
|
1361
|
+
);
|
|
1362
|
+
|
|
1363
|
+
-- Daily cost aggregates table (queried by getDailyCostAggregations)
|
|
1364
|
+
CREATE TABLE IF NOT EXISTS cost_aggregations_daily (
|
|
1365
|
+
date TEXT NOT NULL,
|
|
1366
|
+
provider TEXT NOT NULL,
|
|
1367
|
+
model TEXT NOT NULL,
|
|
1368
|
+
execution_count INTEGER NOT NULL DEFAULT 0,
|
|
1369
|
+
total_cost_usd REAL NOT NULL DEFAULT 0,
|
|
1370
|
+
total_input_tokens INTEGER NOT NULL DEFAULT 0,
|
|
1371
|
+
total_output_tokens INTEGER NOT NULL DEFAULT 0,
|
|
1372
|
+
avg_cost_per_execution REAL NOT NULL DEFAULT 0,
|
|
1373
|
+
PRIMARY KEY (date, provider, model)
|
|
1374
|
+
);
|
|
1375
|
+
CREATE INDEX IF NOT EXISTS idx_cost_aggregations_daily_date
|
|
1376
|
+
ON cost_aggregations_daily(date);
|
|
1377
|
+
`);
|
|
1378
|
+
this.ensureColumn("model_invocations", "copilot_subcommand", "TEXT");
|
|
1379
|
+
this.ensureColumn("model_invocations", "execution_source", "TEXT");
|
|
1380
|
+
this.ensureColumn("model_invocations", "requested_model_id", "TEXT");
|
|
1381
|
+
this.ensureColumn("model_invocations", "execution_model_id", "TEXT");
|
|
1382
|
+
this.ensureColumn("model_invocations", "reported_model_id", "TEXT");
|
|
1383
|
+
this.ensureColumn("model_invocations", "backend", "TEXT");
|
|
1384
|
+
this.ensureColumn("model_invocations", "scope", "TEXT");
|
|
1385
|
+
this.ensureColumn("model_invocations", "context_selector", "TEXT");
|
|
1386
|
+
this.ensureColumn("model_invocations", "resolution_source", "TEXT");
|
|
1387
|
+
this.ensureColumn("model_invocations", "task_id", "TEXT");
|
|
1388
|
+
this.ensureColumn("model_invocations", "competition_id", "TEXT");
|
|
1389
|
+
this.ensureColumn("model_invocations", "variant_id", "TEXT");
|
|
1390
|
+
this.ensureColumn("enriched_invocations", "billing_mode", "TEXT");
|
|
1391
|
+
this.ensureColumn("enriched_invocations", "usage_data", "TEXT");
|
|
1392
|
+
this.ensureColumn("enriched_invocations", "cost_snapshot", "TEXT");
|
|
1393
|
+
this.ensureColumn("enriched_invocations", "pricing_version", "TEXT");
|
|
1394
|
+
this.ensureColumn("enriched_invocations", "task_id", "TEXT");
|
|
1395
|
+
this.ensureColumn("enriched_invocations", "competition_id", "TEXT");
|
|
1396
|
+
this.ensureColumn("enriched_invocations", "variant_id", "TEXT");
|
|
1397
|
+
}
|
|
1398
|
+
/**
|
|
1399
|
+
* Ensures a column exists in a table, adding it if necessary.
|
|
1400
|
+
* Security: Table name is validated against whitelist to prevent SQL injection (CWE-89).
|
|
1401
|
+
* Column name and definition are parameterized through prepared statements where possible.
|
|
1402
|
+
*
|
|
1403
|
+
* @param table - The table name (must be in ALLOWED_TABLE_NAMES)
|
|
1404
|
+
* @param column - The column name to ensure exists
|
|
1405
|
+
* @param definition - The SQL column definition (e.g., 'TEXT', 'INTEGER NOT NULL')
|
|
1406
|
+
* @throws {Error} If the table name is not in the whitelist
|
|
1407
|
+
*/
|
|
1408
|
+
ensureColumn(table, column, definition) {
|
|
1409
|
+
const validatedTable = validateTableName(table);
|
|
1410
|
+
const validatedColumn = validateSqlIdentifier(column, "column name");
|
|
1411
|
+
const validatedDefinition = validateColumnDefinition(definition);
|
|
1412
|
+
try {
|
|
1413
|
+
const infoStmt = this.db.prepare(`PRAGMA table_info(${validatedTable})`);
|
|
1414
|
+
const columns = infoStmt.all() ?? [];
|
|
1415
|
+
const hasColumn = columns.some((col) => col.name === validatedColumn);
|
|
1416
|
+
if (!hasColumn) {
|
|
1417
|
+
this.db.exec(
|
|
1418
|
+
`ALTER TABLE ${validatedTable} ADD COLUMN ${validatedColumn} ${validatedDefinition}`
|
|
1419
|
+
);
|
|
1420
|
+
}
|
|
1421
|
+
} catch (error) {
|
|
1422
|
+
if (!error || !(error instanceof Error) || !error.message.includes("Invalid table name")) {
|
|
1423
|
+
emitWarning(
|
|
1424
|
+
"WS-003",
|
|
1425
|
+
{
|
|
1426
|
+
taskId: "system",
|
|
1427
|
+
subsystem: "telemetry",
|
|
1428
|
+
executionPhase: "setup"
|
|
1429
|
+
},
|
|
1430
|
+
{
|
|
1431
|
+
severity: "WARN" /* WARN */,
|
|
1432
|
+
metadata: {
|
|
1433
|
+
operation: "ensure_column",
|
|
1434
|
+
table,
|
|
1435
|
+
column,
|
|
1436
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
);
|
|
1440
|
+
} else {
|
|
1441
|
+
throw error;
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
/**
|
|
1446
|
+
* Insert a model invocation record
|
|
1447
|
+
*/
|
|
1448
|
+
insertInvocation(invocation) {
|
|
1449
|
+
const stmt = this.db.prepare(`
|
|
1450
|
+
INSERT INTO model_invocations (
|
|
1451
|
+
id, timestamp, parent_workflow_id, provider_id, model_id,
|
|
1452
|
+
registry_version, tokens_in, tokens_out, time_request_sent_ms,
|
|
1453
|
+
time_first_token_ms, time_last_token_ms, time_to_first_token_ms,
|
|
1454
|
+
total_time_ms, status, error_code, request_size_bytes, agent_role,
|
|
1455
|
+
copilot_subcommand, execution_source,
|
|
1456
|
+
requested_model_id, execution_model_id, reported_model_id,
|
|
1457
|
+
backend, scope, context_selector, resolution_source,
|
|
1458
|
+
task_id, competition_id, variant_id
|
|
1459
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1460
|
+
`);
|
|
1461
|
+
stmt.run(
|
|
1462
|
+
invocation.id,
|
|
1463
|
+
invocation.timestamp,
|
|
1464
|
+
invocation.parentWorkflowId ?? null,
|
|
1465
|
+
invocation.providerId,
|
|
1466
|
+
invocation.modelId,
|
|
1467
|
+
invocation.registryVersion,
|
|
1468
|
+
invocation.tokensIn ?? null,
|
|
1469
|
+
invocation.tokensOut ?? null,
|
|
1470
|
+
invocation.timeRequestSentMs,
|
|
1471
|
+
invocation.timeFirstTokenMs ?? null,
|
|
1472
|
+
invocation.timeLastTokenMs ?? null,
|
|
1473
|
+
invocation.timeToFirstTokenMs ?? null,
|
|
1474
|
+
invocation.totalTimeMs ?? null,
|
|
1475
|
+
invocation.status,
|
|
1476
|
+
invocation.errorCode ?? null,
|
|
1477
|
+
invocation.requestSizeBytes ?? null,
|
|
1478
|
+
invocation.agentRole ?? null,
|
|
1479
|
+
invocation.copilotSubcommand ?? null,
|
|
1480
|
+
invocation.executionSource ?? null,
|
|
1481
|
+
invocation.requestedModelId ?? null,
|
|
1482
|
+
invocation.executionModelId ?? null,
|
|
1483
|
+
invocation.reportedModelId ?? null,
|
|
1484
|
+
invocation.backend ?? null,
|
|
1485
|
+
invocation.scope ?? null,
|
|
1486
|
+
invocation.contextSelector ?? null,
|
|
1487
|
+
invocation.resolutionSource ?? null,
|
|
1488
|
+
invocation.taskId ?? null,
|
|
1489
|
+
invocation.competitionId ?? invocation.taskId ?? null,
|
|
1490
|
+
invocation.variantId ?? null
|
|
1491
|
+
);
|
|
1492
|
+
}
|
|
1493
|
+
/**
|
|
1494
|
+
* Get a model invocation by ID
|
|
1495
|
+
*/
|
|
1496
|
+
getInvocation(id) {
|
|
1497
|
+
const stmt = this.db.prepare(`
|
|
1498
|
+
SELECT * FROM model_invocations WHERE id = ?
|
|
1499
|
+
`);
|
|
1500
|
+
const row = stmt.get(id);
|
|
1501
|
+
return row ? convertRowToCamelCase(row) : null;
|
|
1502
|
+
}
|
|
1503
|
+
/**
|
|
1504
|
+
* Insert a registry snapshot
|
|
1505
|
+
*/
|
|
1506
|
+
insertRegistrySnapshot(snapshot) {
|
|
1507
|
+
const stmt = this.db.prepare(`
|
|
1508
|
+
INSERT OR REPLACE INTO registry_snapshots (registry_version, created_at, json_blob)
|
|
1509
|
+
VALUES (?, ?, ?)
|
|
1510
|
+
`);
|
|
1511
|
+
stmt.run(snapshot.registryVersion, snapshot.createdAt, snapshot.jsonBlob);
|
|
1512
|
+
}
|
|
1513
|
+
/**
|
|
1514
|
+
* Get the most recently persisted registry snapshot, if any.
|
|
1515
|
+
*/
|
|
1516
|
+
getLatestRegistrySnapshot() {
|
|
1517
|
+
const snapshots = this.getRegistrySnapshots(1);
|
|
1518
|
+
return snapshots[0] ?? null;
|
|
1519
|
+
}
|
|
1520
|
+
/**
|
|
1521
|
+
* List registry snapshots newest-first.
|
|
1522
|
+
*
|
|
1523
|
+
* Used by enrichment when an invocation's stamped registry_version is a
|
|
1524
|
+
* placeholder / stale id: prefer the exact version when present, then walk
|
|
1525
|
+
* other persisted snapshots so known providers still price.
|
|
1526
|
+
*/
|
|
1527
|
+
getRegistrySnapshots(limit) {
|
|
1528
|
+
const boundedLimit = typeof limit === "number" && Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : void 0;
|
|
1529
|
+
const stmt = this.db.prepare(
|
|
1530
|
+
boundedLimit !== void 0 ? `
|
|
1531
|
+
SELECT * FROM registry_snapshots
|
|
1532
|
+
ORDER BY created_at DESC
|
|
1533
|
+
LIMIT ?
|
|
1534
|
+
` : `
|
|
1535
|
+
SELECT * FROM registry_snapshots
|
|
1536
|
+
ORDER BY created_at DESC
|
|
1537
|
+
`
|
|
1538
|
+
);
|
|
1539
|
+
const rows = (boundedLimit !== void 0 ? stmt.all(boundedLimit) : stmt.all()) ?? [];
|
|
1540
|
+
return rows.map(
|
|
1541
|
+
(row) => convertRowToCamelCase(row)
|
|
1542
|
+
).filter((snapshot) => snapshot !== null);
|
|
1543
|
+
}
|
|
1544
|
+
/**
|
|
1545
|
+
* Get the most recently persisted registry version, if any.
|
|
1546
|
+
*/
|
|
1547
|
+
getLatestRegistryVersion() {
|
|
1548
|
+
return this.getLatestRegistrySnapshot()?.registryVersion ?? null;
|
|
1549
|
+
}
|
|
1550
|
+
/**
|
|
1551
|
+
* Get a registry snapshot by version
|
|
1552
|
+
*/
|
|
1553
|
+
getRegistrySnapshot(registryVersion) {
|
|
1554
|
+
const stmt = this.db.prepare(`
|
|
1555
|
+
SELECT * FROM registry_snapshots WHERE registry_version = ?
|
|
1556
|
+
`);
|
|
1557
|
+
const row = stmt.get(registryVersion);
|
|
1558
|
+
return row ? convertRowToCamelCase(row) : null;
|
|
1559
|
+
}
|
|
1560
|
+
/**
|
|
1561
|
+
* Insert an enriched invocation record
|
|
1562
|
+
*/
|
|
1563
|
+
insertEnrichedInvocation(enriched) {
|
|
1564
|
+
const stmt = this.db.prepare(`
|
|
1565
|
+
INSERT OR REPLACE INTO enriched_invocations (
|
|
1566
|
+
id, invocation_id, computed_cost_cents, pricing_source, enriched_at, billing_mode,
|
|
1567
|
+
usage_data, cost_snapshot, pricing_version, task_id, competition_id, variant_id
|
|
1568
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1569
|
+
`);
|
|
1570
|
+
stmt.run(
|
|
1571
|
+
enriched.id,
|
|
1572
|
+
enriched.invocationId,
|
|
1573
|
+
enriched.computedCostCents ?? null,
|
|
1574
|
+
enriched.pricingSource,
|
|
1575
|
+
enriched.enrichedAt,
|
|
1576
|
+
enriched.billingMode ?? null,
|
|
1577
|
+
enriched.usageData ?? null,
|
|
1578
|
+
enriched.costSnapshot ?? null,
|
|
1579
|
+
enriched.pricingVersion ?? null,
|
|
1580
|
+
enriched.taskId ?? null,
|
|
1581
|
+
enriched.competitionId ?? null,
|
|
1582
|
+
enriched.variantId ?? null
|
|
1583
|
+
);
|
|
1584
|
+
}
|
|
1585
|
+
/**
|
|
1586
|
+
* Execute related telemetry writes atomically when the SQLite driver supports it.
|
|
1587
|
+
*/
|
|
1588
|
+
transaction(operation) {
|
|
1589
|
+
if (!this.db.transaction) {
|
|
1590
|
+
return operation();
|
|
1591
|
+
}
|
|
1592
|
+
return this.db.transaction(operation)();
|
|
1593
|
+
}
|
|
1594
|
+
/**
|
|
1595
|
+
* Get unenriched invocations
|
|
1596
|
+
*/
|
|
1597
|
+
getUnenrichedInvocations(limit = 100) {
|
|
1598
|
+
const stmt = this.db.prepare(`
|
|
1599
|
+
SELECT i.* FROM model_invocations i
|
|
1600
|
+
LEFT JOIN enriched_invocations e ON i.id = e.invocation_id
|
|
1601
|
+
WHERE e.id IS NULL
|
|
1602
|
+
ORDER BY i.timestamp ASC
|
|
1603
|
+
LIMIT ?
|
|
1604
|
+
`);
|
|
1605
|
+
const rows = stmt.all(limit) ?? [];
|
|
1606
|
+
return rows.map(
|
|
1607
|
+
(row) => convertRowToCamelCase(row)
|
|
1608
|
+
).filter((x) => x !== null);
|
|
1609
|
+
}
|
|
1610
|
+
/**
|
|
1611
|
+
* Upsert hourly aggregate
|
|
1612
|
+
*/
|
|
1613
|
+
upsertHourlyAggregate(aggregate) {
|
|
1614
|
+
const stmt = this.db.prepare(`
|
|
1615
|
+
INSERT OR REPLACE INTO aggregates_hourly (
|
|
1616
|
+
bucket_start, provider_id, model_id, total_invocations,
|
|
1617
|
+
total_tokens_in, total_tokens_out, avg_cost_cents,
|
|
1618
|
+
p95_latency_ms, error_rate
|
|
1619
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1620
|
+
`);
|
|
1621
|
+
stmt.run(
|
|
1622
|
+
aggregate.bucketStart,
|
|
1623
|
+
aggregate.providerId,
|
|
1624
|
+
aggregate.modelId,
|
|
1625
|
+
aggregate.totalInvocations,
|
|
1626
|
+
aggregate.totalTokensIn,
|
|
1627
|
+
aggregate.totalTokensOut,
|
|
1628
|
+
aggregate.avgCostCents,
|
|
1629
|
+
aggregate.p95LatencyMs,
|
|
1630
|
+
aggregate.errorRate
|
|
1631
|
+
);
|
|
1632
|
+
}
|
|
1633
|
+
/**
|
|
1634
|
+
* Get hourly aggregates for a time range
|
|
1635
|
+
*/
|
|
1636
|
+
getHourlyAggregates(providerId, modelId, startBucket, endBucket) {
|
|
1637
|
+
const stmt = this.db.prepare(`
|
|
1638
|
+
SELECT * FROM aggregates_hourly
|
|
1639
|
+
WHERE provider_id = ? AND model_id = ?
|
|
1640
|
+
AND bucket_start >= ? AND bucket_start <= ?
|
|
1641
|
+
ORDER BY bucket_start ASC
|
|
1642
|
+
`);
|
|
1643
|
+
const rows = stmt.all(providerId, modelId, startBucket, endBucket) ?? [];
|
|
1644
|
+
return rows.map(
|
|
1645
|
+
(row) => convertRowToCamelCase(row)
|
|
1646
|
+
).filter((x) => x !== null);
|
|
1647
|
+
}
|
|
1648
|
+
/**
|
|
1649
|
+
* Get cost breakdown by agent role for swarm executions
|
|
1650
|
+
* Returns aggregated cost metrics grouped by agent role
|
|
1651
|
+
*/
|
|
1652
|
+
getRoleCostBreakdown(startTimestamp, endTimestamp) {
|
|
1653
|
+
let query = `
|
|
1654
|
+
SELECT
|
|
1655
|
+
i.agent_role as agent_role,
|
|
1656
|
+
COUNT(*) as total_invocations,
|
|
1657
|
+
COALESCE(SUM(i.tokens_in), 0) as total_tokens_in,
|
|
1658
|
+
COALESCE(SUM(i.tokens_out), 0) as total_tokens_out,
|
|
1659
|
+
COALESCE(AVG(CASE WHEN e.billing_mode IS NULL OR e.billing_mode = 'metered' THEN e.computed_cost_cents END), 0) as avg_cost_cents
|
|
1660
|
+
FROM model_invocations i
|
|
1661
|
+
LEFT JOIN enriched_invocations e ON i.id = e.invocation_id
|
|
1662
|
+
WHERE i.agent_role IS NOT NULL
|
|
1663
|
+
`;
|
|
1664
|
+
const params = [];
|
|
1665
|
+
if (startTimestamp !== void 0) {
|
|
1666
|
+
query += " AND i.timestamp >= ?";
|
|
1667
|
+
params.push(startTimestamp);
|
|
1668
|
+
}
|
|
1669
|
+
if (endTimestamp !== void 0) {
|
|
1670
|
+
query += " AND i.timestamp <= ?";
|
|
1671
|
+
params.push(endTimestamp);
|
|
1672
|
+
}
|
|
1673
|
+
query += `
|
|
1674
|
+
GROUP BY i.agent_role
|
|
1675
|
+
ORDER BY avg_cost_cents DESC
|
|
1676
|
+
`;
|
|
1677
|
+
const stmt = this.db.prepare(query);
|
|
1678
|
+
const rows = params.length > 0 ? stmt.all(...params) : stmt.all();
|
|
1679
|
+
return (rows ?? []).map(
|
|
1680
|
+
(row) => convertRowToCamelCase(row)
|
|
1681
|
+
).filter(
|
|
1682
|
+
(x) => x !== null
|
|
1683
|
+
);
|
|
1684
|
+
}
|
|
1685
|
+
/**
|
|
1686
|
+
* Return TTFT and token-throughput statistics grouped by provider/model.
|
|
1687
|
+
*
|
|
1688
|
+
* Only streaming invocations (status='success', time_to_first_token_ms IS NOT NULL)
|
|
1689
|
+
* are included. Rows with total_time_ms <= 0 or null tokens_out are excluded from
|
|
1690
|
+
* the tokens/sec calculation to avoid divide-by-zero and noise.
|
|
1691
|
+
*
|
|
1692
|
+
* Percentiles are computed in JS from a sorted slice, so they are exact for the
|
|
1693
|
+
* dataset (not approximations). Safe to call on an empty or partially-populated DB.
|
|
1694
|
+
*
|
|
1695
|
+
* @param limit - Max provider/model groups to return, ordered by invocation count desc.
|
|
1696
|
+
*/
|
|
1697
|
+
getTtftReport(limit = 20, options = {}) {
|
|
1698
|
+
let rows;
|
|
1699
|
+
try {
|
|
1700
|
+
const stmt = this.db.prepare(`
|
|
1701
|
+
SELECT
|
|
1702
|
+
provider_id,
|
|
1703
|
+
model_id,
|
|
1704
|
+
time_to_first_token_ms,
|
|
1705
|
+
tokens_out,
|
|
1706
|
+
total_time_ms
|
|
1707
|
+
FROM model_invocations
|
|
1708
|
+
WHERE time_to_first_token_ms IS NOT NULL
|
|
1709
|
+
AND status = 'success'
|
|
1710
|
+
${options.includeRaw ? "" : invocationAnalyticsQualityWhereSql()}
|
|
1711
|
+
ORDER BY provider_id, model_id, time_to_first_token_ms ASC
|
|
1712
|
+
`);
|
|
1713
|
+
rows = stmt.all();
|
|
1714
|
+
} catch {
|
|
1715
|
+
return [];
|
|
1716
|
+
}
|
|
1717
|
+
if (rows.length === 0) return [];
|
|
1718
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1719
|
+
for (const row of rows) {
|
|
1720
|
+
const compatibleModelId = resolveCompatibleInvocationModelId(row.provider_id, row.model_id);
|
|
1721
|
+
const key = `${row.provider_id}\0${compatibleModelId}`;
|
|
1722
|
+
let group = groups.get(key);
|
|
1723
|
+
if (!group) {
|
|
1724
|
+
group = { providerId: row.provider_id, modelId: compatibleModelId, rows: [] };
|
|
1725
|
+
groups.set(key, group);
|
|
1726
|
+
}
|
|
1727
|
+
group.rows.push(row);
|
|
1728
|
+
}
|
|
1729
|
+
const result = [];
|
|
1730
|
+
for (const group of groups.values()) {
|
|
1731
|
+
const groupRows = group.rows;
|
|
1732
|
+
const ttfts = groupRows.map((r) => r.time_to_first_token_ms).sort((a, b) => a - b);
|
|
1733
|
+
const n = ttfts.length;
|
|
1734
|
+
if (n === 0) continue;
|
|
1735
|
+
const p50 = ttfts[Math.min(Math.floor(n * 0.5), n - 1)] ?? null;
|
|
1736
|
+
const p95 = ttfts[Math.min(Math.floor(n * 0.95), n - 1)] ?? null;
|
|
1737
|
+
const avgTtft = Math.round(ttfts.reduce((s, v) => s + v, 0) / n);
|
|
1738
|
+
const tokensSecs = groupRows.filter(
|
|
1739
|
+
(r) => r.total_time_ms !== null && r.total_time_ms > 0 && r.tokens_out !== null && r.tokens_out > 0
|
|
1740
|
+
).map((r) => r.tokens_out * 1e3 / r.total_time_ms);
|
|
1741
|
+
const avgTokensPerSec = tokensSecs.length > 0 ? Math.round(tokensSecs.reduce((s, v) => s + v, 0) / tokensSecs.length * 100) / 100 : null;
|
|
1742
|
+
result.push({
|
|
1743
|
+
providerId: group.providerId,
|
|
1744
|
+
modelId: group.modelId,
|
|
1745
|
+
streamingInvocations: n,
|
|
1746
|
+
p50TtftMs: p50,
|
|
1747
|
+
p95TtftMs: p95,
|
|
1748
|
+
avgTtftMs: avgTtft,
|
|
1749
|
+
avgTokensPerSec
|
|
1750
|
+
});
|
|
1751
|
+
}
|
|
1752
|
+
result.sort((a, b) => b.streamingInvocations - a.streamingInvocations);
|
|
1753
|
+
return result.slice(0, limit);
|
|
1754
|
+
}
|
|
1755
|
+
/**
|
|
1756
|
+
* Close database connection
|
|
1757
|
+
*/
|
|
1758
|
+
close() {
|
|
1759
|
+
this.db.close();
|
|
1760
|
+
}
|
|
1761
|
+
/**
|
|
1762
|
+
* Backfill daily cost aggregates from model_invocations (optional enrichment).
|
|
1763
|
+
* Idempotent via INSERT OR REPLACE; safe to call before reads or in worker cycles.
|
|
1764
|
+
*
|
|
1765
|
+
* Source universe is every matching model_invocations row; enrichment is optional
|
|
1766
|
+
* via LEFT JOIN. Numeric cost totals are stored only for fully priced groups;
|
|
1767
|
+
* incomplete groups store 0 cost so partial known-member sums are never
|
|
1768
|
+
* persisted as totals. Coverage status for reads is derived by
|
|
1769
|
+
* {@link getDailyCostAggregations}.
|
|
1770
|
+
*
|
|
1771
|
+
* @returns Number of aggregate rows upserted, or 0 on failure (worker-compatible).
|
|
1772
|
+
*/
|
|
1773
|
+
backfillDailyCostAggregations(startDate, endDate) {
|
|
1774
|
+
const result = this.tryBackfillDailyCostAggregations(startDate, endDate);
|
|
1775
|
+
return result.ok ? result.changes : 0;
|
|
1776
|
+
}
|
|
1777
|
+
/**
|
|
1778
|
+
* Internal backfill with explicit success/failure (does not launder errors to "0 changes").
|
|
1779
|
+
*/
|
|
1780
|
+
tryBackfillDailyCostAggregations(startDate, endDate) {
|
|
1781
|
+
const params = [];
|
|
1782
|
+
let whereClause = "WHERE 1=1";
|
|
1783
|
+
if (startDate) {
|
|
1784
|
+
whereClause += " AND i.timestamp >= ?";
|
|
1785
|
+
params.push(utcDateStartMs(startDate));
|
|
1786
|
+
}
|
|
1787
|
+
if (endDate) {
|
|
1788
|
+
whereClause += " AND i.timestamp < ?";
|
|
1789
|
+
params.push(utcDateStartMs(endDate) + DAY_MS);
|
|
1790
|
+
}
|
|
1791
|
+
try {
|
|
1792
|
+
const stmt = this.db.prepare(`
|
|
1793
|
+
INSERT OR REPLACE INTO cost_aggregations_daily (
|
|
1794
|
+
date,
|
|
1795
|
+
provider,
|
|
1796
|
+
model,
|
|
1797
|
+
execution_count,
|
|
1798
|
+
total_cost_usd,
|
|
1799
|
+
total_input_tokens,
|
|
1800
|
+
total_output_tokens,
|
|
1801
|
+
avg_cost_per_execution
|
|
1802
|
+
)
|
|
1803
|
+
SELECT
|
|
1804
|
+
date(i.timestamp / 1000, 'unixepoch') as date,
|
|
1805
|
+
i.provider_id as provider,
|
|
1806
|
+
i.model_id as model,
|
|
1807
|
+
COUNT(*) as execution_count,
|
|
1808
|
+
CASE
|
|
1809
|
+
WHEN COUNT(*) > 0
|
|
1810
|
+
AND COUNT(*) = SUM(
|
|
1811
|
+
CASE WHEN (${SNAPSHOT_FIRST_COST_CENTS_SQL}) IS NOT NULL THEN 1 ELSE 0 END
|
|
1812
|
+
)
|
|
1813
|
+
THEN COALESCE(SUM((${SNAPSHOT_FIRST_COST_CENTS_SQL})), 0) / 100.0
|
|
1814
|
+
ELSE 0
|
|
1815
|
+
END as total_cost_usd,
|
|
1816
|
+
COALESCE(SUM(i.tokens_in), 0) as total_input_tokens,
|
|
1817
|
+
COALESCE(SUM(i.tokens_out), 0) as total_output_tokens,
|
|
1818
|
+
CASE
|
|
1819
|
+
WHEN COUNT(*) > 0
|
|
1820
|
+
AND COUNT(*) = SUM(
|
|
1821
|
+
CASE WHEN (${SNAPSHOT_FIRST_COST_CENTS_SQL}) IS NOT NULL THEN 1 ELSE 0 END
|
|
1822
|
+
)
|
|
1823
|
+
THEN COALESCE(SUM((${SNAPSHOT_FIRST_COST_CENTS_SQL})), 0) / 100.0 / COUNT(*)
|
|
1824
|
+
ELSE 0
|
|
1825
|
+
END as avg_cost_per_execution
|
|
1826
|
+
FROM model_invocations i
|
|
1827
|
+
LEFT JOIN enriched_invocations e ON e.invocation_id = i.id
|
|
1828
|
+
${whereClause}
|
|
1829
|
+
GROUP BY date, provider, model
|
|
1830
|
+
`);
|
|
1831
|
+
const result = params.length > 0 ? stmt.run(...params) : stmt.run();
|
|
1832
|
+
return { ok: true, changes: result.changes ?? 0 };
|
|
1833
|
+
} catch (error) {
|
|
1834
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1835
|
+
emitWarning(
|
|
1836
|
+
"WS-003",
|
|
1837
|
+
{
|
|
1838
|
+
taskId: "system",
|
|
1839
|
+
subsystem: "telemetry",
|
|
1840
|
+
executionPhase: "execution"
|
|
1841
|
+
},
|
|
1842
|
+
{
|
|
1843
|
+
severity: "WARN" /* WARN */,
|
|
1844
|
+
metadata: {
|
|
1845
|
+
operation: "backfill_daily_cost_aggregations",
|
|
1846
|
+
error: message,
|
|
1847
|
+
startDate,
|
|
1848
|
+
endDate
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
);
|
|
1852
|
+
return { ok: false, error: message };
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
/**
|
|
1856
|
+
* Get daily cost aggregations for a specified date range.
|
|
1857
|
+
*
|
|
1858
|
+
* Status-bearing: numeric totals/averages exist only when every included
|
|
1859
|
+
* invocation has authoritative snapshot-first cost evidence. Mixed or
|
|
1860
|
+
* all-unpriced evidence is `incomplete` (counts retained, costs null).
|
|
1861
|
+
* Query/backfill failure is `error` (no totals). Empty complete means no data.
|
|
1862
|
+
*
|
|
1863
|
+
* Source universe is every matching model_invocations row (optional enrichment
|
|
1864
|
+
* via LEFT JOIN). Live and legacy tables are composed at the storage-native
|
|
1865
|
+
* date+provider+model key first: live replaces only its exact native key;
|
|
1866
|
+
* legacy-only native keys remain. Only then are merged native rows aggregated
|
|
1867
|
+
* to the requested date/provider/model grouping. A group total is numeric only
|
|
1868
|
+
* when every constituent native row is complete (no partial subtotals).
|
|
1869
|
+
* Ordering is preserved (date DESC, then provider/model ASC when grouped).
|
|
1870
|
+
*/
|
|
1871
|
+
getDailyCostAggregations(startDate, endDate, groupBy) {
|
|
1872
|
+
const backfill = this.tryBackfillDailyCostAggregations(startDate, endDate);
|
|
1873
|
+
if (!backfill.ok) {
|
|
1874
|
+
return {
|
|
1875
|
+
status: "error",
|
|
1876
|
+
rows: [],
|
|
1877
|
+
executionCount: 0,
|
|
1878
|
+
pricedCount: 0,
|
|
1879
|
+
error: backfill.error,
|
|
1880
|
+
detail: "backfill_daily_cost_aggregations failed"
|
|
1881
|
+
};
|
|
1882
|
+
}
|
|
1883
|
+
try {
|
|
1884
|
+
const liveNative = this.queryLiveDailyCostCoverage(startDate, endDate);
|
|
1885
|
+
const legacyNative = this.queryPreseededDailyCostAggregationsNative(startDate, endDate);
|
|
1886
|
+
if (liveNative.length === 0) {
|
|
1887
|
+
const rows2 = this.aggregateNativeDailyCostRows(legacyNative, groupBy);
|
|
1888
|
+
return this.buildDailyCostResultFromRows(rows2);
|
|
1889
|
+
}
|
|
1890
|
+
if (legacyNative.length === 0) {
|
|
1891
|
+
const rows2 = this.aggregateNativeDailyCostRows(
|
|
1892
|
+
this.buildDailyCostRowsFromLive(liveNative),
|
|
1893
|
+
groupBy
|
|
1894
|
+
);
|
|
1895
|
+
return this.buildDailyCostResultFromRows(rows2);
|
|
1896
|
+
}
|
|
1897
|
+
const mergedNative = this.mergeLiveAndLegacyAtNativeKey(liveNative, legacyNative);
|
|
1898
|
+
const rows = this.aggregateNativeDailyCostRows(mergedNative, groupBy);
|
|
1899
|
+
return this.buildDailyCostResultFromRows(rows);
|
|
1900
|
+
} catch (error) {
|
|
1901
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1902
|
+
emitWarning(
|
|
1903
|
+
"WS-003",
|
|
1904
|
+
{
|
|
1905
|
+
taskId: "system",
|
|
1906
|
+
subsystem: "telemetry",
|
|
1907
|
+
executionPhase: "execution"
|
|
1908
|
+
},
|
|
1909
|
+
{
|
|
1910
|
+
severity: "WARN" /* WARN */,
|
|
1911
|
+
metadata: {
|
|
1912
|
+
operation: "query_daily_cost_aggregations",
|
|
1913
|
+
error: message,
|
|
1914
|
+
startDate,
|
|
1915
|
+
endDate
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
);
|
|
1919
|
+
return {
|
|
1920
|
+
status: "error",
|
|
1921
|
+
rows: [],
|
|
1922
|
+
executionCount: 0,
|
|
1923
|
+
pricedCount: 0,
|
|
1924
|
+
error: message,
|
|
1925
|
+
detail: "query_daily_cost_aggregations failed"
|
|
1926
|
+
};
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
/**
|
|
1930
|
+
* Storage-native merge identity: date + provider + model.
|
|
1931
|
+
*/
|
|
1932
|
+
nativeDailyCostKey(row) {
|
|
1933
|
+
return `${row.date}\0${row.provider ?? ""}\0${row.model ?? ""}`;
|
|
1934
|
+
}
|
|
1935
|
+
/**
|
|
1936
|
+
* Stable key for daily aggregation under the requested grouping.
|
|
1937
|
+
*/
|
|
1938
|
+
dailyCostAggregationKey(row, groupBy) {
|
|
1939
|
+
if (groupBy === "provider") {
|
|
1940
|
+
return `${row.date}\0${row.provider ?? ""}`;
|
|
1941
|
+
}
|
|
1942
|
+
if (groupBy === "model") {
|
|
1943
|
+
return `${row.date}\0${row.model ?? ""}`;
|
|
1944
|
+
}
|
|
1945
|
+
return row.date;
|
|
1946
|
+
}
|
|
1947
|
+
/**
|
|
1948
|
+
* Ordering: date DESC, then provider/model ASC when grouped (matches SQL ORDER BY).
|
|
1949
|
+
* At native resolution, order by date DESC, provider ASC, model ASC.
|
|
1950
|
+
*/
|
|
1951
|
+
compareDailyCostAggregationRows(a, b, groupBy) {
|
|
1952
|
+
if (a.date !== b.date) {
|
|
1953
|
+
return a.date < b.date ? 1 : -1;
|
|
1954
|
+
}
|
|
1955
|
+
if (groupBy === "provider" || groupBy === "native") {
|
|
1956
|
+
const ap = a.provider ?? "";
|
|
1957
|
+
const bp = b.provider ?? "";
|
|
1958
|
+
if (ap !== bp) {
|
|
1959
|
+
return ap < bp ? -1 : 1;
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
if (groupBy === "model" || groupBy === "native") {
|
|
1963
|
+
const am = a.model ?? "";
|
|
1964
|
+
const bm = b.model ?? "";
|
|
1965
|
+
if (am !== bm) {
|
|
1966
|
+
return am < bm ? -1 : 1;
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
return 0;
|
|
1970
|
+
}
|
|
1971
|
+
/**
|
|
1972
|
+
* Live coverage at native date+provider+model resolution.
|
|
1973
|
+
* Source universe: model_invocations; enrichment optional via LEFT JOIN.
|
|
1974
|
+
*/
|
|
1975
|
+
queryLiveDailyCostCoverage(startDate, endDate) {
|
|
1976
|
+
const params = [];
|
|
1977
|
+
let whereClause = "WHERE 1=1";
|
|
1978
|
+
if (startDate) {
|
|
1979
|
+
whereClause += " AND i.timestamp >= ?";
|
|
1980
|
+
params.push(utcDateStartMs(startDate));
|
|
1981
|
+
}
|
|
1982
|
+
if (endDate) {
|
|
1983
|
+
whereClause += " AND i.timestamp < ?";
|
|
1984
|
+
params.push(utcDateStartMs(endDate) + DAY_MS);
|
|
1985
|
+
}
|
|
1986
|
+
const query = `
|
|
1987
|
+
SELECT
|
|
1988
|
+
date(i.timestamp / 1000, 'unixepoch') as date,
|
|
1989
|
+
i.provider_id as provider,
|
|
1990
|
+
i.model_id as model,
|
|
1991
|
+
COUNT(*) as execution_count,
|
|
1992
|
+
SUM(
|
|
1993
|
+
CASE WHEN (${SNAPSHOT_FIRST_COST_CENTS_SQL}) IS NOT NULL THEN 1 ELSE 0 END
|
|
1994
|
+
) as priced_count,
|
|
1995
|
+
SUM((${SNAPSHOT_FIRST_COST_CENTS_SQL})) as total_cost_cents,
|
|
1996
|
+
COALESCE(SUM(i.tokens_in), 0) as total_input_tokens,
|
|
1997
|
+
COALESCE(SUM(i.tokens_out), 0) as total_output_tokens
|
|
1998
|
+
FROM model_invocations i
|
|
1999
|
+
LEFT JOIN enriched_invocations e ON e.invocation_id = i.id
|
|
2000
|
+
${whereClause}
|
|
2001
|
+
GROUP BY date, i.provider_id, i.model_id
|
|
2002
|
+
ORDER BY date DESC, provider ASC, model ASC
|
|
2003
|
+
`;
|
|
2004
|
+
const stmt = this.db.prepare(query);
|
|
2005
|
+
const rows = params.length > 0 ? stmt.all(...params) : stmt.all();
|
|
2006
|
+
return rows.map((row) => this.normalizeLiveDailyCostCoverageRow(row));
|
|
2007
|
+
}
|
|
2008
|
+
normalizeLiveDailyCostCoverageRow(row) {
|
|
2009
|
+
if (row === null || typeof row !== "object") {
|
|
2010
|
+
return {
|
|
2011
|
+
date: "",
|
|
2012
|
+
provider: "",
|
|
2013
|
+
model: "",
|
|
2014
|
+
execution_count: 0,
|
|
2015
|
+
priced_count: 0,
|
|
2016
|
+
total_cost_cents: null,
|
|
2017
|
+
total_input_tokens: 0,
|
|
2018
|
+
total_output_tokens: 0
|
|
2019
|
+
};
|
|
2020
|
+
}
|
|
2021
|
+
const dateValue = Reflect.get(row, "date");
|
|
2022
|
+
const providerValue = Reflect.get(row, "provider");
|
|
2023
|
+
const modelValue = Reflect.get(row, "model");
|
|
2024
|
+
const totalCostCentsValue = Reflect.get(row, "total_cost_cents");
|
|
2025
|
+
const totalCostCents = totalCostCentsValue === null || totalCostCentsValue === void 0 ? null : Number(totalCostCentsValue);
|
|
2026
|
+
return {
|
|
2027
|
+
date: typeof dateValue === "string" ? dateValue : String(dateValue ?? ""),
|
|
2028
|
+
provider: typeof providerValue === "string" ? providerValue : String(providerValue ?? ""),
|
|
2029
|
+
model: typeof modelValue === "string" ? modelValue : String(modelValue ?? ""),
|
|
2030
|
+
execution_count: Number(Reflect.get(row, "execution_count")) || 0,
|
|
2031
|
+
priced_count: Number(Reflect.get(row, "priced_count")) || 0,
|
|
2032
|
+
total_cost_cents: totalCostCents === null || Number.isNaN(totalCostCents) ? null : totalCostCents,
|
|
2033
|
+
total_input_tokens: Number(Reflect.get(row, "total_input_tokens")) || 0,
|
|
2034
|
+
total_output_tokens: Number(Reflect.get(row, "total_output_tokens")) || 0
|
|
2035
|
+
};
|
|
2036
|
+
}
|
|
2037
|
+
buildDailyCostRowsFromLive(live) {
|
|
2038
|
+
return live.map((row) => {
|
|
2039
|
+
const execCount = Number(row.execution_count) || 0;
|
|
2040
|
+
const priced = Number(row.priced_count) || 0;
|
|
2041
|
+
const rowComplete = execCount > 0 && priced === execCount;
|
|
2042
|
+
const totalCost = rowComplete ? (Number(row.total_cost_cents) || 0) / 100 : null;
|
|
2043
|
+
const avgCostPerExecution = rowComplete && execCount > 0 && totalCost !== null ? totalCost / execCount : null;
|
|
2044
|
+
return {
|
|
2045
|
+
date: row.date,
|
|
2046
|
+
provider: row.provider,
|
|
2047
|
+
model: row.model,
|
|
2048
|
+
executionCount: execCount,
|
|
2049
|
+
pricedCount: priced,
|
|
2050
|
+
totalTokensIn: Number(row.total_input_tokens) || 0,
|
|
2051
|
+
totalTokensOut: Number(row.total_output_tokens) || 0,
|
|
2052
|
+
totalCost,
|
|
2053
|
+
avgCostPerExecution,
|
|
2054
|
+
status: rowComplete ? "complete" : "incomplete"
|
|
2055
|
+
};
|
|
2056
|
+
});
|
|
2057
|
+
}
|
|
2058
|
+
buildDailyCostResultFromRows(rows) {
|
|
2059
|
+
let executionCount = 0;
|
|
2060
|
+
let pricedCount = 0;
|
|
2061
|
+
let anyIncomplete = false;
|
|
2062
|
+
for (const row of rows) {
|
|
2063
|
+
executionCount += row.executionCount;
|
|
2064
|
+
pricedCount += row.pricedCount;
|
|
2065
|
+
if (row.status === "incomplete") {
|
|
2066
|
+
anyIncomplete = true;
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
return {
|
|
2070
|
+
status: anyIncomplete ? "incomplete" : "complete",
|
|
2071
|
+
rows,
|
|
2072
|
+
executionCount,
|
|
2073
|
+
pricedCount,
|
|
2074
|
+
...anyIncomplete ? { detail: `incomplete cost evidence (${pricedCount}/${executionCount} priced)` } : {}
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2077
|
+
/**
|
|
2078
|
+
* Compose live and legacy rows at storage-native date+provider+model keys.
|
|
2079
|
+
* Live is authoritative for its exact native key only; legacy-only keys remain.
|
|
2080
|
+
*/
|
|
2081
|
+
mergeLiveAndLegacyAtNativeKey(live, legacyRows) {
|
|
2082
|
+
const liveRows = this.buildDailyCostRowsFromLive(live);
|
|
2083
|
+
const liveKeys = new Set(liveRows.map((row) => this.nativeDailyCostKey(row)));
|
|
2084
|
+
const legacyOnly = legacyRows.filter((row) => !liveKeys.has(this.nativeDailyCostKey(row)));
|
|
2085
|
+
return [...liveRows, ...legacyOnly];
|
|
2086
|
+
}
|
|
2087
|
+
/**
|
|
2088
|
+
* Aggregate storage-native rows to the requested date/provider/model grouping.
|
|
2089
|
+
* A group total/average is numeric only when every constituent native row is
|
|
2090
|
+
* complete; partial subtotals are never exposed.
|
|
2091
|
+
*/
|
|
2092
|
+
aggregateNativeDailyCostRows(nativeRows, groupBy) {
|
|
2093
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2094
|
+
for (const row of nativeRows) {
|
|
2095
|
+
const key = this.dailyCostAggregationKey(row, groupBy);
|
|
2096
|
+
let group = groups.get(key);
|
|
2097
|
+
if (!group) {
|
|
2098
|
+
group = {
|
|
2099
|
+
date: row.date,
|
|
2100
|
+
...groupBy === "provider" ? { provider: row.provider } : {},
|
|
2101
|
+
...groupBy === "model" ? { model: row.model } : {},
|
|
2102
|
+
members: []
|
|
2103
|
+
};
|
|
2104
|
+
groups.set(key, group);
|
|
2105
|
+
}
|
|
2106
|
+
group.members.push(row);
|
|
2107
|
+
}
|
|
2108
|
+
const aggregated = [];
|
|
2109
|
+
for (const group of groups.values()) {
|
|
2110
|
+
let executionCount = 0;
|
|
2111
|
+
let pricedCount = 0;
|
|
2112
|
+
let totalTokensIn = 0;
|
|
2113
|
+
let totalTokensOut = 0;
|
|
2114
|
+
let allComplete = true;
|
|
2115
|
+
let totalCostSum = 0;
|
|
2116
|
+
for (const member of group.members) {
|
|
2117
|
+
executionCount += member.executionCount;
|
|
2118
|
+
pricedCount += member.pricedCount;
|
|
2119
|
+
totalTokensIn += member.totalTokensIn;
|
|
2120
|
+
totalTokensOut += member.totalTokensOut;
|
|
2121
|
+
if (member.status !== "complete" || member.totalCost === null) {
|
|
2122
|
+
allComplete = false;
|
|
2123
|
+
} else {
|
|
2124
|
+
totalCostSum += member.totalCost;
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
const complete = allComplete && executionCount > 0;
|
|
2128
|
+
const totalCost = complete ? totalCostSum : null;
|
|
2129
|
+
const avgCostPerExecution = complete && executionCount > 0 && totalCost !== null ? totalCost / executionCount : null;
|
|
2130
|
+
aggregated.push({
|
|
2131
|
+
date: group.date,
|
|
2132
|
+
provider: group.provider,
|
|
2133
|
+
model: group.model,
|
|
2134
|
+
executionCount,
|
|
2135
|
+
pricedCount,
|
|
2136
|
+
totalTokensIn,
|
|
2137
|
+
totalTokensOut,
|
|
2138
|
+
totalCost,
|
|
2139
|
+
avgCostPerExecution,
|
|
2140
|
+
status: complete ? "complete" : "incomplete"
|
|
2141
|
+
});
|
|
2142
|
+
}
|
|
2143
|
+
return aggregated.sort((a, b) => this.compareDailyCostAggregationRows(a, b, groupBy));
|
|
2144
|
+
}
|
|
2145
|
+
/**
|
|
2146
|
+
* Pre-seeded cost_aggregations_daily fixtures at storage-native resolution
|
|
2147
|
+
* (date, provider, model). Treated as complete; pricedCount equals
|
|
2148
|
+
* executionCount. Callers that also have live coverage must merge at the
|
|
2149
|
+
* native key so live remains authoritative for exact collisions only.
|
|
2150
|
+
*/
|
|
2151
|
+
queryPreseededDailyCostAggregationsNative(startDate, endDate) {
|
|
2152
|
+
const params = [];
|
|
2153
|
+
let whereClause = " WHERE 1=1";
|
|
2154
|
+
if (startDate) {
|
|
2155
|
+
whereClause += " AND date >= ?";
|
|
2156
|
+
params.push(startDate);
|
|
2157
|
+
}
|
|
2158
|
+
if (endDate) {
|
|
2159
|
+
whereClause += " AND date <= ?";
|
|
2160
|
+
params.push(endDate);
|
|
2161
|
+
}
|
|
2162
|
+
const query = `
|
|
2163
|
+
SELECT
|
|
2164
|
+
date,
|
|
2165
|
+
provider,
|
|
2166
|
+
model,
|
|
2167
|
+
execution_count,
|
|
2168
|
+
total_cost_usd,
|
|
2169
|
+
total_input_tokens,
|
|
2170
|
+
total_output_tokens,
|
|
2171
|
+
avg_cost_per_execution
|
|
2172
|
+
FROM cost_aggregations_daily
|
|
2173
|
+
${whereClause}
|
|
2174
|
+
ORDER BY date DESC, provider ASC, model ASC
|
|
2175
|
+
`;
|
|
2176
|
+
const stmt = this.db.prepare(query);
|
|
2177
|
+
const rawRows = params.length > 0 ? stmt.all(...params) : stmt.all();
|
|
2178
|
+
return rawRows.map((raw) => this.normalizePreseededDailyCostAggregateRow(raw));
|
|
2179
|
+
}
|
|
2180
|
+
normalizePreseededDailyCostAggregateRow(raw) {
|
|
2181
|
+
if (raw === null || typeof raw !== "object") {
|
|
2182
|
+
return {
|
|
2183
|
+
date: "",
|
|
2184
|
+
executionCount: 0,
|
|
2185
|
+
pricedCount: 0,
|
|
2186
|
+
totalTokensIn: 0,
|
|
2187
|
+
totalTokensOut: 0,
|
|
2188
|
+
totalCost: 0,
|
|
2189
|
+
avgCostPerExecution: 0,
|
|
2190
|
+
status: "complete"
|
|
2191
|
+
};
|
|
2192
|
+
}
|
|
2193
|
+
const dateValue = Reflect.get(raw, "date");
|
|
2194
|
+
const providerValue = Reflect.get(raw, "provider");
|
|
2195
|
+
const modelValue = Reflect.get(raw, "model");
|
|
2196
|
+
const avgValue = Reflect.get(raw, "avg_cost_per_execution");
|
|
2197
|
+
const execCount = Number(Reflect.get(raw, "execution_count")) || 0;
|
|
2198
|
+
const totalCost = Number(Reflect.get(raw, "total_cost_usd")) || 0;
|
|
2199
|
+
const avgFromRow = avgValue === null || avgValue === void 0 ? null : Number(avgValue);
|
|
2200
|
+
const avgCostPerExecution = avgFromRow !== null && !Number.isNaN(avgFromRow) ? avgFromRow : execCount > 0 ? totalCost / execCount : 0;
|
|
2201
|
+
const provider = typeof providerValue === "string" ? providerValue : void 0;
|
|
2202
|
+
const model = typeof modelValue === "string" ? modelValue : void 0;
|
|
2203
|
+
return {
|
|
2204
|
+
date: typeof dateValue === "string" ? dateValue : String(dateValue ?? ""),
|
|
2205
|
+
...provider !== void 0 ? { provider } : {},
|
|
2206
|
+
...model !== void 0 ? { model } : {},
|
|
2207
|
+
executionCount: execCount,
|
|
2208
|
+
pricedCount: execCount,
|
|
2209
|
+
totalTokensIn: Number(Reflect.get(raw, "total_input_tokens")) || 0,
|
|
2210
|
+
totalTokensOut: Number(Reflect.get(raw, "total_output_tokens")) || 0,
|
|
2211
|
+
totalCost,
|
|
2212
|
+
avgCostPerExecution,
|
|
2213
|
+
status: "complete"
|
|
2214
|
+
};
|
|
2215
|
+
}
|
|
2216
|
+
/**
|
|
2217
|
+
* Determine if telemetry contains non-metered billing records
|
|
2218
|
+
*/
|
|
2219
|
+
hasNonMeteredBilling() {
|
|
2220
|
+
try {
|
|
2221
|
+
const stmt = this.db.prepare(`
|
|
2222
|
+
SELECT 1 FROM enriched_invocations
|
|
2223
|
+
WHERE billing_mode IS NOT NULL AND billing_mode != 'metered'
|
|
2224
|
+
LIMIT 1
|
|
2225
|
+
`);
|
|
2226
|
+
const row = stmt.get();
|
|
2227
|
+
return Boolean(row);
|
|
2228
|
+
} catch (error) {
|
|
2229
|
+
emitWarning(
|
|
2230
|
+
"WS-003",
|
|
2231
|
+
{
|
|
2232
|
+
taskId: "system",
|
|
2233
|
+
subsystem: "telemetry",
|
|
2234
|
+
executionPhase: "execution"
|
|
2235
|
+
},
|
|
2236
|
+
{
|
|
2237
|
+
severity: "WARN" /* WARN */,
|
|
2238
|
+
metadata: {
|
|
2239
|
+
operation: "query_non_metered_billing",
|
|
2240
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
);
|
|
2244
|
+
return false;
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
// ============================================================================
|
|
2248
|
+
// HYBRID STORAGE: Cost Tracking with Tokens + Snapshot
|
|
2249
|
+
// ============================================================================
|
|
2250
|
+
/**
|
|
2251
|
+
* Record an execution cost with hybrid storage (tokens + cost snapshot)
|
|
2252
|
+
* Stores both raw usage data (immutable) and cost snapshot (historical accuracy)
|
|
2253
|
+
*/
|
|
2254
|
+
recordExecutionCost(record) {
|
|
2255
|
+
const stmt = this.db.prepare(`
|
|
2256
|
+
INSERT OR REPLACE INTO enriched_invocations (
|
|
2257
|
+
id,
|
|
2258
|
+
invocation_id,
|
|
2259
|
+
computed_cost_cents,
|
|
2260
|
+
pricing_source,
|
|
2261
|
+
enriched_at,
|
|
2262
|
+
billing_mode,
|
|
2263
|
+
usage_data,
|
|
2264
|
+
cost_snapshot,
|
|
2265
|
+
pricing_version,
|
|
2266
|
+
task_id,
|
|
2267
|
+
competition_id,
|
|
2268
|
+
variant_id
|
|
2269
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2270
|
+
`);
|
|
2271
|
+
const enrichedId = `enrich_${Date.now()}_${randomBytes(8).toString("hex")}`;
|
|
2272
|
+
const enrichedAt = record.timestamp.getTime();
|
|
2273
|
+
stmt.run(
|
|
2274
|
+
enrichedId,
|
|
2275
|
+
record.executionId,
|
|
2276
|
+
record.costSnapshot.totalCostCents,
|
|
2277
|
+
record.pricingSource,
|
|
2278
|
+
enrichedAt,
|
|
2279
|
+
"metered",
|
|
2280
|
+
// Default to metered for new records
|
|
2281
|
+
JSON.stringify(record.usage),
|
|
2282
|
+
JSON.stringify(record.costSnapshot),
|
|
2283
|
+
record.pricingVersion,
|
|
2284
|
+
record.taskId ?? null,
|
|
2285
|
+
record.competitionId ?? null,
|
|
2286
|
+
record.variantId ?? null
|
|
2287
|
+
);
|
|
2288
|
+
}
|
|
2289
|
+
/**
|
|
2290
|
+
* Get execution cost record by execution ID
|
|
2291
|
+
* Reconstructs full ExecutionCostRecord from hybrid storage
|
|
2292
|
+
*/
|
|
2293
|
+
getExecutionCost(executionId) {
|
|
2294
|
+
const stmt = this.db.prepare(`
|
|
2295
|
+
SELECT
|
|
2296
|
+
e.invocation_id,
|
|
2297
|
+
i.timestamp,
|
|
2298
|
+
i.provider_id,
|
|
2299
|
+
i.model_id,
|
|
2300
|
+
e.usage_data,
|
|
2301
|
+
e.cost_snapshot,
|
|
2302
|
+
e.computed_cost_cents,
|
|
2303
|
+
e.pricing_version,
|
|
2304
|
+
e.pricing_source,
|
|
2305
|
+
e.task_id,
|
|
2306
|
+
e.competition_id,
|
|
2307
|
+
i.agent_role,
|
|
2308
|
+
e.variant_id
|
|
2309
|
+
FROM enriched_invocations e
|
|
2310
|
+
JOIN model_invocations i ON e.invocation_id = i.id
|
|
2311
|
+
WHERE e.invocation_id = ?
|
|
2312
|
+
`);
|
|
2313
|
+
const row = stmt.get(executionId);
|
|
2314
|
+
if (!row) {
|
|
2315
|
+
return null;
|
|
2316
|
+
}
|
|
2317
|
+
const usage = row.usage_data ? parseJsonWithContext(row.usage_data, "Execution usage data") : null;
|
|
2318
|
+
const costSnapshot = row.cost_snapshot ? parseJsonWithContext(row.cost_snapshot, "Cost snapshot") : null;
|
|
2319
|
+
const resolvedCostSnapshot = resolveSnapshotFirstCostSnapshot({
|
|
2320
|
+
costSnapshot,
|
|
2321
|
+
computedCostCents: row.computed_cost_cents
|
|
2322
|
+
});
|
|
2323
|
+
if (!usage || !resolvedCostSnapshot) {
|
|
2324
|
+
return null;
|
|
2325
|
+
}
|
|
2326
|
+
return {
|
|
2327
|
+
executionId: row.invocation_id,
|
|
2328
|
+
timestamp: new Date(row.timestamp),
|
|
2329
|
+
provider: row.provider_id,
|
|
2330
|
+
model: row.model_id,
|
|
2331
|
+
usage,
|
|
2332
|
+
pricingVersion: row.pricing_version || "unknown",
|
|
2333
|
+
pricingSource: row.pricing_source || "xml",
|
|
2334
|
+
costSnapshot: resolvedCostSnapshot,
|
|
2335
|
+
computedCostCents: row.computed_cost_cents ?? void 0,
|
|
2336
|
+
taskId: row.task_id ?? void 0,
|
|
2337
|
+
competitionId: row.competition_id ?? void 0,
|
|
2338
|
+
agentRole: row.agent_role ?? void 0,
|
|
2339
|
+
variantId: row.variant_id ?? void 0
|
|
2340
|
+
};
|
|
2341
|
+
}
|
|
2342
|
+
/**
|
|
2343
|
+
* Recalculate cost for an execution using current pricing
|
|
2344
|
+
* Requires calculator function to be passed in to avoid circular dependency
|
|
2345
|
+
*
|
|
2346
|
+
* @param executionId - Execution ID to recalculate
|
|
2347
|
+
* @param calculateCost - Cost calculation function (provider, model, usage) => cost in cents
|
|
2348
|
+
* @returns New cost in cents, or null if execution not found or missing usage data
|
|
2349
|
+
*/
|
|
2350
|
+
recalculateCost(executionId, calculateCost) {
|
|
2351
|
+
const record = this.getExecutionCost(executionId);
|
|
2352
|
+
if (!record) {
|
|
2353
|
+
return null;
|
|
2354
|
+
}
|
|
2355
|
+
try {
|
|
2356
|
+
const newCostCents = calculateCost(record.provider, record.model, record.usage);
|
|
2357
|
+
return newCostCents;
|
|
2358
|
+
} catch (error) {
|
|
2359
|
+
emitWarning(
|
|
2360
|
+
"WS-003",
|
|
2361
|
+
{
|
|
2362
|
+
taskId: "system",
|
|
2363
|
+
subsystem: "telemetry",
|
|
2364
|
+
executionPhase: "execution"
|
|
2365
|
+
},
|
|
2366
|
+
{
|
|
2367
|
+
severity: "WARN" /* WARN */,
|
|
2368
|
+
metadata: {
|
|
2369
|
+
operation: "recalculate_cost",
|
|
2370
|
+
executionId,
|
|
2371
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
);
|
|
2375
|
+
return null;
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
/**
|
|
2379
|
+
* Get all execution costs for a task
|
|
2380
|
+
*/
|
|
2381
|
+
getTaskExecutionCosts(taskId) {
|
|
2382
|
+
const stmt = this.db.prepare(`
|
|
2383
|
+
SELECT
|
|
2384
|
+
e.invocation_id,
|
|
2385
|
+
i.timestamp,
|
|
2386
|
+
i.provider_id,
|
|
2387
|
+
i.model_id,
|
|
2388
|
+
e.usage_data,
|
|
2389
|
+
e.cost_snapshot,
|
|
2390
|
+
e.computed_cost_cents,
|
|
2391
|
+
e.pricing_version,
|
|
2392
|
+
e.pricing_source,
|
|
2393
|
+
e.task_id,
|
|
2394
|
+
e.competition_id,
|
|
2395
|
+
i.agent_role,
|
|
2396
|
+
e.variant_id
|
|
2397
|
+
FROM enriched_invocations e
|
|
2398
|
+
JOIN model_invocations i ON e.invocation_id = i.id
|
|
2399
|
+
WHERE e.task_id = ?
|
|
2400
|
+
ORDER BY i.timestamp ASC
|
|
2401
|
+
`);
|
|
2402
|
+
const rows = stmt.all(taskId);
|
|
2403
|
+
const mappedRecords = rows.map((row) => {
|
|
2404
|
+
const usage = row.usage_data ? parseJsonWithContext(row.usage_data, "Execution usage data") : null;
|
|
2405
|
+
const costSnapshot = row.cost_snapshot ? parseJsonWithContext(row.cost_snapshot, "Cost snapshot") : null;
|
|
2406
|
+
const resolvedCostSnapshot = resolveSnapshotFirstCostSnapshot({
|
|
2407
|
+
costSnapshot,
|
|
2408
|
+
computedCostCents: row.computed_cost_cents
|
|
2409
|
+
});
|
|
2410
|
+
if (!usage || !resolvedCostSnapshot) {
|
|
2411
|
+
return null;
|
|
2412
|
+
}
|
|
2413
|
+
return {
|
|
2414
|
+
executionId: row.invocation_id,
|
|
2415
|
+
timestamp: new Date(row.timestamp),
|
|
2416
|
+
provider: row.provider_id,
|
|
2417
|
+
model: row.model_id,
|
|
2418
|
+
usage,
|
|
2419
|
+
pricingVersion: row.pricing_version || "unknown",
|
|
2420
|
+
pricingSource: row.pricing_source || "xml",
|
|
2421
|
+
costSnapshot: resolvedCostSnapshot,
|
|
2422
|
+
computedCostCents: row.computed_cost_cents ?? void 0,
|
|
2423
|
+
taskId: row.task_id ?? void 0,
|
|
2424
|
+
competitionId: row.competition_id ?? void 0,
|
|
2425
|
+
agentRole: row.agent_role ?? void 0,
|
|
2426
|
+
variantId: row.variant_id ?? void 0
|
|
2427
|
+
};
|
|
2428
|
+
});
|
|
2429
|
+
return mappedRecords.filter((record) => record !== null);
|
|
2430
|
+
}
|
|
2431
|
+
/**
|
|
2432
|
+
* Get all execution costs for a competition
|
|
2433
|
+
*/
|
|
2434
|
+
getCompetitionExecutionCosts(competitionId) {
|
|
2435
|
+
const stmt = this.db.prepare(`
|
|
2436
|
+
SELECT
|
|
2437
|
+
e.invocation_id,
|
|
2438
|
+
i.timestamp,
|
|
2439
|
+
i.provider_id,
|
|
2440
|
+
i.model_id,
|
|
2441
|
+
e.usage_data,
|
|
2442
|
+
e.cost_snapshot,
|
|
2443
|
+
e.computed_cost_cents,
|
|
2444
|
+
e.pricing_version,
|
|
2445
|
+
e.pricing_source,
|
|
2446
|
+
e.task_id,
|
|
2447
|
+
e.competition_id,
|
|
2448
|
+
i.agent_role,
|
|
2449
|
+
e.variant_id
|
|
2450
|
+
FROM enriched_invocations e
|
|
2451
|
+
JOIN model_invocations i ON e.invocation_id = i.id
|
|
2452
|
+
WHERE e.competition_id = ?
|
|
2453
|
+
ORDER BY i.timestamp ASC
|
|
2454
|
+
`);
|
|
2455
|
+
const rows = stmt.all(competitionId);
|
|
2456
|
+
const mappedRecords = rows.map((row) => {
|
|
2457
|
+
const usage = row.usage_data ? parseJsonWithContext(row.usage_data, "Execution usage data") : null;
|
|
2458
|
+
const costSnapshot = row.cost_snapshot ? parseJsonWithContext(row.cost_snapshot, "Cost snapshot") : null;
|
|
2459
|
+
const resolvedCostSnapshot = resolveSnapshotFirstCostSnapshot({
|
|
2460
|
+
costSnapshot,
|
|
2461
|
+
computedCostCents: row.computed_cost_cents
|
|
2462
|
+
});
|
|
2463
|
+
if (!usage || !resolvedCostSnapshot) {
|
|
2464
|
+
return null;
|
|
2465
|
+
}
|
|
2466
|
+
return {
|
|
2467
|
+
executionId: row.invocation_id,
|
|
2468
|
+
timestamp: new Date(row.timestamp),
|
|
2469
|
+
provider: row.provider_id,
|
|
2470
|
+
model: row.model_id,
|
|
2471
|
+
usage,
|
|
2472
|
+
pricingVersion: row.pricing_version || "unknown",
|
|
2473
|
+
pricingSource: row.pricing_source || "xml",
|
|
2474
|
+
costSnapshot: resolvedCostSnapshot,
|
|
2475
|
+
computedCostCents: row.computed_cost_cents ?? void 0,
|
|
2476
|
+
taskId: row.task_id ?? void 0,
|
|
2477
|
+
competitionId: row.competition_id ?? void 0,
|
|
2478
|
+
agentRole: row.agent_role ?? void 0,
|
|
2479
|
+
variantId: row.variant_id ?? void 0
|
|
2480
|
+
};
|
|
2481
|
+
});
|
|
2482
|
+
return mappedRecords.filter((record) => record !== null);
|
|
2483
|
+
}
|
|
2484
|
+
/**
|
|
2485
|
+
* Count invocations that have not yet been enriched with cost data.
|
|
2486
|
+
*/
|
|
2487
|
+
countUnenrichedInvocations() {
|
|
2488
|
+
const stmt = this.db.prepare(`
|
|
2489
|
+
SELECT COUNT(*) as count
|
|
2490
|
+
FROM model_invocations i
|
|
2491
|
+
LEFT JOIN enriched_invocations e ON i.id = e.invocation_id
|
|
2492
|
+
WHERE e.id IS NULL
|
|
2493
|
+
`);
|
|
2494
|
+
const row = stmt.get();
|
|
2495
|
+
return row?.count ?? 0;
|
|
2496
|
+
}
|
|
2497
|
+
/**
|
|
2498
|
+
* Count enriched invocations since a given timestamp.
|
|
2499
|
+
*/
|
|
2500
|
+
countEnrichedInvocationsSince(startTimestampMs) {
|
|
2501
|
+
const stmt = this.db.prepare(`
|
|
2502
|
+
SELECT COUNT(*) as count
|
|
2503
|
+
FROM enriched_invocations e
|
|
2504
|
+
JOIN model_invocations i ON e.invocation_id = i.id
|
|
2505
|
+
WHERE i.timestamp >= ?
|
|
2506
|
+
`);
|
|
2507
|
+
const row = stmt.get(startTimestampMs);
|
|
2508
|
+
return row?.count ?? 0;
|
|
2509
|
+
}
|
|
2510
|
+
/**
|
|
2511
|
+
* Whether any invocation in the window reported non-zero token usage.
|
|
2512
|
+
*/
|
|
2513
|
+
hasTokenUsageSince(startTimestampMs) {
|
|
2514
|
+
const stmt = this.db.prepare(`
|
|
2515
|
+
SELECT 1
|
|
2516
|
+
FROM model_invocations
|
|
2517
|
+
WHERE timestamp >= ?
|
|
2518
|
+
AND (
|
|
2519
|
+
COALESCE(tokens_in, 0) > 0
|
|
2520
|
+
OR COALESCE(tokens_out, 0) > 0
|
|
2521
|
+
)
|
|
2522
|
+
LIMIT 1
|
|
2523
|
+
`);
|
|
2524
|
+
return Boolean(stmt.get(startTimestampMs));
|
|
2525
|
+
}
|
|
2526
|
+
/**
|
|
2527
|
+
* Count raw model invocations since a given timestamp (before cost enrichment).
|
|
2528
|
+
*/
|
|
2529
|
+
countRawInvocationsSince(startTimestampMs) {
|
|
2530
|
+
const stmt = this.db.prepare(`
|
|
2531
|
+
SELECT COUNT(*) as count
|
|
2532
|
+
FROM model_invocations
|
|
2533
|
+
WHERE timestamp >= ?
|
|
2534
|
+
`);
|
|
2535
|
+
const row = stmt.get(startTimestampMs);
|
|
2536
|
+
return row?.count ?? 0;
|
|
2537
|
+
}
|
|
2538
|
+
/**
|
|
2539
|
+
* Get execution costs since a given timestamp
|
|
2540
|
+
*/
|
|
2541
|
+
getExecutionCostsSince(startTimestampMs) {
|
|
2542
|
+
const stmt = this.db.prepare(`
|
|
2543
|
+
SELECT
|
|
2544
|
+
e.invocation_id,
|
|
2545
|
+
i.timestamp,
|
|
2546
|
+
i.provider_id,
|
|
2547
|
+
i.model_id,
|
|
2548
|
+
i.tokens_in,
|
|
2549
|
+
i.tokens_out,
|
|
2550
|
+
e.usage_data,
|
|
2551
|
+
e.cost_snapshot,
|
|
2552
|
+
e.computed_cost_cents,
|
|
2553
|
+
e.pricing_version,
|
|
2554
|
+
e.pricing_source,
|
|
2555
|
+
e.task_id,
|
|
2556
|
+
e.competition_id,
|
|
2557
|
+
i.agent_role,
|
|
2558
|
+
e.variant_id
|
|
2559
|
+
FROM enriched_invocations e
|
|
2560
|
+
JOIN model_invocations i ON e.invocation_id = i.id
|
|
2561
|
+
WHERE i.timestamp >= ?
|
|
2562
|
+
ORDER BY i.timestamp ASC
|
|
2563
|
+
`);
|
|
2564
|
+
const rows = stmt.all(startTimestampMs);
|
|
2565
|
+
const mappedRecords = rows.map((row) => {
|
|
2566
|
+
const usage = row.usage_data ? parseJsonWithContext(row.usage_data, "Execution usage data") : {
|
|
2567
|
+
inputTokens: row.tokens_in ?? 0,
|
|
2568
|
+
outputTokens: row.tokens_out ?? 0
|
|
2569
|
+
};
|
|
2570
|
+
const costSnapshot = row.cost_snapshot ? parseJsonWithContext(row.cost_snapshot, "Cost snapshot") : null;
|
|
2571
|
+
const resolvedCostSnapshot = resolveSnapshotFirstCostSnapshot({
|
|
2572
|
+
costSnapshot,
|
|
2573
|
+
computedCostCents: row.computed_cost_cents
|
|
2574
|
+
});
|
|
2575
|
+
if (!resolvedCostSnapshot) {
|
|
2576
|
+
return null;
|
|
2577
|
+
}
|
|
2578
|
+
return {
|
|
2579
|
+
executionId: row.invocation_id,
|
|
2580
|
+
timestamp: new Date(row.timestamp),
|
|
2581
|
+
provider: row.provider_id,
|
|
2582
|
+
model: row.model_id,
|
|
2583
|
+
usage,
|
|
2584
|
+
pricingVersion: row.pricing_version || "unknown",
|
|
2585
|
+
pricingSource: row.pricing_source || "xml",
|
|
2586
|
+
costSnapshot: resolvedCostSnapshot,
|
|
2587
|
+
computedCostCents: row.computed_cost_cents ?? void 0,
|
|
2588
|
+
taskId: row.task_id ?? void 0,
|
|
2589
|
+
competitionId: row.competition_id ?? void 0,
|
|
2590
|
+
agentRole: row.agent_role ?? void 0,
|
|
2591
|
+
variantId: row.variant_id ?? void 0
|
|
2592
|
+
};
|
|
2593
|
+
});
|
|
2594
|
+
return mappedRecords.filter((record) => record !== null);
|
|
2595
|
+
}
|
|
2596
|
+
/**
|
|
2597
|
+
* Get total cost for a task from cost snapshots.
|
|
2598
|
+
*
|
|
2599
|
+
* Returns a status-bearing amount in cents (as stored on enriched rows).
|
|
2600
|
+
* Known zero is valid only when priced rows exist and sum to 0.
|
|
2601
|
+
* Empty match set, all-null evidence, and query failures do not collapse to bare 0.
|
|
2602
|
+
*/
|
|
2603
|
+
getTaskTotalCost(taskId) {
|
|
2604
|
+
try {
|
|
2605
|
+
const stmt = this.db.prepare(`
|
|
2606
|
+
SELECT
|
|
2607
|
+
${SNAPSHOT_FIRST_COST_CENTS_SQL} AS cost_cents
|
|
2608
|
+
FROM enriched_invocations e
|
|
2609
|
+
WHERE e.task_id = ?
|
|
2610
|
+
`);
|
|
2611
|
+
const rows = stmt.all(taskId);
|
|
2612
|
+
if (rows.length === 0) {
|
|
2613
|
+
return unavailableCostAmount(
|
|
2614
|
+
"cents",
|
|
2615
|
+
ENRICHED_INVOCATIONS_SOURCE,
|
|
2616
|
+
`no enriched cost rows for task=${taskId}`
|
|
2617
|
+
);
|
|
2618
|
+
}
|
|
2619
|
+
let totalCents = 0;
|
|
2620
|
+
let pricedCount = 0;
|
|
2621
|
+
for (const row of rows) {
|
|
2622
|
+
if (row.cost_cents === null || row.cost_cents === void 0) {
|
|
2623
|
+
continue;
|
|
2624
|
+
}
|
|
2625
|
+
if (!Number.isFinite(row.cost_cents)) {
|
|
2626
|
+
return unavailableCostAmount(
|
|
2627
|
+
"cents",
|
|
2628
|
+
ENRICHED_INVOCATIONS_SOURCE,
|
|
2629
|
+
`non-finite cost evidence for task=${taskId}`
|
|
2630
|
+
);
|
|
2631
|
+
}
|
|
2632
|
+
pricedCount += 1;
|
|
2633
|
+
totalCents += row.cost_cents;
|
|
2634
|
+
}
|
|
2635
|
+
if (pricedCount === 0) {
|
|
2636
|
+
return unavailableCostAmount(
|
|
2637
|
+
"cents",
|
|
2638
|
+
ENRICHED_INVOCATIONS_SOURCE,
|
|
2639
|
+
`null or missing cost evidence for task=${taskId}`
|
|
2640
|
+
);
|
|
2641
|
+
}
|
|
2642
|
+
if (pricedCount < rows.length) {
|
|
2643
|
+
return unavailableCostAmount(
|
|
2644
|
+
"cents",
|
|
2645
|
+
ENRICHED_INVOCATIONS_SOURCE,
|
|
2646
|
+
`partial cost evidence for task=${taskId} (${pricedCount}/${rows.length} priced)`
|
|
2647
|
+
);
|
|
2648
|
+
}
|
|
2649
|
+
return availableCostAmount(totalCents, "cents", ENRICHED_INVOCATIONS_SOURCE);
|
|
2650
|
+
} catch (error) {
|
|
2651
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2652
|
+
return errorCostAmount(
|
|
2653
|
+
"cents",
|
|
2654
|
+
ENRICHED_INVOCATIONS_SOURCE,
|
|
2655
|
+
message,
|
|
2656
|
+
`query failure for task=${taskId}`
|
|
2657
|
+
);
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
/**
|
|
2661
|
+
* Get cost breakdown by provider for a competition.
|
|
2662
|
+
*
|
|
2663
|
+
* Status-bearing (C11): empty match set, null/partial evidence, and query
|
|
2664
|
+
* failures never collapse to an empty map or bare numeric zero. Known zero is
|
|
2665
|
+
* returned only when priced rows exist and sum to 0. Zero-valued providers are
|
|
2666
|
+
* retained rather than omitted.
|
|
2667
|
+
*/
|
|
2668
|
+
getCompetitionCostBreakdown(competitionId) {
|
|
2669
|
+
try {
|
|
2670
|
+
const stmt = this.db.prepare(`
|
|
2671
|
+
SELECT
|
|
2672
|
+
i.provider_id,
|
|
2673
|
+
SUM(${SNAPSHOT_FIRST_COST_CENTS_SQL}) as total_cost_cents,
|
|
2674
|
+
COUNT(*) as row_count,
|
|
2675
|
+
SUM(CASE WHEN ${SNAPSHOT_FIRST_COST_CENTS_SQL} IS NOT NULL THEN 1 ELSE 0 END) as priced_count
|
|
2676
|
+
FROM enriched_invocations e
|
|
2677
|
+
JOIN model_invocations i ON e.invocation_id = i.id
|
|
2678
|
+
WHERE e.competition_id = ?
|
|
2679
|
+
GROUP BY i.provider_id
|
|
2680
|
+
`);
|
|
2681
|
+
const rows = stmt.all(competitionId);
|
|
2682
|
+
if (rows.length === 0) {
|
|
2683
|
+
const total2 = unavailableCostAmount(
|
|
2684
|
+
"cents",
|
|
2685
|
+
ENRICHED_INVOCATIONS_SOURCE,
|
|
2686
|
+
`no enriched cost rows for competition=${competitionId}`
|
|
2687
|
+
);
|
|
2688
|
+
return {
|
|
2689
|
+
status: "unavailable",
|
|
2690
|
+
byProvider: {},
|
|
2691
|
+
total: total2,
|
|
2692
|
+
source: ENRICHED_INVOCATIONS_SOURCE,
|
|
2693
|
+
detail: total2.detail
|
|
2694
|
+
};
|
|
2695
|
+
}
|
|
2696
|
+
const byProvider = {};
|
|
2697
|
+
let allAvailable = true;
|
|
2698
|
+
let totalCents = 0;
|
|
2699
|
+
const partialDetails = [];
|
|
2700
|
+
for (const row of rows) {
|
|
2701
|
+
const rowCount = row.row_count ?? 0;
|
|
2702
|
+
const pricedCount = row.priced_count ?? 0;
|
|
2703
|
+
const totalCostCents = row.total_cost_cents;
|
|
2704
|
+
if (rowCount === 0) {
|
|
2705
|
+
byProvider[row.provider_id] = unavailableCostAmount(
|
|
2706
|
+
"cents",
|
|
2707
|
+
ENRICHED_INVOCATIONS_SOURCE,
|
|
2708
|
+
`no rows for provider=${row.provider_id} competition=${competitionId}`
|
|
2709
|
+
);
|
|
2710
|
+
allAvailable = false;
|
|
2711
|
+
continue;
|
|
2712
|
+
}
|
|
2713
|
+
if (pricedCount === 0 || totalCostCents === null || totalCostCents === void 0) {
|
|
2714
|
+
byProvider[row.provider_id] = unavailableCostAmount(
|
|
2715
|
+
"cents",
|
|
2716
|
+
ENRICHED_INVOCATIONS_SOURCE,
|
|
2717
|
+
`null or missing cost evidence for provider=${row.provider_id} competition=${competitionId}`
|
|
2718
|
+
);
|
|
2719
|
+
allAvailable = false;
|
|
2720
|
+
partialDetails.push(`${row.provider_id}:null`);
|
|
2721
|
+
continue;
|
|
2722
|
+
}
|
|
2723
|
+
if (pricedCount < rowCount) {
|
|
2724
|
+
byProvider[row.provider_id] = unavailableCostAmount(
|
|
2725
|
+
"cents",
|
|
2726
|
+
ENRICHED_INVOCATIONS_SOURCE,
|
|
2727
|
+
`partial cost evidence for provider=${row.provider_id} competition=${competitionId} (${pricedCount}/${rowCount} priced)`
|
|
2728
|
+
);
|
|
2729
|
+
allAvailable = false;
|
|
2730
|
+
partialDetails.push(`${row.provider_id}:${pricedCount}/${rowCount}`);
|
|
2731
|
+
continue;
|
|
2732
|
+
}
|
|
2733
|
+
byProvider[row.provider_id] = availableCostAmount(
|
|
2734
|
+
totalCostCents,
|
|
2735
|
+
"cents",
|
|
2736
|
+
ENRICHED_INVOCATIONS_SOURCE
|
|
2737
|
+
);
|
|
2738
|
+
totalCents += totalCostCents;
|
|
2739
|
+
}
|
|
2740
|
+
if (!allAvailable) {
|
|
2741
|
+
const detail = partialDetails.length > 0 ? `partial or null cost evidence for competition=${competitionId} (${partialDetails.join(", ")})` : `incomplete cost evidence for competition=${competitionId}`;
|
|
2742
|
+
const total2 = unavailableCostAmount("cents", ENRICHED_INVOCATIONS_SOURCE, detail);
|
|
2743
|
+
return {
|
|
2744
|
+
status: "unavailable",
|
|
2745
|
+
byProvider,
|
|
2746
|
+
total: total2,
|
|
2747
|
+
source: ENRICHED_INVOCATIONS_SOURCE,
|
|
2748
|
+
detail
|
|
2749
|
+
};
|
|
2750
|
+
}
|
|
2751
|
+
const total = availableCostAmount(totalCents, "cents", ENRICHED_INVOCATIONS_SOURCE);
|
|
2752
|
+
return {
|
|
2753
|
+
status: "available",
|
|
2754
|
+
byProvider,
|
|
2755
|
+
total,
|
|
2756
|
+
source: ENRICHED_INVOCATIONS_SOURCE
|
|
2757
|
+
};
|
|
2758
|
+
} catch (error) {
|
|
2759
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2760
|
+
const total = errorCostAmount(
|
|
2761
|
+
"cents",
|
|
2762
|
+
ENRICHED_INVOCATIONS_SOURCE,
|
|
2763
|
+
message,
|
|
2764
|
+
`query failure for competition=${competitionId}`
|
|
2765
|
+
);
|
|
2766
|
+
return {
|
|
2767
|
+
status: "error",
|
|
2768
|
+
byProvider: {},
|
|
2769
|
+
total,
|
|
2770
|
+
source: ENRICHED_INVOCATIONS_SOURCE,
|
|
2771
|
+
error: message,
|
|
2772
|
+
detail: total.detail
|
|
2773
|
+
};
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
};
|
|
2777
|
+
function generateInvocationId() {
|
|
2778
|
+
return `inv_${Date.now()}_${randomBytes(8).toString("hex")}`;
|
|
2779
|
+
}
|
|
2780
|
+
function resolveInvocationsDbPath(poeticDir) {
|
|
2781
|
+
return path3.join(poeticDir, "telemetry", "invocations.db");
|
|
2782
|
+
}
|
|
2783
|
+
|
|
2784
|
+
export {
|
|
2785
|
+
loadProviderDirectory,
|
|
2786
|
+
buildRegistry,
|
|
2787
|
+
saveRegistry,
|
|
2788
|
+
SNAPSHOT_FIRST_COST_CENTS_SQL,
|
|
2789
|
+
ensurePendingInvocationCostEnrichment,
|
|
2790
|
+
summarizeInvocationCostVisibility,
|
|
2791
|
+
resolveInvocationRegistryVersion,
|
|
2792
|
+
availableCostAmount,
|
|
2793
|
+
unavailableCostAmount,
|
|
2794
|
+
errorCostAmount,
|
|
2795
|
+
isAvailableCostAmount,
|
|
2796
|
+
costAmountOrNull,
|
|
2797
|
+
projectCostVisibility,
|
|
2798
|
+
fromVisibilitySummary,
|
|
2799
|
+
formatCostProjectionNotice,
|
|
2800
|
+
invocationAnalyticsQualityClauses,
|
|
2801
|
+
invocationProvenanceWhereSql,
|
|
2802
|
+
isInvocationMetricQualityRow,
|
|
2803
|
+
resolveCompatibleInvocationModelId,
|
|
2804
|
+
invocationModelMatchesSameModel,
|
|
2805
|
+
ModelInvocationsDB,
|
|
2806
|
+
generateInvocationId,
|
|
2807
|
+
resolveInvocationsDbPath
|
|
2808
|
+
};
|
|
2809
|
+
//# sourceMappingURL=chunk-GKN3HMN4.js.map
|