@newrelic/preflight 0.0.1-pre.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (578) hide show
  1. package/LICENSE +183 -0
  2. package/README.md +498 -0
  3. package/dist/alerts/alert-log.d.ts +24 -0
  4. package/dist/alerts/alert-log.d.ts.map +1 -0
  5. package/dist/alerts/alert-log.js +159 -0
  6. package/dist/alerts/alert-log.js.map +1 -0
  7. package/dist/alerts/alert-snapshot-collector.d.ts +168 -0
  8. package/dist/alerts/alert-snapshot-collector.d.ts.map +1 -0
  9. package/dist/alerts/alert-snapshot-collector.js +243 -0
  10. package/dist/alerts/alert-snapshot-collector.js.map +1 -0
  11. package/dist/alerts/local-alert-engine.d.ts +86 -0
  12. package/dist/alerts/local-alert-engine.d.ts.map +1 -0
  13. package/dist/alerts/local-alert-engine.js +466 -0
  14. package/dist/alerts/local-alert-engine.js.map +1 -0
  15. package/dist/alerts/local-alert-rule.d.ts +439 -0
  16. package/dist/alerts/local-alert-rule.d.ts.map +1 -0
  17. package/dist/alerts/local-alert-rule.js +139 -0
  18. package/dist/alerts/local-alert-rule.js.map +1 -0
  19. package/dist/alerts/os-notifier.d.ts +39 -0
  20. package/dist/alerts/os-notifier.d.ts.map +1 -0
  21. package/dist/alerts/os-notifier.js +170 -0
  22. package/dist/alerts/os-notifier.js.map +1 -0
  23. package/dist/alerts/types.d.ts +35 -0
  24. package/dist/alerts/types.d.ts.map +1 -0
  25. package/dist/alerts/types.js +8 -0
  26. package/dist/alerts/types.js.map +1 -0
  27. package/dist/config.d.ts +169 -0
  28. package/dist/config.d.ts.map +1 -0
  29. package/dist/config.js +860 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/dashboard/dashboard-server.d.ts +38 -0
  32. package/dist/dashboard/dashboard-server.d.ts.map +1 -0
  33. package/dist/dashboard/dashboard-server.js +207 -0
  34. package/dist/dashboard/dashboard-server.js.map +1 -0
  35. package/dist/dashboard/index.d.ts +3 -0
  36. package/dist/dashboard/index.d.ts.map +1 -0
  37. package/dist/dashboard/index.js +2 -0
  38. package/dist/dashboard/index.js.map +1 -0
  39. package/dist/dashboard/live-event-bus.d.ts +99 -0
  40. package/dist/dashboard/live-event-bus.d.ts.map +1 -0
  41. package/dist/dashboard/live-event-bus.js +56 -0
  42. package/dist/dashboard/live-event-bus.js.map +1 -0
  43. package/dist/dashboard/routes/api-handler.d.ts +122 -0
  44. package/dist/dashboard/routes/api-handler.d.ts.map +1 -0
  45. package/dist/dashboard/routes/api-handler.js +1414 -0
  46. package/dist/dashboard/routes/api-handler.js.map +1 -0
  47. package/dist/dashboard/routes/replay-analyzer.d.ts +15 -0
  48. package/dist/dashboard/routes/replay-analyzer.d.ts.map +1 -0
  49. package/dist/dashboard/routes/replay-analyzer.js +227 -0
  50. package/dist/dashboard/routes/replay-analyzer.js.map +1 -0
  51. package/dist/dashboard/routes/sse-handler.d.ts +4 -0
  52. package/dist/dashboard/routes/sse-handler.d.ts.map +1 -0
  53. package/dist/dashboard/routes/sse-handler.js +122 -0
  54. package/dist/dashboard/routes/sse-handler.js.map +1 -0
  55. package/dist/dashboard/routes/static-handler.d.ts +3 -0
  56. package/dist/dashboard/routes/static-handler.d.ts.map +1 -0
  57. package/dist/dashboard/routes/static-handler.js +103 -0
  58. package/dist/dashboard/routes/static-handler.js.map +1 -0
  59. package/dist/data/alerts/conditions/01-daily-cost-spike.json +16 -0
  60. package/dist/data/alerts/conditions/02-low-efficiency-score.json +16 -0
  61. package/dist/data/alerts/conditions/03-stuck-loop-rate.json +16 -0
  62. package/dist/data/alerts/conditions/04-anti-pattern-rate.json +16 -0
  63. package/dist/data/alerts/conditions/05-session-cost-budget.json +16 -0
  64. package/dist/data/alerts/conditions-personal/01-personal-daily-cost.json +16 -0
  65. package/dist/data/alerts/conditions-personal/02-personal-session-cost.json +16 -0
  66. package/dist/data/alerts/conditions-personal/03-personal-low-efficiency.json +16 -0
  67. package/dist/data/alerts/conditions-personal/04-personal-anti-pattern-rate.json +16 -0
  68. package/dist/data/alerts/conditions-personal/05-personal-stuck-loop.json +16 -0
  69. package/dist/data/alerts/policy.json +4 -0
  70. package/dist/data/dashboards/ai-coding-assistant-manager-view.json +103 -0
  71. package/dist/data/dashboards/ai-coding-assistant-overview.json +239 -0
  72. package/dist/data/dashboards/ai-coding-assistant-personal.json +442 -0
  73. package/dist/data/dashboards/ai-coding-assistant-platform-comparison.json +320 -0
  74. package/dist/data/dashboards/ai-coding-assistant-security.json +275 -0
  75. package/dist/data/dashboards/ai-coding-assistant-session-detail.json +296 -0
  76. package/dist/data/dashboards/ai-coding-assistant-team-view.json +345 -0
  77. package/dist/deploy/data-paths.d.ts +22 -0
  78. package/dist/deploy/data-paths.d.ts.map +1 -0
  79. package/dist/deploy/data-paths.js +69 -0
  80. package/dist/deploy/data-paths.js.map +1 -0
  81. package/dist/deploy/deploy-alerts.d.ts +58 -0
  82. package/dist/deploy/deploy-alerts.d.ts.map +1 -0
  83. package/dist/deploy/deploy-alerts.js +371 -0
  84. package/dist/deploy/deploy-alerts.js.map +1 -0
  85. package/dist/deploy/deploy-dashboards.d.ts +92 -0
  86. package/dist/deploy/deploy-dashboards.d.ts.map +1 -0
  87. package/dist/deploy/deploy-dashboards.js +282 -0
  88. package/dist/deploy/deploy-dashboards.js.map +1 -0
  89. package/dist/digest/digest-formatter.d.ts +3 -0
  90. package/dist/digest/digest-formatter.d.ts.map +1 -0
  91. package/dist/digest/digest-formatter.js +37 -0
  92. package/dist/digest/digest-formatter.js.map +1 -0
  93. package/dist/digest/digest-sender.d.ts +2 -0
  94. package/dist/digest/digest-sender.d.ts.map +1 -0
  95. package/dist/digest/digest-sender.js +29 -0
  96. package/dist/digest/digest-sender.js.map +1 -0
  97. package/dist/hooks/bash-classifier.d.ts +26 -0
  98. package/dist/hooks/bash-classifier.d.ts.map +1 -0
  99. package/dist/hooks/bash-classifier.js +409 -0
  100. package/dist/hooks/bash-classifier.js.map +1 -0
  101. package/dist/hooks/collector-script.d.ts +47 -0
  102. package/dist/hooks/collector-script.d.ts.map +1 -0
  103. package/dist/hooks/collector-script.js +662 -0
  104. package/dist/hooks/collector-script.js.map +1 -0
  105. package/dist/hooks/event-processor.d.ts +65 -0
  106. package/dist/hooks/event-processor.d.ts.map +1 -0
  107. package/dist/hooks/event-processor.js +342 -0
  108. package/dist/hooks/event-processor.js.map +1 -0
  109. package/dist/hooks/index.d.ts +7 -0
  110. package/dist/hooks/index.d.ts.map +1 -0
  111. package/dist/hooks/index.js +5 -0
  112. package/dist/hooks/index.js.map +1 -0
  113. package/dist/hooks/session-resolver.d.ts +66 -0
  114. package/dist/hooks/session-resolver.d.ts.map +1 -0
  115. package/dist/hooks/session-resolver.js +196 -0
  116. package/dist/hooks/session-resolver.js.map +1 -0
  117. package/dist/hooks/tool-parsers.d.ts +19 -0
  118. package/dist/hooks/tool-parsers.d.ts.map +1 -0
  119. package/dist/hooks/tool-parsers.js +260 -0
  120. package/dist/hooks/tool-parsers.js.map +1 -0
  121. package/dist/index.d.ts +107 -0
  122. package/dist/index.d.ts.map +1 -0
  123. package/dist/index.js +1505 -0
  124. package/dist/index.js.map +1 -0
  125. package/dist/install/cli.d.ts +11 -0
  126. package/dist/install/cli.d.ts.map +1 -0
  127. package/dist/install/cli.js +365 -0
  128. package/dist/install/cli.js.map +1 -0
  129. package/dist/install/index.d.ts +4 -0
  130. package/dist/install/index.d.ts.map +1 -0
  131. package/dist/install/index.js +3 -0
  132. package/dist/install/index.js.map +1 -0
  133. package/dist/install/install-helper.d.ts +35 -0
  134. package/dist/install/install-helper.d.ts.map +1 -0
  135. package/dist/install/install-helper.js +227 -0
  136. package/dist/install/install-helper.js.map +1 -0
  137. package/dist/install/key-validator.d.ts +19 -0
  138. package/dist/install/key-validator.d.ts.map +1 -0
  139. package/dist/install/key-validator.js +122 -0
  140. package/dist/install/key-validator.js.map +1 -0
  141. package/dist/install/migrate.d.ts +12 -0
  142. package/dist/install/migrate.d.ts.map +1 -0
  143. package/dist/install/migrate.js +115 -0
  144. package/dist/install/migrate.js.map +1 -0
  145. package/dist/install/schedule.d.ts +11 -0
  146. package/dist/install/schedule.d.ts.map +1 -0
  147. package/dist/install/schedule.js +114 -0
  148. package/dist/install/schedule.js.map +1 -0
  149. package/dist/install/setup-wizard.d.ts +40 -0
  150. package/dist/install/setup-wizard.d.ts.map +1 -0
  151. package/dist/install/setup-wizard.js +489 -0
  152. package/dist/install/setup-wizard.js.map +1 -0
  153. package/dist/lib/date.d.ts +54 -0
  154. package/dist/lib/date.d.ts.map +1 -0
  155. package/dist/lib/date.js +85 -0
  156. package/dist/lib/date.js.map +1 -0
  157. package/dist/metrics/anti-patterns.d.ts +62 -0
  158. package/dist/metrics/anti-patterns.d.ts.map +1 -0
  159. package/dist/metrics/anti-patterns.js +301 -0
  160. package/dist/metrics/anti-patterns.js.map +1 -0
  161. package/dist/metrics/api-failure-tracker.d.ts +82 -0
  162. package/dist/metrics/api-failure-tracker.d.ts.map +1 -0
  163. package/dist/metrics/api-failure-tracker.js +202 -0
  164. package/dist/metrics/api-failure-tracker.js.map +1 -0
  165. package/dist/metrics/budget-tracker.d.ts +60 -0
  166. package/dist/metrics/budget-tracker.d.ts.map +1 -0
  167. package/dist/metrics/budget-tracker.js +130 -0
  168. package/dist/metrics/budget-tracker.js.map +1 -0
  169. package/dist/metrics/claudemd-tracker.d.ts +108 -0
  170. package/dist/metrics/claudemd-tracker.d.ts.map +1 -0
  171. package/dist/metrics/claudemd-tracker.js +337 -0
  172. package/dist/metrics/claudemd-tracker.js.map +1 -0
  173. package/dist/metrics/collaboration-profile.d.ts +65 -0
  174. package/dist/metrics/collaboration-profile.d.ts.map +1 -0
  175. package/dist/metrics/collaboration-profile.js +231 -0
  176. package/dist/metrics/collaboration-profile.js.map +1 -0
  177. package/dist/metrics/context-composition-tracker.d.ts +74 -0
  178. package/dist/metrics/context-composition-tracker.d.ts.map +1 -0
  179. package/dist/metrics/context-composition-tracker.js +202 -0
  180. package/dist/metrics/context-composition-tracker.js.map +1 -0
  181. package/dist/metrics/context-tracker.d.ts +78 -0
  182. package/dist/metrics/context-tracker.d.ts.map +1 -0
  183. package/dist/metrics/context-tracker.js +222 -0
  184. package/dist/metrics/context-tracker.js.map +1 -0
  185. package/dist/metrics/context-window-tracker.d.ts +18 -0
  186. package/dist/metrics/context-window-tracker.d.ts.map +1 -0
  187. package/dist/metrics/context-window-tracker.js +35 -0
  188. package/dist/metrics/context-window-tracker.js.map +1 -0
  189. package/dist/metrics/cost-forecast.d.ts +36 -0
  190. package/dist/metrics/cost-forecast.d.ts.map +1 -0
  191. package/dist/metrics/cost-forecast.js +91 -0
  192. package/dist/metrics/cost-forecast.js.map +1 -0
  193. package/dist/metrics/cost-per-outcome.d.ts +102 -0
  194. package/dist/metrics/cost-per-outcome.d.ts.map +1 -0
  195. package/dist/metrics/cost-per-outcome.js +266 -0
  196. package/dist/metrics/cost-per-outcome.js.map +1 -0
  197. package/dist/metrics/cost-tracker.d.ts +78 -0
  198. package/dist/metrics/cost-tracker.d.ts.map +1 -0
  199. package/dist/metrics/cost-tracker.js +169 -0
  200. package/dist/metrics/cost-tracker.js.map +1 -0
  201. package/dist/metrics/decision-tracker.d.ts +49 -0
  202. package/dist/metrics/decision-tracker.d.ts.map +1 -0
  203. package/dist/metrics/decision-tracker.js +161 -0
  204. package/dist/metrics/decision-tracker.js.map +1 -0
  205. package/dist/metrics/efficiency-score.d.ts +80 -0
  206. package/dist/metrics/efficiency-score.d.ts.map +1 -0
  207. package/dist/metrics/efficiency-score.js +219 -0
  208. package/dist/metrics/efficiency-score.js.map +1 -0
  209. package/dist/metrics/git-efficiency-tracker.d.ts +165 -0
  210. package/dist/metrics/git-efficiency-tracker.d.ts.map +1 -0
  211. package/dist/metrics/git-efficiency-tracker.js +1056 -0
  212. package/dist/metrics/git-efficiency-tracker.js.map +1 -0
  213. package/dist/metrics/index.d.ts +26 -0
  214. package/dist/metrics/index.d.ts.map +1 -0
  215. package/dist/metrics/index.js +14 -0
  216. package/dist/metrics/index.js.map +1 -0
  217. package/dist/metrics/instruction-drift-tracker.d.ts +69 -0
  218. package/dist/metrics/instruction-drift-tracker.d.ts.map +1 -0
  219. package/dist/metrics/instruction-drift-tracker.js +213 -0
  220. package/dist/metrics/instruction-drift-tracker.js.map +1 -0
  221. package/dist/metrics/latency-decomposition.d.ts +50 -0
  222. package/dist/metrics/latency-decomposition.d.ts.map +1 -0
  223. package/dist/metrics/latency-decomposition.js +112 -0
  224. package/dist/metrics/latency-decomposition.js.map +1 -0
  225. package/dist/metrics/latency-tracker.d.ts +33 -0
  226. package/dist/metrics/latency-tracker.d.ts.map +1 -0
  227. package/dist/metrics/latency-tracker.js +93 -0
  228. package/dist/metrics/latency-tracker.js.map +1 -0
  229. package/dist/metrics/live-session-registry.d.ts +29 -0
  230. package/dist/metrics/live-session-registry.d.ts.map +1 -0
  231. package/dist/metrics/live-session-registry.js +103 -0
  232. package/dist/metrics/live-session-registry.js.map +1 -0
  233. package/dist/metrics/model-usage-tracker.d.ts +21 -0
  234. package/dist/metrics/model-usage-tracker.d.ts.map +1 -0
  235. package/dist/metrics/model-usage-tracker.js +53 -0
  236. package/dist/metrics/model-usage-tracker.js.map +1 -0
  237. package/dist/metrics/percentile.d.ts +5 -0
  238. package/dist/metrics/percentile.d.ts.map +1 -0
  239. package/dist/metrics/percentile.js +10 -0
  240. package/dist/metrics/percentile.js.map +1 -0
  241. package/dist/metrics/personal-coach.d.ts +47 -0
  242. package/dist/metrics/personal-coach.d.ts.map +1 -0
  243. package/dist/metrics/personal-coach.js +241 -0
  244. package/dist/metrics/personal-coach.js.map +1 -0
  245. package/dist/metrics/prompt-feedback.d.ts +75 -0
  246. package/dist/metrics/prompt-feedback.d.ts.map +1 -0
  247. package/dist/metrics/prompt-feedback.js +286 -0
  248. package/dist/metrics/prompt-feedback.js.map +1 -0
  249. package/dist/metrics/proxy-metrics.d.ts +54 -0
  250. package/dist/metrics/proxy-metrics.d.ts.map +1 -0
  251. package/dist/metrics/proxy-metrics.js +228 -0
  252. package/dist/metrics/proxy-metrics.js.map +1 -0
  253. package/dist/metrics/quality-proxy-tracker.d.ts +51 -0
  254. package/dist/metrics/quality-proxy-tracker.d.ts.map +1 -0
  255. package/dist/metrics/quality-proxy-tracker.js +162 -0
  256. package/dist/metrics/quality-proxy-tracker.js.map +1 -0
  257. package/dist/metrics/recommendation-engine.d.ts +72 -0
  258. package/dist/metrics/recommendation-engine.d.ts.map +1 -0
  259. package/dist/metrics/recommendation-engine.js +207 -0
  260. package/dist/metrics/recommendation-engine.js.map +1 -0
  261. package/dist/metrics/retry-detector.d.ts +43 -0
  262. package/dist/metrics/retry-detector.d.ts.map +1 -0
  263. package/dist/metrics/retry-detector.js +179 -0
  264. package/dist/metrics/retry-detector.js.map +1 -0
  265. package/dist/metrics/session-tracker.d.ts +75 -0
  266. package/dist/metrics/session-tracker.d.ts.map +1 -0
  267. package/dist/metrics/session-tracker.js +249 -0
  268. package/dist/metrics/session-tracker.js.map +1 -0
  269. package/dist/metrics/task-completion-tracker.d.ts +15 -0
  270. package/dist/metrics/task-completion-tracker.d.ts.map +1 -0
  271. package/dist/metrics/task-completion-tracker.js +27 -0
  272. package/dist/metrics/task-completion-tracker.js.map +1 -0
  273. package/dist/metrics/task-detector.d.ts +84 -0
  274. package/dist/metrics/task-detector.d.ts.map +1 -0
  275. package/dist/metrics/task-detector.js +302 -0
  276. package/dist/metrics/task-detector.js.map +1 -0
  277. package/dist/metrics/tool-selection-scorer.d.ts +39 -0
  278. package/dist/metrics/tool-selection-scorer.d.ts.map +1 -0
  279. package/dist/metrics/tool-selection-scorer.js +193 -0
  280. package/dist/metrics/tool-selection-scorer.js.map +1 -0
  281. package/dist/metrics/trend-analyzer.d.ts +92 -0
  282. package/dist/metrics/trend-analyzer.d.ts.map +1 -0
  283. package/dist/metrics/trend-analyzer.js +293 -0
  284. package/dist/metrics/trend-analyzer.js.map +1 -0
  285. package/dist/metrics/turn-cost-attributor.d.ts +41 -0
  286. package/dist/metrics/turn-cost-attributor.d.ts.map +1 -0
  287. package/dist/metrics/turn-cost-attributor.js +118 -0
  288. package/dist/metrics/turn-cost-attributor.js.map +1 -0
  289. package/dist/metrics/turn-tracker.d.ts +49 -0
  290. package/dist/metrics/turn-tracker.d.ts.map +1 -0
  291. package/dist/metrics/turn-tracker.js +192 -0
  292. package/dist/metrics/turn-tracker.js.map +1 -0
  293. package/dist/platforms/amazon-q-adapter.d.ts +10 -0
  294. package/dist/platforms/amazon-q-adapter.d.ts.map +1 -0
  295. package/dist/platforms/amazon-q-adapter.js +75 -0
  296. package/dist/platforms/amazon-q-adapter.js.map +1 -0
  297. package/dist/platforms/claude-code-adapter.d.ts +10 -0
  298. package/dist/platforms/claude-code-adapter.d.ts.map +1 -0
  299. package/dist/platforms/claude-code-adapter.js +48 -0
  300. package/dist/platforms/claude-code-adapter.js.map +1 -0
  301. package/dist/platforms/continue-adapter.d.ts +10 -0
  302. package/dist/platforms/continue-adapter.d.ts.map +1 -0
  303. package/dist/platforms/continue-adapter.js +73 -0
  304. package/dist/platforms/continue-adapter.js.map +1 -0
  305. package/dist/platforms/copilot-adapter.d.ts +37 -0
  306. package/dist/platforms/copilot-adapter.d.ts.map +1 -0
  307. package/dist/platforms/copilot-adapter.js +66 -0
  308. package/dist/platforms/copilot-adapter.js.map +1 -0
  309. package/dist/platforms/cursor-adapter.d.ts +10 -0
  310. package/dist/platforms/cursor-adapter.d.ts.map +1 -0
  311. package/dist/platforms/cursor-adapter.js +60 -0
  312. package/dist/platforms/cursor-adapter.js.map +1 -0
  313. package/dist/platforms/generic-mcp-adapter.d.ts +113 -0
  314. package/dist/platforms/generic-mcp-adapter.d.ts.map +1 -0
  315. package/dist/platforms/generic-mcp-adapter.js +139 -0
  316. package/dist/platforms/generic-mcp-adapter.js.map +1 -0
  317. package/dist/platforms/index.d.ts +15 -0
  318. package/dist/platforms/index.d.ts.map +1 -0
  319. package/dist/platforms/index.js +12 -0
  320. package/dist/platforms/index.js.map +1 -0
  321. package/dist/platforms/platform-registry.d.ts +11 -0
  322. package/dist/platforms/platform-registry.d.ts.map +1 -0
  323. package/dist/platforms/platform-registry.js +54 -0
  324. package/dist/platforms/platform-registry.js.map +1 -0
  325. package/dist/platforms/types.d.ts +36 -0
  326. package/dist/platforms/types.d.ts.map +1 -0
  327. package/dist/platforms/types.js +2 -0
  328. package/dist/platforms/types.js.map +1 -0
  329. package/dist/platforms/windsurf-adapter.d.ts +10 -0
  330. package/dist/platforms/windsurf-adapter.d.ts.map +1 -0
  331. package/dist/platforms/windsurf-adapter.js +63 -0
  332. package/dist/platforms/windsurf-adapter.js.map +1 -0
  333. package/dist/platforms/zed-adapter.d.ts +10 -0
  334. package/dist/platforms/zed-adapter.d.ts.map +1 -0
  335. package/dist/platforms/zed-adapter.js +72 -0
  336. package/dist/platforms/zed-adapter.js.map +1 -0
  337. package/dist/proxy/index.d.ts +7 -0
  338. package/dist/proxy/index.d.ts.map +1 -0
  339. package/dist/proxy/index.js +5 -0
  340. package/dist/proxy/index.js.map +1 -0
  341. package/dist/proxy/otlp-receiver.d.ts +28 -0
  342. package/dist/proxy/otlp-receiver.d.ts.map +1 -0
  343. package/dist/proxy/otlp-receiver.js +319 -0
  344. package/dist/proxy/otlp-receiver.js.map +1 -0
  345. package/dist/proxy/proxy-manager.d.ts +47 -0
  346. package/dist/proxy/proxy-manager.d.ts.map +1 -0
  347. package/dist/proxy/proxy-manager.js +338 -0
  348. package/dist/proxy/proxy-manager.js.map +1 -0
  349. package/dist/proxy/types.d.ts +72 -0
  350. package/dist/proxy/types.d.ts.map +1 -0
  351. package/dist/proxy/types.js +33 -0
  352. package/dist/proxy/types.js.map +1 -0
  353. package/dist/proxy/upstream-http.d.ts +26 -0
  354. package/dist/proxy/upstream-http.d.ts.map +1 -0
  355. package/dist/proxy/upstream-http.js +209 -0
  356. package/dist/proxy/upstream-http.js.map +1 -0
  357. package/dist/proxy/upstream-stdio.d.ts +25 -0
  358. package/dist/proxy/upstream-stdio.d.ts.map +1 -0
  359. package/dist/proxy/upstream-stdio.js +256 -0
  360. package/dist/proxy/upstream-stdio.js.map +1 -0
  361. package/dist/security/audit-trail.d.ts +74 -0
  362. package/dist/security/audit-trail.d.ts.map +1 -0
  363. package/dist/security/audit-trail.js +338 -0
  364. package/dist/security/audit-trail.js.map +1 -0
  365. package/dist/security/index.d.ts +5 -0
  366. package/dist/security/index.d.ts.map +1 -0
  367. package/dist/security/index.js +4 -0
  368. package/dist/security/index.js.map +1 -0
  369. package/dist/security/ssrf.d.ts +2 -0
  370. package/dist/security/ssrf.d.ts.map +1 -0
  371. package/dist/security/ssrf.js +126 -0
  372. package/dist/security/ssrf.js.map +1 -0
  373. package/dist/server.d.ts +14 -0
  374. package/dist/server.d.ts.map +1 -0
  375. package/dist/server.js +117 -0
  376. package/dist/server.js.map +1 -0
  377. package/dist/shared/__test-utils__/log-output.d.ts +49 -0
  378. package/dist/shared/__test-utils__/log-output.d.ts.map +1 -0
  379. package/dist/shared/__test-utils__/log-output.js +38 -0
  380. package/dist/shared/__test-utils__/log-output.js.map +1 -0
  381. package/dist/shared/config.d.ts +56 -0
  382. package/dist/shared/config.d.ts.map +1 -0
  383. package/dist/shared/config.js +290 -0
  384. package/dist/shared/config.js.map +1 -0
  385. package/dist/shared/errors.d.ts +139 -0
  386. package/dist/shared/errors.d.ts.map +1 -0
  387. package/dist/shared/errors.js +406 -0
  388. package/dist/shared/errors.js.map +1 -0
  389. package/dist/shared/events/factory.d.ts +143 -0
  390. package/dist/shared/events/factory.d.ts.map +1 -0
  391. package/dist/shared/events/factory.js +351 -0
  392. package/dist/shared/events/factory.js.map +1 -0
  393. package/dist/shared/events/index.d.ts +6 -0
  394. package/dist/shared/events/index.d.ts.map +1 -0
  395. package/dist/shared/events/index.js +3 -0
  396. package/dist/shared/events/index.js.map +1 -0
  397. package/dist/shared/events/serialize.d.ts +87 -0
  398. package/dist/shared/events/serialize.d.ts.map +1 -0
  399. package/dist/shared/events/serialize.js +510 -0
  400. package/dist/shared/events/serialize.js.map +1 -0
  401. package/dist/shared/events/types.d.ts +139 -0
  402. package/dist/shared/events/types.d.ts.map +1 -0
  403. package/dist/shared/events/types.js +2 -0
  404. package/dist/shared/events/types.js.map +1 -0
  405. package/dist/shared/harvest/event-buffer.d.ts +59 -0
  406. package/dist/shared/harvest/event-buffer.d.ts.map +1 -0
  407. package/dist/shared/harvest/event-buffer.js +100 -0
  408. package/dist/shared/harvest/event-buffer.js.map +1 -0
  409. package/dist/shared/harvest/harvest-scheduler.d.ts +200 -0
  410. package/dist/shared/harvest/harvest-scheduler.d.ts.map +1 -0
  411. package/dist/shared/harvest/harvest-scheduler.js +647 -0
  412. package/dist/shared/harvest/harvest-scheduler.js.map +1 -0
  413. package/dist/shared/harvest/index.d.ts +7 -0
  414. package/dist/shared/harvest/index.d.ts.map +1 -0
  415. package/dist/shared/harvest/index.js +4 -0
  416. package/dist/shared/harvest/index.js.map +1 -0
  417. package/dist/shared/harvest/metric-aggregator.d.ts +115 -0
  418. package/dist/shared/harvest/metric-aggregator.d.ts.map +1 -0
  419. package/dist/shared/harvest/metric-aggregator.js +247 -0
  420. package/dist/shared/harvest/metric-aggregator.js.map +1 -0
  421. package/dist/shared/index.d.ts +22 -0
  422. package/dist/shared/index.d.ts.map +1 -0
  423. package/dist/shared/index.js +13 -0
  424. package/dist/shared/index.js.map +1 -0
  425. package/dist/shared/logger.d.ts +57 -0
  426. package/dist/shared/logger.d.ts.map +1 -0
  427. package/dist/shared/logger.js +166 -0
  428. package/dist/shared/logger.js.map +1 -0
  429. package/dist/shared/pricing-data.d.ts +4 -0
  430. package/dist/shared/pricing-data.d.ts.map +1 -0
  431. package/dist/shared/pricing-data.js +473 -0
  432. package/dist/shared/pricing-data.js.map +1 -0
  433. package/dist/shared/pricing.d.ts +148 -0
  434. package/dist/shared/pricing.d.ts.map +1 -0
  435. package/dist/shared/pricing.js +528 -0
  436. package/dist/shared/pricing.js.map +1 -0
  437. package/dist/shared/redact.d.ts +33 -0
  438. package/dist/shared/redact.d.ts.map +1 -0
  439. package/dist/shared/redact.js +110 -0
  440. package/dist/shared/redact.js.map +1 -0
  441. package/dist/shared/timing.d.ts +96 -0
  442. package/dist/shared/timing.d.ts.map +1 -0
  443. package/dist/shared/timing.js +173 -0
  444. package/dist/shared/timing.js.map +1 -0
  445. package/dist/shared/tokens.d.ts +145 -0
  446. package/dist/shared/tokens.d.ts.map +1 -0
  447. package/dist/shared/tokens.js +492 -0
  448. package/dist/shared/tokens.js.map +1 -0
  449. package/dist/shared/transport/events-api.d.ts +14 -0
  450. package/dist/shared/transport/events-api.d.ts.map +1 -0
  451. package/dist/shared/transport/events-api.js +29 -0
  452. package/dist/shared/transport/events-api.js.map +1 -0
  453. package/dist/shared/transport/http-client.d.ts +49 -0
  454. package/dist/shared/transport/http-client.d.ts.map +1 -0
  455. package/dist/shared/transport/http-client.js +381 -0
  456. package/dist/shared/transport/http-client.js.map +1 -0
  457. package/dist/shared/transport/index.d.ts +10 -0
  458. package/dist/shared/transport/index.d.ts.map +1 -0
  459. package/dist/shared/transport/index.js +6 -0
  460. package/dist/shared/transport/index.js.map +1 -0
  461. package/dist/shared/transport/logs-api.d.ts +29 -0
  462. package/dist/shared/transport/logs-api.d.ts.map +1 -0
  463. package/dist/shared/transport/logs-api.js +40 -0
  464. package/dist/shared/transport/logs-api.js.map +1 -0
  465. package/dist/shared/transport/metric-api.d.ts +9 -0
  466. package/dist/shared/transport/metric-api.d.ts.map +1 -0
  467. package/dist/shared/transport/metric-api.js +39 -0
  468. package/dist/shared/transport/metric-api.js.map +1 -0
  469. package/dist/shared/transport/otlp-event-bridge.d.ts +22 -0
  470. package/dist/shared/transport/otlp-event-bridge.d.ts.map +1 -0
  471. package/dist/shared/transport/otlp-event-bridge.js +50 -0
  472. package/dist/shared/transport/otlp-event-bridge.js.map +1 -0
  473. package/dist/shared/transport/otlp-shared.d.ts +14 -0
  474. package/dist/shared/transport/otlp-shared.d.ts.map +1 -0
  475. package/dist/shared/transport/otlp-shared.js +49 -0
  476. package/dist/shared/transport/otlp-shared.js.map +1 -0
  477. package/dist/shared/transport/otlp-transport.d.ts +58 -0
  478. package/dist/shared/transport/otlp-transport.d.ts.map +1 -0
  479. package/dist/shared/transport/otlp-transport.js +236 -0
  480. package/dist/shared/transport/otlp-transport.js.map +1 -0
  481. package/dist/shared/transport/types.d.ts +129 -0
  482. package/dist/shared/transport/types.d.ts.map +1 -0
  483. package/dist/shared/transport/types.js +2 -0
  484. package/dist/shared/transport/types.js.map +1 -0
  485. package/dist/shared/version.d.ts +2 -0
  486. package/dist/shared/version.d.ts.map +1 -0
  487. package/dist/shared/version.js +2 -0
  488. package/dist/shared/version.js.map +1 -0
  489. package/dist/storage/index.d.ts +7 -0
  490. package/dist/storage/index.d.ts.map +1 -0
  491. package/dist/storage/index.js +4 -0
  492. package/dist/storage/index.js.map +1 -0
  493. package/dist/storage/local-store.d.ts +153 -0
  494. package/dist/storage/local-store.d.ts.map +1 -0
  495. package/dist/storage/local-store.js +719 -0
  496. package/dist/storage/local-store.js.map +1 -0
  497. package/dist/storage/retention.d.ts +2 -0
  498. package/dist/storage/retention.d.ts.map +1 -0
  499. package/dist/storage/retention.js +53 -0
  500. package/dist/storage/retention.js.map +1 -0
  501. package/dist/storage/session-store.d.ts +97 -0
  502. package/dist/storage/session-store.d.ts.map +1 -0
  503. package/dist/storage/session-store.js +391 -0
  504. package/dist/storage/session-store.js.map +1 -0
  505. package/dist/storage/types.d.ts +64 -0
  506. package/dist/storage/types.d.ts.map +1 -0
  507. package/dist/storage/types.js +2 -0
  508. package/dist/storage/types.js.map +1 -0
  509. package/dist/storage/weekly-summary.d.ts +61 -0
  510. package/dist/storage/weekly-summary.d.ts.map +1 -0
  511. package/dist/storage/weekly-summary.js +243 -0
  512. package/dist/storage/weekly-summary.js.map +1 -0
  513. package/dist/tools/analytics-tools.d.ts +101 -0
  514. package/dist/tools/analytics-tools.d.ts.map +1 -0
  515. package/dist/tools/analytics-tools.js +71 -0
  516. package/dist/tools/analytics-tools.js.map +1 -0
  517. package/dist/tools/cost-tools.d.ts +121 -0
  518. package/dist/tools/cost-tools.d.ts.map +1 -0
  519. package/dist/tools/cost-tools.js +174 -0
  520. package/dist/tools/cost-tools.js.map +1 -0
  521. package/dist/tools/cross-session-tools.d.ts +376 -0
  522. package/dist/tools/cross-session-tools.d.ts.map +1 -0
  523. package/dist/tools/cross-session-tools.js +820 -0
  524. package/dist/tools/cross-session-tools.js.map +1 -0
  525. package/dist/tools/extended-analytics-tools.d.ts +164 -0
  526. package/dist/tools/extended-analytics-tools.d.ts.map +1 -0
  527. package/dist/tools/extended-analytics-tools.js +121 -0
  528. package/dist/tools/extended-analytics-tools.js.map +1 -0
  529. package/dist/tools/index.d.ts +7 -0
  530. package/dist/tools/index.d.ts.map +1 -0
  531. package/dist/tools/index.js +4 -0
  532. package/dist/tools/index.js.map +1 -0
  533. package/dist/tools/session-stats.d.ts +162 -0
  534. package/dist/tools/session-stats.d.ts.map +1 -0
  535. package/dist/tools/session-stats.js +1054 -0
  536. package/dist/tools/session-stats.js.map +1 -0
  537. package/dist/tools/workflow-tools.d.ts +126 -0
  538. package/dist/tools/workflow-tools.d.ts.map +1 -0
  539. package/dist/tools/workflow-tools.js +274 -0
  540. package/dist/tools/workflow-tools.js.map +1 -0
  541. package/dist/tracing/mcp-tracer.d.ts +4 -0
  542. package/dist/tracing/mcp-tracer.d.ts.map +1 -0
  543. package/dist/tracing/mcp-tracer.js +14 -0
  544. package/dist/tracing/mcp-tracer.js.map +1 -0
  545. package/dist/tracing/session-span.d.ts +14 -0
  546. package/dist/tracing/session-span.d.ts.map +1 -0
  547. package/dist/tracing/session-span.js +53 -0
  548. package/dist/tracing/session-span.js.map +1 -0
  549. package/dist/tracing/task-span-tracker.d.ts +11 -0
  550. package/dist/tracing/task-span-tracker.d.ts.map +1 -0
  551. package/dist/tracing/task-span-tracker.js +59 -0
  552. package/dist/tracing/task-span-tracker.js.map +1 -0
  553. package/dist/tracing/tool-call-span.d.ts +4 -0
  554. package/dist/tracing/tool-call-span.d.ts.map +1 -0
  555. package/dist/tracing/tool-call-span.js +60 -0
  556. package/dist/tracing/tool-call-span.js.map +1 -0
  557. package/dist/transport/index.d.ts +3 -0
  558. package/dist/transport/index.d.ts.map +1 -0
  559. package/dist/transport/index.js +2 -0
  560. package/dist/transport/index.js.map +1 -0
  561. package/dist/transport/log-ingest.d.ts +42 -0
  562. package/dist/transport/log-ingest.d.ts.map +1 -0
  563. package/dist/transport/log-ingest.js +151 -0
  564. package/dist/transport/log-ingest.js.map +1 -0
  565. package/dist/transport/nr-ingest.d.ts +171 -0
  566. package/dist/transport/nr-ingest.d.ts.map +1 -0
  567. package/dist/transport/nr-ingest.js +659 -0
  568. package/dist/transport/nr-ingest.js.map +1 -0
  569. package/dist/types.d.ts +45 -0
  570. package/dist/types.d.ts.map +1 -0
  571. package/dist/types.js +2 -0
  572. package/dist/types.js.map +1 -0
  573. package/dist/web/assets/index-BrL281N-.css +2 -0
  574. package/dist/web/assets/index-CcaYZzXm.js +42 -0
  575. package/dist/web/favicon.svg +15 -0
  576. package/dist/web/index.html +15 -0
  577. package/examples/local-alert-rules.json +106 -0
  578. package/package.json +125 -1
@@ -0,0 +1,148 @@
1
+ import type { TokenUsage } from './tokens.js';
2
+ export interface ModelPricing {
3
+ readonly inputPerMTok: number;
4
+ readonly outputPerMTok: number;
5
+ readonly thinkingPerMTok?: number;
6
+ readonly cacheReadPerMTok?: number;
7
+ readonly cacheCreationPerMTok?: number;
8
+ readonly contextWindow: number;
9
+ /** Input-token count above which tier rates apply. */
10
+ readonly tierThreshold?: number;
11
+ readonly tierInputPerMTok?: number;
12
+ readonly tierOutputPerMTok?: number;
13
+ readonly tierThinkingPerMTok?: number;
14
+ /**
15
+ * How tier rates are applied once `inputTokens > tierThreshold`. Defaults to
16
+ * `'flat'` (current behavior, matches Gemini 1.5/2.5 Pro semantics).
17
+ *
18
+ * - `'flat'`: the **entire request** (input, output, thinking) is billed at
19
+ * the tier rates. This is what every provider we currently price uses.
20
+ * - `'marginal'`: only the **input tokens above the threshold** are billed
21
+ * at `tierInputPerMTok`; tokens up to the threshold use `inputPerMTok`.
22
+ * Output and thinking always use their base rates in this mode — the
23
+ * `tierOutputPerMTok` / `tierThinkingPerMTok` fields are ignored. This
24
+ * models providers that charge a higher rate purely for excess context.
25
+ *
26
+ * No currently wrapped provider needs `'marginal'`; the mode exists for
27
+ * forward compatibility.
28
+ */
29
+ readonly tierMode?: 'flat' | 'marginal';
30
+ }
31
+ export interface CostBreakdown {
32
+ inputUsd: number;
33
+ outputUsd: number;
34
+ thinkingUsd: number;
35
+ cacheReadUsd: number;
36
+ cacheCreationUsd: number;
37
+ totalUsd: number;
38
+ savingsFromCacheUsd: number;
39
+ }
40
+ /**
41
+ * Load a custom pricing override file from disk.
42
+ *
43
+ * **This function is synchronous (`readFileSync` / `statSync`) and is intended
44
+ * for startup-time use only**. Calling it from a hot path
45
+ * — for example, a SIGHUP-triggered reload while the process is also serving
46
+ * inference traffic — will block the event loop for the duration of the read
47
+ * and parse. If you need reload-on-signal, schedule it from a worker thread
48
+ * or wrap it in `setImmediate` so the surrounding tick can complete first.
49
+ *
50
+ * Files larger than {@link MAX_PRICING_FILE_BYTES} are rejected without being
51
+ * read — a pricing table is a small dictionary, and a
52
+ * mis-pointed multi-GB file would OOM `JSON.parse`.
53
+ *
54
+ * Returns the parsed override map on success, or `null` when the file is
55
+ * missing, the wrong shape, too large, or contains no valid entries (§PR7).
56
+ *
57
+ * **Note:** `null` is returned for both "file not found" and "all entries
58
+ * invalid" — callers cannot distinguish the two cases from the return value
59
+ * alone. Both emit a `logger.warn` with a distinguishing message so the
60
+ * difference is visible in stderr. A future improvement would be to return a
61
+ * discriminated union `{ status: 'ok' | 'not-found' | 'all-invalid', ... }`.
62
+ */
63
+ export declare function loadCustomPricing(filePath: string): Record<string, ModelPricing> | null;
64
+ /**
65
+ * Encapsulated pricing table for a single agent / tenant.
66
+ *
67
+ * Each `PricingTable` instance owns its own merged table and resolution state.
68
+ * Construct one per agent when a single Node process needs to serve multiple
69
+ * tenants with different custom pricing overrides; otherwise the module-level
70
+ * functions (`resolveModelPricing`, `calculateCost`, `initPricing`) operate on
71
+ * a process-wide default singleton and are sufficient for the common case.
72
+ *
73
+ * Multi-tenant example:
74
+ * ```ts
75
+ * const tenantA = new PricingTable('/etc/agent-a/pricing.json');
76
+ * const tenantB = new PricingTable('/etc/agent-b/pricing.json');
77
+ * tenantA.resolve('claude-opus-4-7'); // sees agent-a overrides
78
+ * tenantB.resolve('claude-opus-4-7'); // sees agent-b overrides
79
+ * ```
80
+ */
81
+ export declare class PricingTable {
82
+ private table;
83
+ constructor(customFilePath?: string | null);
84
+ /**
85
+ * Reset the table to built-in defaults, optionally re-overlaying a custom
86
+ * pricing file. Equivalent to `new PricingTable(customFilePath)` but reuses
87
+ * the existing instance.
88
+ */
89
+ reset(customFilePath?: string | null): void;
90
+ /**
91
+ * Resolve a model name to its pricing entry. See module-level
92
+ * `resolveModelPricing` for the resolution algorithm.
93
+ */
94
+ resolve(modelName: string): ModelPricing | null;
95
+ /**
96
+ * Calculate a cost breakdown for the given model and token usage. See
97
+ * module-level `calculateCost` for tiered-pricing semantics.
98
+ */
99
+ calculateCost(model: string, usage: TokenUsage): CostBreakdown;
100
+ }
101
+ /**
102
+ * (Re-)initialize the **default singleton** pricing table. Call with a custom
103
+ * file path to overlay user-provided prices on top of the built-in table.
104
+ * Call with `null`/`undefined` to reset to the built-in defaults.
105
+ *
106
+ * **Synchronous I/O — startup-only.** Reads the custom file with `readFileSync`
107
+ *; calling this on a hot path (e.g. a SIGHUP-triggered
108
+ * reload while the process is serving inference traffic) blocks the event
109
+ * loop. Schedule reloads from a worker thread or wrap in `setImmediate`.
110
+ *
111
+ * **Note:** This mutates a process-wide singleton. If your process serves
112
+ * multiple agents with distinct pricing files, instantiate a `PricingTable`
113
+ * per agent instead — calling `initPricing` again will overwrite the
114
+ * previously-loaded prices and the first agent will silently see the second's.
115
+ */
116
+ export declare function initPricing(customFilePath?: string | null): void;
117
+ /**
118
+ * Resolve a model name against the default singleton pricing table.
119
+ *
120
+ * 1. Exact match (e.g. `claude-sonnet-4-20250514`)
121
+ * 2. Family-name alias (e.g. `claude-opus-4` → `claude-opus-4-7`) — see
122
+ * MODEL_ALIASES in pricing-data.ts. Aliases are the *primary* mechanism
123
+ * for routing family names to current-generation pricing.
124
+ * 3. Forward prefix — table key starts with modelName
125
+ * (e.g. an unaliased `gemini-2.5-flash` would match itself; only used for
126
+ * coverage of new keys not yet in the alias map)
127
+ * 4. Reverse prefix — modelName starts with table key's base (date stripped)
128
+ * (e.g. `claude-opus-4-99` matches base `claude-opus-4` from a dated key)
129
+ * 5. Return `null` and log a warning if nothing matches.
130
+ */
131
+ export declare function resolveModelPricing(modelName: string): ModelPricing | null;
132
+ /**
133
+ * Calculate a cost breakdown for the given model and token usage against the
134
+ * default singleton pricing table.
135
+ *
136
+ * If the model is unknown, returns an all-zero breakdown and logs a warning.
137
+ *
138
+ * Tiered pricing semantics (when `inputTokens > tierThreshold`):
139
+ *
140
+ * - `tierMode: 'flat'` (default) — the entire request (input, output, thinking)
141
+ * is billed at the configured tier rates. Matches Gemini 1.5/2.5 Pro.
142
+ * - `tierMode: 'marginal'` — only the input tokens above the threshold are
143
+ * billed at `tierInputPerMTok`; tokens up to the threshold use `inputPerMTok`.
144
+ * Output and thinking always use their base rates (the `tierOutput*` /
145
+ * `tierThinking*` fields are ignored in this mode).
146
+ */
147
+ export declare function calculateCost(model: string, usage: TokenUsage): CostBreakdown;
148
+ //# sourceMappingURL=pricing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../../src/shared/pricing.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAU9C,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,sDAAsD;IACtD,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;CACzC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AA+LD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,IAAI,CA4DvF;AAqCD;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAA+B;gBAEhC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI;IAgB1C;;;;OAIG;IACH,KAAK,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAa3C;;;OAGG;IACH,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IA+E/C;;;OAGG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,aAAa;CAO/D;AAQD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAEhE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAE1E;AAuFD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,aAAa,CAE7E"}
@@ -0,0 +1,528 @@
1
+ import { readFileSync, statSync } from 'node:fs';
2
+ import { resolve, extname } from 'node:path';
3
+ import { DEFAULT_PRICING_TABLE, MODEL_ALIASES } from './pricing-data.js';
4
+ import { createLogger } from './logger.js';
5
+ const logger = createLogger('pricing');
6
+ const ZERO_COST = Object.freeze({
7
+ inputUsd: 0,
8
+ outputUsd: 0,
9
+ thinkingUsd: 0,
10
+ cacheReadUsd: 0,
11
+ cacheCreationUsd: 0,
12
+ totalUsd: 0,
13
+ savingsFromCacheUsd: 0,
14
+ });
15
+ // Strip a trailing dated suffix (-YYYYMMDD) to get the model family base name.
16
+ const DATED_SUFFIX_RE = /-\d{8}$/;
17
+ // ---------------------------------------------------------------------------
18
+ // Custom pricing file
19
+ // ---------------------------------------------------------------------------
20
+ // Sanity ceiling for per-MTok rates. The most expensive frontier model rates
21
+ // at the time of writing are well under $100/MTok; a value above $10,000/MTok
22
+ // almost certainly reflects a typo (e.g. forgetting that the unit is per
23
+ // million tokens, or pricing in cents instead of dollars). Reject these
24
+ // outright — silently accepting them produces wildly inflated cost telemetry.
25
+ const MAX_REASONABLE_RATE_PER_MTOK = 10_000;
26
+ // Maximum allowed size of a custom pricing JSON file.
27
+ // A pricing table is a small dictionary; even with hundreds of model entries
28
+ // the file is well under 100 KB. The 1 MB cap is a defensive ceiling that
29
+ // prevents `readFileSync` from happily slurping a multi-gigabyte file the
30
+ // caller mis-pointed us at, which would then OOM `JSON.parse`.
31
+ const MAX_PRICING_FILE_BYTES = 1_000_000;
32
+ function isFiniteNonNegative(v) {
33
+ return typeof v === 'number' && Number.isFinite(v) && v >= 0;
34
+ }
35
+ function isWithinRateCeiling(v) {
36
+ return v <= MAX_REASONABLE_RATE_PER_MTOK;
37
+ }
38
+ function validatePricingEntry(model, entry) {
39
+ if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) {
40
+ logger.warn('Custom pricing entry is not an object — skipped', { model });
41
+ return null;
42
+ }
43
+ const e = entry;
44
+ if (!isFiniteNonNegative(e.inputPerMTok)) {
45
+ logger.warn('Custom pricing entry has invalid inputPerMTok — skipped', {
46
+ model,
47
+ value: e.inputPerMTok,
48
+ });
49
+ return null;
50
+ }
51
+ if (!isWithinRateCeiling(e.inputPerMTok)) {
52
+ logger.warn('Custom pricing entry has implausibly large inputPerMTok — skipped', {
53
+ model,
54
+ value: e.inputPerMTok,
55
+ ceiling: MAX_REASONABLE_RATE_PER_MTOK,
56
+ });
57
+ return null;
58
+ }
59
+ if (!isFiniteNonNegative(e.outputPerMTok)) {
60
+ logger.warn('Custom pricing entry has invalid outputPerMTok — skipped', {
61
+ model,
62
+ value: e.outputPerMTok,
63
+ });
64
+ return null;
65
+ }
66
+ if (!isWithinRateCeiling(e.outputPerMTok)) {
67
+ logger.warn('Custom pricing entry has implausibly large outputPerMTok — skipped', {
68
+ model,
69
+ value: e.outputPerMTok,
70
+ ceiling: MAX_REASONABLE_RATE_PER_MTOK,
71
+ });
72
+ return null;
73
+ }
74
+ if (typeof e.contextWindow !== 'number' ||
75
+ !Number.isFinite(e.contextWindow) ||
76
+ e.contextWindow <= 0 ||
77
+ !Number.isInteger(e.contextWindow)) {
78
+ logger.warn('Custom pricing entry has invalid contextWindow (must be a positive integer) — skipped', { model, value: e.contextWindow });
79
+ return null;
80
+ }
81
+ const optionalRateFields = [
82
+ 'thinkingPerMTok',
83
+ 'cacheReadPerMTok',
84
+ 'cacheCreationPerMTok',
85
+ 'tierInputPerMTok',
86
+ 'tierOutputPerMTok',
87
+ 'tierThinkingPerMTok',
88
+ ];
89
+ for (const field of optionalRateFields) {
90
+ const value = e[field];
91
+ if (value === undefined)
92
+ continue;
93
+ if (!isFiniteNonNegative(value)) {
94
+ logger.warn(`Custom pricing entry has invalid ${field} — skipped`, { model, value });
95
+ return null;
96
+ }
97
+ if (!isWithinRateCeiling(value)) {
98
+ logger.warn(`Custom pricing entry has implausibly large ${field} — skipped`, {
99
+ model,
100
+ value,
101
+ ceiling: MAX_REASONABLE_RATE_PER_MTOK,
102
+ });
103
+ return null;
104
+ }
105
+ }
106
+ if (e.tierThreshold !== undefined &&
107
+ (typeof e.tierThreshold !== 'number' ||
108
+ !Number.isFinite(e.tierThreshold) ||
109
+ e.tierThreshold <= 0 ||
110
+ !Number.isInteger(e.tierThreshold))) {
111
+ logger.warn('Custom pricing entry has invalid tierThreshold (must be a positive integer) — skipped', { model, value: e.tierThreshold });
112
+ return null;
113
+ }
114
+ if (e.tierMode !== undefined && e.tierMode !== 'flat' && e.tierMode !== 'marginal') {
115
+ logger.warn('Custom pricing entry has invalid tierMode (must be "flat" or "marginal") — skipped', {
116
+ model,
117
+ value: e.tierMode,
118
+ });
119
+ return null;
120
+ }
121
+ // Relational sanity checks — accept the entry but warn on configurations
122
+ // that almost always indicate a misconfiguration. These are warnings, not
123
+ // rejections: unusual but legitimate price structures (e.g. an experimental
124
+ // discount where cache reads cost more than fresh input) should still be
125
+ // representable without forcing the user to fork the validator.
126
+ const inputRate = e.inputPerMTok;
127
+ if (e.cacheReadPerMTok !== undefined && e.cacheReadPerMTok > inputRate) {
128
+ logger.warn('Custom pricing entry has cacheReadPerMTok above inputPerMTok — accepted but unusual', {
129
+ model,
130
+ cacheReadPerMTok: e.cacheReadPerMTok,
131
+ inputPerMTok: inputRate,
132
+ });
133
+ }
134
+ if (e.tierInputPerMTok !== undefined && e.tierInputPerMTok < inputRate) {
135
+ logger.warn('Custom pricing entry has tierInputPerMTok below inputPerMTok — accepted but unusual (tier rates are typically higher)', {
136
+ model,
137
+ tierInputPerMTok: e.tierInputPerMTok,
138
+ inputPerMTok: inputRate,
139
+ });
140
+ }
141
+ // Construct a fresh `ModelPricing` object that contains ONLY recognized
142
+ // fields. Returning `e` directly leaks any typo'd or
143
+ // future / unknown keys from the user's JSON into the merged table, where
144
+ // they would surface in serialization (`Object.entries(...)`) and risk
145
+ // future code mistaking them for valid pricing fields. Built as a single
146
+ // literal to satisfy the readonly interface (§PR5).
147
+ return {
148
+ inputPerMTok: e.inputPerMTok,
149
+ outputPerMTok: e.outputPerMTok,
150
+ contextWindow: e.contextWindow,
151
+ ...(typeof e.thinkingPerMTok === 'number' && { thinkingPerMTok: e.thinkingPerMTok }),
152
+ ...(typeof e.cacheReadPerMTok === 'number' && { cacheReadPerMTok: e.cacheReadPerMTok }),
153
+ ...(typeof e.cacheCreationPerMTok === 'number' && {
154
+ cacheCreationPerMTok: e.cacheCreationPerMTok,
155
+ }),
156
+ ...(typeof e.tierThreshold === 'number' && { tierThreshold: e.tierThreshold }),
157
+ ...(typeof e.tierInputPerMTok === 'number' && { tierInputPerMTok: e.tierInputPerMTok }),
158
+ ...(typeof e.tierOutputPerMTok === 'number' && { tierOutputPerMTok: e.tierOutputPerMTok }),
159
+ ...(typeof e.tierThinkingPerMTok === 'number' && {
160
+ tierThinkingPerMTok: e.tierThinkingPerMTok,
161
+ }),
162
+ ...((e.tierMode === 'flat' || e.tierMode === 'marginal') && { tierMode: e.tierMode }),
163
+ };
164
+ }
165
+ /**
166
+ * Load a custom pricing override file from disk.
167
+ *
168
+ * **This function is synchronous (`readFileSync` / `statSync`) and is intended
169
+ * for startup-time use only**. Calling it from a hot path
170
+ * — for example, a SIGHUP-triggered reload while the process is also serving
171
+ * inference traffic — will block the event loop for the duration of the read
172
+ * and parse. If you need reload-on-signal, schedule it from a worker thread
173
+ * or wrap it in `setImmediate` so the surrounding tick can complete first.
174
+ *
175
+ * Files larger than {@link MAX_PRICING_FILE_BYTES} are rejected without being
176
+ * read — a pricing table is a small dictionary, and a
177
+ * mis-pointed multi-GB file would OOM `JSON.parse`.
178
+ *
179
+ * Returns the parsed override map on success, or `null` when the file is
180
+ * missing, the wrong shape, too large, or contains no valid entries (§PR7).
181
+ *
182
+ * **Note:** `null` is returned for both "file not found" and "all entries
183
+ * invalid" — callers cannot distinguish the two cases from the return value
184
+ * alone. Both emit a `logger.warn` with a distinguishing message so the
185
+ * difference is visible in stderr. A future improvement would be to return a
186
+ * discriminated union `{ status: 'ok' | 'not-found' | 'all-invalid', ... }`.
187
+ */
188
+ export function loadCustomPricing(filePath) {
189
+ const resolvedPath = resolve(filePath);
190
+ if (extname(resolvedPath).toLowerCase() !== '.json') {
191
+ logger.warn('Custom pricing file must have a .json extension', { filePath: resolvedPath });
192
+ return null;
193
+ }
194
+ try {
195
+ // Reject oversized files before reading — `readFileSync` will happily
196
+ // slurp gigabytes into memory and `JSON.parse` will then OOM.
197
+ const stat = statSync(resolvedPath);
198
+ if (stat.size > MAX_PRICING_FILE_BYTES) {
199
+ logger.warn('Custom pricing file exceeds size limit — skipped', {
200
+ filePath: resolvedPath,
201
+ sizeBytes: stat.size,
202
+ maxBytes: MAX_PRICING_FILE_BYTES,
203
+ });
204
+ return null;
205
+ }
206
+ const raw = readFileSync(resolvedPath, 'utf-8');
207
+ const parsed = JSON.parse(raw);
208
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
209
+ logger.warn('Custom pricing file is not a JSON object', { filePath: resolvedPath });
210
+ return null;
211
+ }
212
+ // Null-prototype object prevents __proto__ assignment from polluting
213
+ // Object.prototype even if a reserved key slips through (§P1).
214
+ const result = Object.create(null);
215
+ const RESERVED = new Set(['__proto__', 'constructor', 'prototype']);
216
+ for (const [model, entry] of Object.entries(parsed)) {
217
+ if (RESERVED.has(model)) {
218
+ logger.warn('Custom pricing entry uses a reserved key — skipped', { model });
219
+ continue;
220
+ }
221
+ const validated = validatePricingEntry(model, entry);
222
+ if (validated !== null) {
223
+ result[model] = validated;
224
+ }
225
+ }
226
+ // Return null when no valid entries were found so callers can distinguish
227
+ // "applied custom pricing" (truthy) from "file present but all invalid" (§PR1).
228
+ if (Object.keys(result).length === 0) {
229
+ logger.warn('Custom pricing file contained no valid entries', { filePath: resolvedPath });
230
+ return null;
231
+ }
232
+ return result;
233
+ }
234
+ catch (err) {
235
+ logger.warn('Failed to load custom pricing file', {
236
+ filePath: resolvedPath,
237
+ error: err instanceof Error ? err.message : String(err),
238
+ });
239
+ return null;
240
+ }
241
+ }
242
+ /**
243
+ * Deep-clone a pricing table.
244
+ *
245
+ * `{ ...DEFAULT_PRICING_TABLE }` is a shallow copy — the inner `ModelPricing`
246
+ * objects share references. With the default-singleton pattern, that means a
247
+ * caller doing
248
+ *
249
+ * ```ts
250
+ * import { DEFAULT_PRICING_TABLE } from '@newrelic/ai-telemetry';
251
+ * DEFAULT_PRICING_TABLE['claude-opus-4-7'].outputPerMTok = 99999;
252
+ * ```
253
+ *
254
+ * would mutate the live pricing data backing every active `PricingTable`
255
+ * instance — including the process-wide default — until the next `reset()`.
256
+ * Deep-cloning at construction breaks the shared-reference path so consumer
257
+ * code can't accidentally rewrite the canonical rates.
258
+ *
259
+ * `structuredClone` (Node 17+) is preferred over `JSON.parse(JSON.stringify())`
260
+ * because it preserves the value-type fidelity of `ModelPricing` (numeric
261
+ * fields stay numbers; an undefined optional field stays undefined rather
262
+ * than being silently dropped). The pricing table contains only plain
263
+ * objects with primitive fields, so neither path's edge cases (cycles,
264
+ * functions, Date) apply here, but `structuredClone` is also faster on
265
+ * modern V8.
266
+ */
267
+ function clonePricingTable(source) {
268
+ return structuredClone(source);
269
+ }
270
+ // ---------------------------------------------------------------------------
271
+ // PricingTable — instance-based pricing
272
+ // ---------------------------------------------------------------------------
273
+ /**
274
+ * Encapsulated pricing table for a single agent / tenant.
275
+ *
276
+ * Each `PricingTable` instance owns its own merged table and resolution state.
277
+ * Construct one per agent when a single Node process needs to serve multiple
278
+ * tenants with different custom pricing overrides; otherwise the module-level
279
+ * functions (`resolveModelPricing`, `calculateCost`, `initPricing`) operate on
280
+ * a process-wide default singleton and are sufficient for the common case.
281
+ *
282
+ * Multi-tenant example:
283
+ * ```ts
284
+ * const tenantA = new PricingTable('/etc/agent-a/pricing.json');
285
+ * const tenantB = new PricingTable('/etc/agent-b/pricing.json');
286
+ * tenantA.resolve('claude-opus-4-7'); // sees agent-a overrides
287
+ * tenantB.resolve('claude-opus-4-7'); // sees agent-b overrides
288
+ * ```
289
+ */
290
+ export class PricingTable {
291
+ table;
292
+ constructor(customFilePath) {
293
+ // Null-prototype base: Object.assign to a null-prototype target does not
294
+ // invoke the __proto__ setter, so prototype pollution is defused even if
295
+ // loadCustomPricing returns a result containing that key (§P1).
296
+ this.table = Object.assign(Object.create(null), clonePricingTable(DEFAULT_PRICING_TABLE));
297
+ if (customFilePath) {
298
+ const custom = loadCustomPricing(customFilePath);
299
+ if (custom) {
300
+ Object.assign(this.table, custom);
301
+ }
302
+ }
303
+ }
304
+ /**
305
+ * Reset the table to built-in defaults, optionally re-overlaying a custom
306
+ * pricing file. Equivalent to `new PricingTable(customFilePath)` but reuses
307
+ * the existing instance.
308
+ */
309
+ reset(customFilePath) {
310
+ this.table = Object.assign(Object.create(null), clonePricingTable(DEFAULT_PRICING_TABLE));
311
+ if (customFilePath) {
312
+ const custom = loadCustomPricing(customFilePath);
313
+ if (custom) {
314
+ Object.assign(this.table, custom);
315
+ }
316
+ }
317
+ }
318
+ /**
319
+ * Resolve a model name to its pricing entry. See module-level
320
+ * `resolveModelPricing` for the resolution algorithm.
321
+ */
322
+ resolve(modelName) {
323
+ // Returns shallow copies so caller mutation cannot corrupt the instance
324
+ // table — resolved entries are values, not live references (§PRC1).
325
+ // 1. Exact match — Object.hasOwn guards against inherited prototype values
326
+ // for non-null-prototype tables and makes intent explicit (§P2).
327
+ if (Object.hasOwn(this.table, modelName)) {
328
+ return { ...this.table[modelName] };
329
+ }
330
+ // 2. Family-name alias
331
+ const aliasTarget = MODEL_ALIASES[modelName];
332
+ if (aliasTarget && Object.hasOwn(this.table, aliasTarget)) {
333
+ return { ...this.table[aliasTarget] };
334
+ }
335
+ // Forward prefix: find table keys that start with the given name followed
336
+ // by a digit-led suffix. Longest key wins on tie — but two same-length
337
+ // candidates would be non-deterministic (§PR1). Log a warning in that case
338
+ // so future table additions that create ambiguity are visible.
339
+ let bestKey = null;
340
+ let ambiguous = false;
341
+ for (const key of Object.keys(this.table)) {
342
+ const suffix = key.slice(modelName.length);
343
+ if (key.startsWith(modelName) && /^-\d/.test(suffix)) {
344
+ if (bestKey === null || key.length > bestKey.length) {
345
+ bestKey = key;
346
+ ambiguous = false;
347
+ }
348
+ else if (key.length === bestKey.length) {
349
+ ambiguous = true;
350
+ }
351
+ }
352
+ }
353
+ if (ambiguous && bestKey !== null) {
354
+ // §11.5: returning a non-deterministic result is worse than returning null,
355
+ // because the caller cannot distinguish "pricing found" from "pricing guessed".
356
+ // Returning null forces the table maintainer to add an explicit alias entry
357
+ // rather than silently accepting iteration-order-dependent pricing.
358
+ logger.warn('Ambiguous forward-prefix match — two same-length candidates; returning null. ' +
359
+ 'Add an explicit MODEL_ALIASES entry to resolve.', {
360
+ model: modelName,
361
+ });
362
+ return null;
363
+ }
364
+ if (bestKey && Object.hasOwn(this.table, bestKey)) {
365
+ return { ...this.table[bestKey] };
366
+ }
367
+ // Reverse prefix: strip date suffix from table keys and check if modelName
368
+ // starts with the resulting base. When the matched base has an alias,
369
+ // route through the alias to the *current-generation* entry rather than
370
+ // the legacy dated key.
371
+ let bestBase = null;
372
+ let bestBaseKey = null;
373
+ for (const key of Object.keys(this.table)) {
374
+ const base = key.replace(DATED_SUFFIX_RE, '');
375
+ if (base !== key && modelName.startsWith(base)) {
376
+ if (bestBase === null || base.length > bestBase.length) {
377
+ bestBase = base;
378
+ bestBaseKey = key;
379
+ }
380
+ }
381
+ }
382
+ if (bestBase && bestBaseKey) {
383
+ const aliasedTarget = MODEL_ALIASES[bestBase];
384
+ if (aliasedTarget && Object.hasOwn(this.table, aliasedTarget)) {
385
+ return { ...this.table[aliasedTarget] };
386
+ }
387
+ return Object.hasOwn(this.table, bestBaseKey) ? { ...this.table[bestBaseKey] } : null;
388
+ }
389
+ logger.warn('Unknown model, pricing not available', { model: modelName });
390
+ return null;
391
+ }
392
+ /**
393
+ * Calculate a cost breakdown for the given model and token usage. See
394
+ * module-level `calculateCost` for tiered-pricing semantics.
395
+ */
396
+ calculateCost(model, usage) {
397
+ const pricing = this.resolve(model);
398
+ if (!pricing) {
399
+ return { ...ZERO_COST };
400
+ }
401
+ return computeCost(pricing, usage);
402
+ }
403
+ }
404
+ // ---------------------------------------------------------------------------
405
+ // Process-wide default singleton + back-compat module API
406
+ // ---------------------------------------------------------------------------
407
+ const defaultTable = new PricingTable();
408
+ /**
409
+ * (Re-)initialize the **default singleton** pricing table. Call with a custom
410
+ * file path to overlay user-provided prices on top of the built-in table.
411
+ * Call with `null`/`undefined` to reset to the built-in defaults.
412
+ *
413
+ * **Synchronous I/O — startup-only.** Reads the custom file with `readFileSync`
414
+ *; calling this on a hot path (e.g. a SIGHUP-triggered
415
+ * reload while the process is serving inference traffic) blocks the event
416
+ * loop. Schedule reloads from a worker thread or wrap in `setImmediate`.
417
+ *
418
+ * **Note:** This mutates a process-wide singleton. If your process serves
419
+ * multiple agents with distinct pricing files, instantiate a `PricingTable`
420
+ * per agent instead — calling `initPricing` again will overwrite the
421
+ * previously-loaded prices and the first agent will silently see the second's.
422
+ */
423
+ export function initPricing(customFilePath) {
424
+ defaultTable.reset(customFilePath);
425
+ }
426
+ /**
427
+ * Resolve a model name against the default singleton pricing table.
428
+ *
429
+ * 1. Exact match (e.g. `claude-sonnet-4-20250514`)
430
+ * 2. Family-name alias (e.g. `claude-opus-4` → `claude-opus-4-7`) — see
431
+ * MODEL_ALIASES in pricing-data.ts. Aliases are the *primary* mechanism
432
+ * for routing family names to current-generation pricing.
433
+ * 3. Forward prefix — table key starts with modelName
434
+ * (e.g. an unaliased `gemini-2.5-flash` would match itself; only used for
435
+ * coverage of new keys not yet in the alias map)
436
+ * 4. Reverse prefix — modelName starts with table key's base (date stripped)
437
+ * (e.g. `claude-opus-4-99` matches base `claude-opus-4` from a dated key)
438
+ * 5. Return `null` and log a warning if nothing matches.
439
+ */
440
+ export function resolveModelPricing(modelName) {
441
+ return defaultTable.resolve(modelName);
442
+ }
443
+ // ---------------------------------------------------------------------------
444
+ // Cost calculation helpers
445
+ // ---------------------------------------------------------------------------
446
+ function tokensToUsd(tokens, ratePerMTok) {
447
+ return (tokens * ratePerMTok) / 1_000_000;
448
+ }
449
+ function computeCost(pricing, usage) {
450
+ const tierMode = pricing.tierMode ?? 'flat';
451
+ const useTier = pricing.tierThreshold !== undefined && usage.inputTokens > pricing.tierThreshold;
452
+ // Output / thinking rates: only flat mode uses the tier overrides; in
453
+ // marginal mode they always use the base rates.
454
+ const outputRate = useTier && tierMode === 'flat' && pricing.tierOutputPerMTok !== undefined
455
+ ? pricing.tierOutputPerMTok
456
+ : pricing.outputPerMTok;
457
+ const thinkingRate = useTier && tierMode === 'flat' && pricing.tierThinkingPerMTok !== undefined
458
+ ? pricing.tierThinkingPerMTok
459
+ : (pricing.thinkingPerMTok ?? 0);
460
+ const cacheReadRate = pricing.cacheReadPerMTok ?? 0;
461
+ const cacheCreationRate = pricing.cacheCreationPerMTok ?? 0;
462
+ // Resolve the "billing rate" for input. In flat mode this single rate
463
+ // covers all input tokens; in marginal mode the rate that would have been
464
+ // applied if no caching had happened — used for the savings comparison.
465
+ const inputRate = useTier && tierMode === 'flat' && pricing.tierInputPerMTok !== undefined
466
+ ? pricing.tierInputPerMTok
467
+ : pricing.inputPerMTok;
468
+ // Input cost: marginal mode splits at the threshold; flat mode uses inputRate.
469
+ let inputUsd;
470
+ if (useTier && tierMode === 'marginal' && pricing.tierInputPerMTok !== undefined) {
471
+ const threshold = pricing.tierThreshold;
472
+ const baseTokens = Math.min(usage.inputTokens, threshold);
473
+ const excessTokens = usage.inputTokens - baseTokens;
474
+ inputUsd =
475
+ tokensToUsd(baseTokens, pricing.inputPerMTok) +
476
+ tokensToUsd(excessTokens, pricing.tierInputPerMTok);
477
+ }
478
+ else {
479
+ inputUsd = tokensToUsd(usage.inputTokens, inputRate);
480
+ }
481
+ const outputUsd = tokensToUsd(usage.outputTokens, outputRate);
482
+ const thinkingUsd = tokensToUsd(usage.thinkingTokens, thinkingRate);
483
+ const cacheReadUsd = tokensToUsd(usage.cacheReadTokens, cacheReadRate);
484
+ const cacheCreationUsd = tokensToUsd(usage.cacheCreationTokens, cacheCreationRate);
485
+ const totalUsd = inputUsd + outputUsd + thinkingUsd + cacheReadUsd + cacheCreationUsd;
486
+ // Savings: what the cache-read tokens would have cost at the full input rate.
487
+ // In marginal mode, the savings rate depends on whether the fresh input
488
+ // exceeded the tier threshold — above-threshold tokens save at the tier rate,
489
+ // not the base rate (§PRC3).
490
+ const savingsInputRate = useTier &&
491
+ tierMode === 'marginal' &&
492
+ pricing.tierInputPerMTok !== undefined &&
493
+ usage.inputTokens > (pricing.tierThreshold ?? Infinity)
494
+ ? pricing.tierInputPerMTok
495
+ : inputRate;
496
+ // Clamp to >= 0 — a misconfigured custom pricing entry where cacheReadRate
497
+ // exceeds inputRate would otherwise produce a negative "savings" number that
498
+ // gets reported as a positive cost benefit downstream.
499
+ const savingsFromCacheUsd = Math.max(0, tokensToUsd(usage.cacheReadTokens, savingsInputRate - cacheReadRate));
500
+ return {
501
+ inputUsd,
502
+ outputUsd,
503
+ thinkingUsd,
504
+ cacheReadUsd,
505
+ cacheCreationUsd,
506
+ totalUsd,
507
+ savingsFromCacheUsd,
508
+ };
509
+ }
510
+ /**
511
+ * Calculate a cost breakdown for the given model and token usage against the
512
+ * default singleton pricing table.
513
+ *
514
+ * If the model is unknown, returns an all-zero breakdown and logs a warning.
515
+ *
516
+ * Tiered pricing semantics (when `inputTokens > tierThreshold`):
517
+ *
518
+ * - `tierMode: 'flat'` (default) — the entire request (input, output, thinking)
519
+ * is billed at the configured tier rates. Matches Gemini 1.5/2.5 Pro.
520
+ * - `tierMode: 'marginal'` — only the input tokens above the threshold are
521
+ * billed at `tierInputPerMTok`; tokens up to the threshold use `inputPerMTok`.
522
+ * Output and thinking always use their base rates (the `tierOutput*` /
523
+ * `tierThinking*` fields are ignored in this mode).
524
+ */
525
+ export function calculateCost(model, usage) {
526
+ return defaultTable.calculateCost(model, usage);
527
+ }
528
+ //# sourceMappingURL=pricing.js.map