@mupt-ai/dari-gateway 0.2.0 → 0.3.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 (385) hide show
  1. package/README.md +8 -4
  2. package/dist/bun.d.ts +2 -0
  3. package/dist/bun.js +4 -0
  4. package/dist/context/eval_catalog.d.ts +3 -0
  5. package/dist/context/eval_catalog.js +7 -0
  6. package/dist/context/interaction_fingerprint.d.ts +27 -0
  7. package/dist/context/interaction_fingerprint.js +519 -0
  8. package/dist/context/interaction_fingerprint_format.d.ts +42 -0
  9. package/dist/context/interaction_fingerprint_format.js +113 -0
  10. package/dist/context/interaction_lease_depth.d.ts +2 -0
  11. package/dist/context/interaction_lease_depth.js +110 -0
  12. package/dist/context/lease_history.d.ts +7 -0
  13. package/dist/context/lease_history.js +158 -0
  14. package/dist/context/pricing.d.ts +4 -0
  15. package/dist/context/pricing.js +27 -0
  16. package/dist/context/provider_prompt_accounting.d.ts +14 -0
  17. package/dist/context/provider_prompt_accounting.js +187 -0
  18. package/dist/context/routing_context.d.ts +40 -0
  19. package/dist/context/routing_context.js +211 -0
  20. package/dist/http/auth.d.ts +2 -0
  21. package/dist/http/auth.js +35 -0
  22. package/dist/http/errors.d.ts +19 -0
  23. package/dist/http/errors.js +89 -0
  24. package/dist/http/handler.d.ts +12 -0
  25. package/dist/http/handler.js +88 -0
  26. package/dist/http/http2_server.d.ts +6 -0
  27. package/dist/http/http2_server.js +103 -0
  28. package/dist/http/serve.d.ts +20 -0
  29. package/dist/http/serve.js +78 -0
  30. package/dist/index.d.ts +7 -0
  31. package/dist/index.js +9 -0
  32. package/dist/ingress/anthropic_messages.d.ts +3 -0
  33. package/dist/ingress/anthropic_messages.js +122 -0
  34. package/dist/ingress/anthropic_models.d.ts +1 -0
  35. package/dist/ingress/anthropic_models.js +23 -0
  36. package/dist/ingress/chat_completions.d.ts +2 -0
  37. package/dist/ingress/chat_completions.js +44 -0
  38. package/dist/ingress/claude_code/request_context.d.ts +2 -0
  39. package/dist/ingress/claude_code/request_context.js +31 -0
  40. package/dist/ingress/codex.d.ts +3 -0
  41. package/dist/ingress/codex.js +69 -0
  42. package/dist/ingress/codex_models.d.ts +2 -0
  43. package/dist/ingress/codex_models.js +89 -0
  44. package/dist/ingress/json.d.ts +1 -0
  45. package/dist/ingress/json.js +95 -0
  46. package/dist/ingress/models.d.ts +1 -0
  47. package/dist/ingress/models.js +65 -0
  48. package/dist/ingress/muse_code.d.ts +2 -0
  49. package/dist/ingress/muse_code.js +8 -0
  50. package/dist/ingress/muse_models.d.ts +9 -0
  51. package/dist/ingress/muse_models.js +32 -0
  52. package/dist/interaction/capabilities.d.ts +3 -0
  53. package/dist/interaction/capabilities.js +24 -0
  54. package/dist/interaction/image.d.ts +4 -0
  55. package/dist/interaction/image.js +13 -0
  56. package/dist/interaction/portable_reasoning_state.d.ts +4 -0
  57. package/dist/interaction/portable_reasoning_state.js +18 -0
  58. package/dist/interaction/provider_continuation_state.d.ts +6 -0
  59. package/dist/interaction/provider_continuation_state.js +27 -0
  60. package/dist/interaction/tool_identity.d.ts +10 -0
  61. package/dist/interaction/tool_identity.js +41 -0
  62. package/dist/interaction/types.d.ts +204 -0
  63. package/dist/interaction/types.js +5 -0
  64. package/dist/models/catalog.d.ts +35 -0
  65. package/dist/models/catalog.js +232 -0
  66. package/dist/models/context_limits.d.ts +2 -0
  67. package/dist/models/context_limits.js +7 -0
  68. package/dist/models/execution_provider.d.ts +9 -0
  69. package/dist/models/execution_provider.js +17 -0
  70. package/dist/models/gateways.d.ts +11 -0
  71. package/dist/models/gateways.js +45 -0
  72. package/dist/models/ids.d.ts +1 -0
  73. package/dist/models/ids.js +15 -0
  74. package/dist/models/provider_defaults.d.ts +4 -0
  75. package/dist/models/provider_defaults.js +44 -0
  76. package/dist/models/runtime_overrides.d.ts +7 -0
  77. package/dist/models/runtime_overrides.js +226 -0
  78. package/dist/models/subscriptions.d.ts +8 -0
  79. package/dist/models/subscriptions.js +37 -0
  80. package/dist/observability/errors.d.ts +8 -0
  81. package/dist/observability/errors.js +11 -0
  82. package/dist/platform/platform.d.ts +156 -0
  83. package/dist/platform/platform.js +11 -0
  84. package/dist/platform/routing_contract.d.ts +2 -0
  85. package/dist/platform/routing_contract.js +5 -0
  86. package/dist/protocol/input/anthropic_messages.d.ts +2 -0
  87. package/dist/protocol/input/anthropic_messages.js +494 -0
  88. package/dist/protocol/input/chat_completions.d.ts +4 -0
  89. package/dist/protocol/input/chat_completions.js +214 -0
  90. package/dist/protocol/input/chat_completions_validation.d.ts +2 -0
  91. package/dist/protocol/input/chat_completions_validation.js +258 -0
  92. package/dist/protocol/input/openai_responses.d.ts +2 -0
  93. package/dist/protocol/input/openai_responses.js +666 -0
  94. package/dist/protocol/input/router_model.d.ts +7 -0
  95. package/dist/protocol/input/router_model.js +43 -0
  96. package/dist/protocol/output/anthropic_messages.d.ts +10 -0
  97. package/dist/protocol/output/anthropic_messages.js +15 -0
  98. package/dist/protocol/output/anthropic_messages_output.d.ts +10 -0
  99. package/dist/protocol/output/anthropic_messages_output.js +124 -0
  100. package/dist/protocol/output/anthropic_messages_stream.d.ts +16 -0
  101. package/dist/protocol/output/anthropic_messages_stream.js +267 -0
  102. package/dist/protocol/output/chat_completions.d.ts +29 -0
  103. package/dist/protocol/output/chat_completions.js +406 -0
  104. package/dist/protocol/output/chat_reasoning_detail.d.ts +2 -0
  105. package/dist/protocol/output/chat_reasoning_detail.js +13 -0
  106. package/dist/protocol/output/delivered_stream_snapshot.d.ts +10 -0
  107. package/dist/protocol/output/delivered_stream_snapshot.js +31 -0
  108. package/dist/protocol/output/openai_responses.d.ts +10 -0
  109. package/dist/protocol/output/openai_responses.js +14 -0
  110. package/dist/protocol/output/openai_responses_output.d.ts +14 -0
  111. package/dist/protocol/output/openai_responses_output.js +125 -0
  112. package/dist/protocol/output/openai_responses_stream.d.ts +18 -0
  113. package/dist/protocol/output/openai_responses_stream.js +546 -0
  114. package/dist/protocol/output/sse_stream.d.ts +31 -0
  115. package/dist/protocol/output/sse_stream.js +67 -0
  116. package/dist/providers/anthropic_safeguards.d.ts +11 -0
  117. package/dist/providers/anthropic_safeguards.js +81 -0
  118. package/dist/providers/aws_assume_role.d.ts +17 -0
  119. package/dist/providers/aws_assume_role.js +107 -0
  120. package/dist/providers/cancel_estimate.d.ts +43 -0
  121. package/dist/providers/cancel_estimate.js +89 -0
  122. package/dist/providers/execution.d.ts +72 -0
  123. package/dist/providers/execution.js +491 -0
  124. package/dist/providers/model_capabilities.d.ts +11 -0
  125. package/dist/providers/model_capabilities.js +29 -0
  126. package/dist/providers/pi.d.ts +65 -0
  127. package/dist/providers/pi.js +325 -0
  128. package/dist/providers/pi_context.d.ts +7 -0
  129. package/dist/providers/pi_context.js +405 -0
  130. package/dist/providers/pi_options.d.ts +11 -0
  131. package/dist/providers/pi_options.js +410 -0
  132. package/dist/providers/pi_output.d.ts +2 -0
  133. package/dist/providers/pi_output.js +453 -0
  134. package/dist/providers/provider_attempt_runner.d.ts +55 -0
  135. package/dist/providers/provider_attempt_runner.js +90 -0
  136. package/dist/providers/provider_call.d.ts +77 -0
  137. package/dist/providers/provider_call.js +296 -0
  138. package/dist/providers/provider_reported_cost.d.ts +10 -0
  139. package/dist/providers/provider_reported_cost.js +123 -0
  140. package/dist/providers/reasoning.d.ts +12 -0
  141. package/dist/providers/reasoning.js +30 -0
  142. package/dist/providers/response_capture.d.ts +23 -0
  143. package/dist/providers/response_capture.js +47 -0
  144. package/dist/providers/stream_finalization.d.ts +46 -0
  145. package/dist/providers/stream_finalization.js +165 -0
  146. package/dist/providers/subscription_limits.d.ts +33 -0
  147. package/dist/providers/subscription_limits.js +179 -0
  148. package/dist/providers/subscription_usage.d.ts +10 -0
  149. package/dist/providers/subscription_usage.js +79 -0
  150. package/dist/runtime/claude_code/auxiliary_routing.d.ts +3 -0
  151. package/dist/runtime/claude_code/auxiliary_routing.js +60 -0
  152. package/dist/runtime/customer_disconnect.d.ts +20 -0
  153. package/dist/runtime/customer_disconnect.js +49 -0
  154. package/dist/runtime/explicit_model.d.ts +7 -0
  155. package/dist/runtime/explicit_model.js +57 -0
  156. package/dist/runtime/harness_identity/conversation.d.ts +33 -0
  157. package/dist/runtime/harness_identity/conversation.js +39 -0
  158. package/dist/runtime/harness_identity/extract.d.ts +8 -0
  159. package/dist/runtime/harness_identity/extract.js +214 -0
  160. package/dist/runtime/harness_identity/index.d.ts +2 -0
  161. package/dist/runtime/harness_identity/index.js +2 -0
  162. package/dist/runtime/prepare_routed_completion.d.ts +33 -0
  163. package/dist/runtime/prepare_routed_completion.js +186 -0
  164. package/dist/runtime/provider_stream_lifecycle.d.ts +31 -0
  165. package/dist/runtime/provider_stream_lifecycle.js +88 -0
  166. package/dist/runtime/provider_stream_outcome.d.ts +23 -0
  167. package/dist/runtime/provider_stream_outcome.js +55 -0
  168. package/dist/runtime/request_tags.d.ts +7 -0
  169. package/dist/runtime/request_tags.js +33 -0
  170. package/dist/runtime/routed_completion.d.ts +17 -0
  171. package/dist/runtime/routed_completion.js +80 -0
  172. package/dist/runtime/routed_protocol.d.ts +30 -0
  173. package/dist/runtime/routed_protocol.js +1 -0
  174. package/dist/runtime/routed_runtime.d.ts +40 -0
  175. package/dist/runtime/routed_runtime.js +535 -0
  176. package/dist/runtime/routing_lease.d.ts +26 -0
  177. package/dist/runtime/routing_lease.js +210 -0
  178. package/dist/runtime/runtime_reporter.d.ts +71 -0
  179. package/dist/runtime/runtime_reporter.js +35 -0
  180. package/dist/runtime/strict_provider_access.d.ts +6 -0
  181. package/dist/runtime/strict_provider_access.js +40 -0
  182. package/dist/self_host/config.d.ts +46 -0
  183. package/dist/self_host/config.js +105 -0
  184. package/dist/self_host/main.d.ts +16 -0
  185. package/dist/self_host/main.js +95 -0
  186. package/dist/self_host/platform.d.ts +13 -0
  187. package/dist/self_host/platform.js +50 -0
  188. package/dist/self_host/prefix_store.d.ts +46 -0
  189. package/dist/self_host/prefix_store.js +261 -0
  190. package/dist/self_host/resolve.d.ts +28 -0
  191. package/dist/self_host/resolve.js +167 -0
  192. package/dist/self_host/subscriptions/access.d.ts +10 -0
  193. package/dist/self_host/subscriptions/access.js +37 -0
  194. package/dist/self_host/subscriptions/http.d.ts +15 -0
  195. package/dist/self_host/subscriptions/http.js +187 -0
  196. package/dist/self_host/subscriptions/index.d.ts +24 -0
  197. package/dist/self_host/subscriptions/index.js +24 -0
  198. package/dist/self_host/subscriptions/oauth.d.ts +59 -0
  199. package/dist/self_host/subscriptions/oauth.js +376 -0
  200. package/dist/self_host/subscriptions/store.d.ts +78 -0
  201. package/dist/self_host/subscriptions/store.js +236 -0
  202. package/dist/strategies/auto_routing_selector.d.ts +12 -0
  203. package/dist/strategies/auto_routing_selector.js +61 -0
  204. package/dist/strategies/eval_relevance.d.ts +17 -0
  205. package/dist/strategies/eval_relevance.js +32 -0
  206. package/dist/strategies/fallback_decision.d.ts +8 -0
  207. package/dist/strategies/fallback_decision.js +17 -0
  208. package/dist/strategies/priority_selector.d.ts +7 -0
  209. package/dist/strategies/priority_selector.js +37 -0
  210. package/dist/strategies/route_decision.d.ts +22 -0
  211. package/dist/strategies/route_decision.js +65 -0
  212. package/dist/strategies/routing.d.ts +74 -0
  213. package/dist/strategies/routing.js +280 -0
  214. package/dist/strategies/selector_projection.d.ts +15 -0
  215. package/dist/strategies/selector_projection.js +161 -0
  216. package/dist/telemetry/background.d.ts +7 -0
  217. package/dist/telemetry/background.js +33 -0
  218. package/dist/telemetry/benchmark_replay.d.ts +37 -0
  219. package/dist/telemetry/benchmark_replay.js +108 -0
  220. package/dist/telemetry/billing_finalization.d.ts +64 -0
  221. package/dist/telemetry/billing_finalization.js +96 -0
  222. package/dist/telemetry/ingest_values.d.ts +4 -0
  223. package/dist/telemetry/ingest_values.js +12 -0
  224. package/dist/telemetry/model_cost.d.ts +3 -0
  225. package/dist/telemetry/model_cost.js +15 -0
  226. package/dist/telemetry/prefix_state.d.ts +47 -0
  227. package/dist/telemetry/prefix_state.js +116 -0
  228. package/dist/telemetry/report.d.ts +88 -0
  229. package/dist/telemetry/report.js +621 -0
  230. package/dist/telemetry/requests/interaction_observability.d.ts +45 -0
  231. package/dist/telemetry/requests/interaction_observability.js +180 -0
  232. package/dist/telemetry/requests/retained_interaction_request.d.ts +8 -0
  233. package/dist/telemetry/requests/retained_interaction_request.js +10 -0
  234. package/dist/telemetry/scrub.d.ts +2 -0
  235. package/dist/telemetry/scrub.js +31 -0
  236. package/dist/telemetry/skills/claude/catalog.d.ts +6 -0
  237. package/dist/telemetry/skills/claude/catalog.js +99 -0
  238. package/dist/telemetry/skills/claude/file_reads.d.ts +4 -0
  239. package/dist/telemetry/skills/claude/file_reads.js +110 -0
  240. package/dist/telemetry/skills/claude/observations.d.ts +3 -0
  241. package/dist/telemetry/skills/claude/observations.js +366 -0
  242. package/dist/telemetry/skills/codex/catalog.d.ts +11 -0
  243. package/dist/telemetry/skills/codex/catalog.js +138 -0
  244. package/dist/telemetry/skills/codex/exec_file_reads.d.ts +9 -0
  245. package/dist/telemetry/skills/codex/exec_file_reads.js +36 -0
  246. package/dist/telemetry/skills/codex/explicit.d.ts +3 -0
  247. package/dist/telemetry/skills/codex/explicit.js +90 -0
  248. package/dist/telemetry/skills/codex/file_reads.d.ts +4 -0
  249. package/dist/telemetry/skills/codex/file_reads.js +37 -0
  250. package/dist/telemetry/skills/codex/identity.d.ts +2 -0
  251. package/dist/telemetry/skills/codex/identity.js +20 -0
  252. package/dist/telemetry/skills/observations.d.ts +21 -0
  253. package/dist/telemetry/skills/observations.js +41 -0
  254. package/dist/telemetry/skills/paths.d.ts +11 -0
  255. package/dist/telemetry/skills/paths.js +61 -0
  256. package/dist/telemetry/skills/pi.d.ts +6 -0
  257. package/dist/telemetry/skills/pi.js +223 -0
  258. package/dist/telemetry/skills/shell_file_reads.d.ts +7 -0
  259. package/dist/telemetry/skills/shell_file_reads.js +226 -0
  260. package/dist/telemetry/skills/shell_words.d.ts +13 -0
  261. package/dist/telemetry/skills/shell_words.js +189 -0
  262. package/dist/telemetry/tools/input.d.ts +2 -0
  263. package/dist/telemetry/tools/input.js +14 -0
  264. package/dist/telemetry/tools/observations.d.ts +29 -0
  265. package/dist/telemetry/tools/observations.js +247 -0
  266. package/dist/telemetry/usage.d.ts +57 -0
  267. package/dist/telemetry/usage.js +136 -0
  268. package/dist/types.d.ts +97 -0
  269. package/dist/types.js +1 -0
  270. package/package.json +22 -7
  271. package/src/bun.ts +2 -2
  272. package/src/context/eval_catalog.ts +1 -1
  273. package/src/context/interaction_fingerprint.ts +9 -9
  274. package/src/context/interaction_fingerprint_format.ts +1 -1
  275. package/src/context/interaction_lease_depth.ts +1 -1
  276. package/src/context/pricing.ts +6 -3
  277. package/src/context/provider_prompt_accounting.ts +5 -5
  278. package/src/context/routing_context.ts +16 -16
  279. package/src/http/auth.ts +1 -1
  280. package/src/http/handler.ts +15 -8
  281. package/src/http/serve.ts +3 -3
  282. package/src/index.ts +14 -6
  283. package/src/ingress/anthropic_messages.ts +12 -12
  284. package/src/ingress/anthropic_models.ts +1 -1
  285. package/src/ingress/chat_completions.ts +17 -10
  286. package/src/ingress/claude_code/request_context.ts +2 -2
  287. package/src/ingress/codex.ts +14 -10
  288. package/src/ingress/codex_models.ts +4 -4
  289. package/src/ingress/json.ts +1 -1
  290. package/src/ingress/models.ts +22 -10
  291. package/src/ingress/muse_code.ts +9 -0
  292. package/src/ingress/muse_models.ts +36 -0
  293. package/src/interaction/capabilities.ts +1 -1
  294. package/src/interaction/portable_reasoning_state.ts +1 -1
  295. package/src/interaction/provider_continuation_state.ts +1 -1
  296. package/src/interaction/tool_identity.ts +2 -2
  297. package/src/interaction/types.ts +2 -0
  298. package/src/models/catalog.ts +310 -0
  299. package/src/models/context_limits.ts +1 -1
  300. package/src/models/execution_provider.ts +5 -2
  301. package/src/models/ids.ts +1 -1
  302. package/src/models/provider_defaults.ts +4 -1
  303. package/src/models/runtime_overrides.ts +23 -1
  304. package/src/models/subscriptions.ts +54 -0
  305. package/src/observability/errors.ts +2 -2
  306. package/src/platform/platform.ts +9 -5
  307. package/src/protocol/input/anthropic_messages.ts +7 -7
  308. package/src/protocol/input/chat_completions.ts +8 -6
  309. package/src/protocol/input/chat_completions_validation.ts +6 -5
  310. package/src/protocol/input/openai_responses.ts +6 -6
  311. package/src/protocol/output/anthropic_messages.ts +3 -3
  312. package/src/protocol/output/anthropic_messages_output.ts +6 -4
  313. package/src/protocol/output/anthropic_messages_stream.ts +7 -7
  314. package/src/protocol/output/chat_completions.ts +13 -13
  315. package/src/protocol/output/chat_reasoning_detail.ts +1 -1
  316. package/src/protocol/output/openai_responses.ts +3 -3
  317. package/src/protocol/output/openai_responses_output.ts +4 -2
  318. package/src/protocol/output/openai_responses_stream.ts +6 -6
  319. package/src/protocol/output/sse_stream.ts +3 -3
  320. package/src/providers/anthropic_safeguards.ts +2 -2
  321. package/src/providers/aws_assume_role.ts +2 -2
  322. package/src/providers/cancel_estimate.ts +1 -1
  323. package/src/providers/execution.ts +17 -17
  324. package/src/providers/pi.ts +34 -19
  325. package/src/providers/pi_context.ts +6 -6
  326. package/src/providers/pi_options.ts +27 -11
  327. package/src/providers/pi_output.ts +5 -5
  328. package/src/providers/provider_attempt_runner.ts +1 -1
  329. package/src/providers/provider_call.ts +21 -17
  330. package/src/providers/provider_reported_cost.ts +3 -3
  331. package/src/providers/reasoning.ts +2 -2
  332. package/src/providers/stream_finalization.ts +9 -9
  333. package/src/providers/subscription_limits.ts +24 -7
  334. package/src/runtime/claude_code/auxiliary_routing.ts +5 -5
  335. package/src/runtime/customer_disconnect.ts +1 -1
  336. package/src/runtime/explicit_model.ts +19 -20
  337. package/src/runtime/harness_identity/conversation.ts +1 -1
  338. package/src/runtime/harness_identity/extract.ts +3 -3
  339. package/src/runtime/harness_identity/index.ts +2 -2
  340. package/src/runtime/prepare_routed_completion.ts +35 -16
  341. package/src/runtime/provider_stream_lifecycle.ts +1 -1
  342. package/src/runtime/provider_stream_outcome.ts +2 -2
  343. package/src/runtime/request_tags.ts +2 -2
  344. package/src/runtime/routed_completion.ts +11 -11
  345. package/src/runtime/routed_protocol.ts +2 -2
  346. package/src/runtime/routed_runtime.ts +20 -19
  347. package/src/runtime/routing_lease.ts +7 -7
  348. package/src/runtime/runtime_reporter.ts +3 -3
  349. package/src/runtime/strict_provider_access.ts +42 -0
  350. package/src/self_host/main.ts +12 -12
  351. package/src/self_host/platform.ts +5 -5
  352. package/src/self_host/prefix_store.ts +2 -2
  353. package/src/self_host/resolve.ts +17 -31
  354. package/src/self_host/subscriptions/access.ts +2 -2
  355. package/src/self_host/subscriptions/http.ts +2 -2
  356. package/src/self_host/subscriptions/index.ts +4 -4
  357. package/src/self_host/subscriptions/oauth.ts +139 -8
  358. package/src/self_host/subscriptions/store.ts +2 -1
  359. package/src/strategies/auto_routing_selector.ts +2 -2
  360. package/src/strategies/eval_relevance.ts +3 -3
  361. package/src/strategies/priority_selector.ts +1 -1
  362. package/src/strategies/route_decision.ts +7 -7
  363. package/src/strategies/routing.ts +10 -10
  364. package/src/strategies/selector_projection.ts +2 -2
  365. package/src/telemetry/billing_finalization.ts +3 -3
  366. package/src/telemetry/model_cost.ts +1 -1
  367. package/src/telemetry/prefix_state.ts +4 -4
  368. package/src/telemetry/report.ts +21 -21
  369. package/src/telemetry/requests/interaction_observability.ts +1 -1
  370. package/src/telemetry/requests/retained_interaction_request.ts +1 -1
  371. package/src/telemetry/skills/claude/catalog.ts +1 -1
  372. package/src/telemetry/skills/claude/file_reads.ts +6 -6
  373. package/src/telemetry/skills/claude/observations.ts +3 -3
  374. package/src/telemetry/skills/codex/catalog.ts +3 -3
  375. package/src/telemetry/skills/codex/exec_file_reads.ts +4 -4
  376. package/src/telemetry/skills/codex/explicit.ts +3 -3
  377. package/src/telemetry/skills/codex/file_reads.ts +5 -5
  378. package/src/telemetry/skills/observations.ts +7 -7
  379. package/src/telemetry/skills/pi.ts +3 -3
  380. package/src/telemetry/skills/shell_file_reads.ts +2 -2
  381. package/src/telemetry/skills/shell_words.ts +1 -1
  382. package/src/telemetry/tools/input.ts +1 -1
  383. package/src/telemetry/tools/observations.ts +9 -9
  384. package/src/telemetry/usage.ts +5 -5
  385. package/src/types.ts +20 -3
@@ -0,0 +1,189 @@
1
+ import { posix, win32 } from "node:path";
2
+ const MAX_COMMAND_TOKENS = 4_096;
3
+ const SHELL_ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=/u;
4
+ const POSIX_SHELL_WHITESPACE = /[\t\n\v\f\r ]/u;
5
+ const POSIX_CONTROL_COMMANDS = new Set([
6
+ "!",
7
+ "[[",
8
+ "case",
9
+ "do",
10
+ "done",
11
+ "elif",
12
+ "else",
13
+ "esac",
14
+ "fi",
15
+ "for",
16
+ "function",
17
+ "if",
18
+ "select",
19
+ "then",
20
+ "time",
21
+ "until",
22
+ "while",
23
+ "{",
24
+ "}",
25
+ ]);
26
+ const TRUSTED_POSIX_EXECUTABLE_DIRECTORIES = new Set([
27
+ "/bin",
28
+ "/opt/homebrew/bin",
29
+ "/usr/bin",
30
+ "/usr/local/bin",
31
+ ]);
32
+ export function executableCommand(words) {
33
+ let index = 0;
34
+ while (index < words.length && SHELL_ASSIGNMENT.test(words[index]))
35
+ index += 1;
36
+ if (words[index] === "env") {
37
+ index += 1;
38
+ while (index < words.length
39
+ && (words[index].startsWith("-") || SHELL_ASSIGNMENT.test(words[index])))
40
+ index += 1;
41
+ }
42
+ if (words[index] === "command" || words[index] === "builtin") {
43
+ index += 1;
44
+ while (index < words.length && words[index].startsWith("-"))
45
+ index += 1;
46
+ }
47
+ const executable = words[index];
48
+ return executable === undefined
49
+ ? undefined
50
+ : { executable, args: words.slice(index + 1) };
51
+ }
52
+ // This lexer recognizes only the shell structure needed to identify direct
53
+ // read commands. Unsupported substitutions and control flow fail closed.
54
+ export function restrictedShellSegments(command, platform) {
55
+ const powershell = platform === "windows";
56
+ if ((!powershell && (command.includes("$(") || command.includes("`"))) || command.includes("\u0000")) {
57
+ return undefined;
58
+ }
59
+ const segments = [];
60
+ let words = [];
61
+ let word = "";
62
+ let wordStarted = false;
63
+ let quote = null;
64
+ let tokenCount = 0;
65
+ const flushWord = () => {
66
+ if (!wordStarted)
67
+ return;
68
+ words.push(word);
69
+ tokenCount += 1;
70
+ word = "";
71
+ wordStarted = false;
72
+ };
73
+ const flushSegment = (next) => {
74
+ flushWord();
75
+ if (words.length > 0)
76
+ segments.push({ words, next });
77
+ words = [];
78
+ };
79
+ for (let index = 0; index < command.length; index += 1) {
80
+ const character = command[index];
81
+ if (quote !== null) {
82
+ if (character === quote) {
83
+ quote = null;
84
+ }
85
+ else if (powershell && character === "`") {
86
+ index += 1;
87
+ if (index >= command.length)
88
+ return undefined;
89
+ word += command[index];
90
+ }
91
+ else if (!powershell && character === "\\" && quote === '"') {
92
+ const escaped = command[index + 1];
93
+ if (escaped === undefined)
94
+ return undefined;
95
+ if (["$", "`", '"', "\\", "\n"].includes(escaped)) {
96
+ index += 1;
97
+ if (escaped !== "\n")
98
+ word += escaped;
99
+ }
100
+ else {
101
+ word += character;
102
+ }
103
+ }
104
+ else {
105
+ word += character;
106
+ }
107
+ wordStarted = true;
108
+ continue;
109
+ }
110
+ if (character === "'" || character === '"') {
111
+ quote = character;
112
+ wordStarted = true;
113
+ continue;
114
+ }
115
+ if ((!powershell && character === "\\") || (powershell && character === "`")) {
116
+ index += 1;
117
+ if (index >= command.length)
118
+ return undefined;
119
+ if (command[index] !== "\n") {
120
+ word += command[index];
121
+ wordStarted = true;
122
+ }
123
+ continue;
124
+ }
125
+ if (POSIX_SHELL_WHITESPACE.test(character)) {
126
+ flushWord();
127
+ if (character === "\n")
128
+ flushSegment("\n");
129
+ continue;
130
+ }
131
+ if (character === "#" && !wordStarted) {
132
+ flushWord();
133
+ while (index + 1 < command.length && command[index + 1] !== "\n")
134
+ index += 1;
135
+ continue;
136
+ }
137
+ if (character === ";" || character === "|" || character === "&") {
138
+ const doubled = command[index + 1] === character;
139
+ const operator = (doubled ? character + character : character);
140
+ if ((character === "|" || character === "&") && doubled)
141
+ index += 1;
142
+ flushSegment(operator);
143
+ continue;
144
+ }
145
+ if (character === "("
146
+ || character === ")"
147
+ || character === "<"
148
+ || character === ">")
149
+ return undefined;
150
+ word += character;
151
+ wordStarted = true;
152
+ }
153
+ if (quote !== null)
154
+ return undefined;
155
+ flushWord();
156
+ if (words.length > 0)
157
+ segments.push({ words, next: null });
158
+ if (tokenCount > MAX_COMMAND_TOKENS
159
+ || segments.length > MAX_COMMAND_TOKENS
160
+ || (platform === "posix" && segments.some((segment) => {
161
+ const parsed = executableCommand(segment.words);
162
+ return parsed !== undefined
163
+ && POSIX_CONTROL_COMMANDS.has(parsed.executable);
164
+ })))
165
+ return undefined;
166
+ return segments;
167
+ }
168
+ export function trustedExecutableName(command, platform) {
169
+ if (!command.includes("/") && !command.includes("\\")) {
170
+ return command.toLowerCase();
171
+ }
172
+ if (platform === "windows") {
173
+ if (!win32.isAbsolute(command))
174
+ return undefined;
175
+ const normalized = win32.normalize(command);
176
+ const directory = win32.dirname(normalized).toLowerCase();
177
+ if (directory !== "c:\\windows\\system32"
178
+ && directory !== "c:\\program files\\powershell\\7")
179
+ return undefined;
180
+ return win32.basename(normalized).replace(/\.exe$/iu, "").toLowerCase();
181
+ }
182
+ if (!posix.isAbsolute(command))
183
+ return undefined;
184
+ const normalized = posix.normalize(command);
185
+ if (!TRUSTED_POSIX_EXECUTABLE_DIRECTORIES.has(posix.dirname(normalized))) {
186
+ return undefined;
187
+ }
188
+ return posix.basename(normalized).toLowerCase();
189
+ }
@@ -0,0 +1,2 @@
1
+ import type { InteractionToolCall } from "../../interaction/types.js";
2
+ export declare function interactionToolInput(item: InteractionToolCall): Record<string, unknown> | undefined;
@@ -0,0 +1,14 @@
1
+ import { isRecord } from "@mupt-ai/dari-router/policy-engine";
2
+ export function interactionToolInput(item) {
3
+ if (isRecord(item.input))
4
+ return item.input;
5
+ if (typeof item.input !== "string")
6
+ return undefined;
7
+ try {
8
+ const parsed = JSON.parse(item.input);
9
+ return isRecord(parsed) ? parsed : undefined;
10
+ }
11
+ catch {
12
+ return undefined;
13
+ }
14
+ }
@@ -0,0 +1,29 @@
1
+ import type { InteractionOutput, InteractionRequest } from "../../interaction/types.js";
2
+ import type { HarnessConversationIdentity } from "../../runtime/harness_identity/index.js";
3
+ export type ToolSkillEventReport = {
4
+ event_type: "tool_call" | "skill_use" | "skill_load";
5
+ tool_call_id: string | null;
6
+ exchange_context_hash: string | null;
7
+ capability_id: string;
8
+ observed_name: string;
9
+ skill_version: null;
10
+ evidence_source: "model_tool_call" | "skill_content_injection" | "skill_command_expansion" | "skill_tool_activation" | "skill_file_read";
11
+ ordinal: number;
12
+ };
13
+ export type ToolResultObservationReport = {
14
+ tool_call_id: string;
15
+ exchange_context_hash: string | null;
16
+ ordinal: number;
17
+ };
18
+ export declare function toolSkillEvents(args: {
19
+ routerId: string;
20
+ request: InteractionRequest;
21
+ output: InteractionOutput | undefined;
22
+ harnessConversationIdentity?: HarnessConversationIdentity;
23
+ zdr: boolean;
24
+ }): ToolSkillEventReport[];
25
+ export declare function toolResultObservations(args: {
26
+ routerId: string;
27
+ request: InteractionRequest;
28
+ zdr: boolean;
29
+ }): ToolResultObservationReport[];
@@ -0,0 +1,247 @@
1
+ import { isRecord, stableStringify } from "@mupt-ai/dari-router/policy-engine";
2
+ import { interactionPrefixChain } from "../../context/interaction_fingerprint.js";
3
+ import { isProtocolToolCallId } from "../../interaction/tool_identity.js";
4
+ import { anthropicMessagesToInteraction } from "../../protocol/input/anthropic_messages.js";
5
+ import { internalChatCompletionsToInteraction } from "../../protocol/input/chat_completions.js";
6
+ import { anthropicContent } from "../../protocol/output/anthropic_messages_output.js";
7
+ import { assistantMessagePayload } from "../../protocol/output/chat_completions.js";
8
+ import { inputSkillObservations, outputSkillObservations, } from "../skills/observations.js";
9
+ const MAX_TOOL_SKILL_EVENTS = 1000;
10
+ export function toolSkillEvents(args) {
11
+ if (args.zdr)
12
+ return [];
13
+ const outputEvents = (args.output?.items ?? [])
14
+ .flatMap((item, ordinal) => item.type === "tool_call"
15
+ && isProtocolToolCallId(item.callId)
16
+ ? [{ item, ordinal }]
17
+ : [])
18
+ .slice(0, MAX_TOOL_SKILL_EVENTS);
19
+ const explicitSkills = inputSkillObservations({
20
+ ...args,
21
+ limit: MAX_TOOL_SKILL_EVENTS - outputEvents.length,
22
+ });
23
+ const outputCallIds = new Set(outputEvents.map(({ item }) => item.callId));
24
+ const automaticSkills = args.output === undefined
25
+ ? []
26
+ : outputSkillObservations({
27
+ request: args.request,
28
+ output: args.output,
29
+ harnessConversationIdentity: args.harnessConversationIdentity,
30
+ limit: MAX_TOOL_SKILL_EVENTS,
31
+ }).filter((skill) => skill.toolCallId !== null
32
+ && outputCallIds.has(skill.toolCallId)).slice(0, MAX_TOOL_SKILL_EVENTS - outputEvents.length - explicitSkills.length);
33
+ if (explicitSkills.length === 0
34
+ && automaticSkills.length === 0
35
+ && outputEvents.length === 0)
36
+ return [];
37
+ const exchangeContextHash = outputEvents.length === 0 || args.output === undefined
38
+ ? null
39
+ : outputExchangeContextHash(args.routerId, args.request, args.output);
40
+ const reportedExplicitSkills = explicitSkills.map((skill, ordinal) => ({
41
+ event_type: skill.eventType,
42
+ tool_call_id: skill.toolCallId,
43
+ exchange_context_hash: null,
44
+ capability_id: skill.capabilityId,
45
+ observed_name: skill.observedName,
46
+ skill_version: null,
47
+ evidence_source: skill.evidenceSource,
48
+ ordinal,
49
+ }));
50
+ const reportedAutomaticSkills = automaticSkills.map((skill, ordinal) => ({
51
+ event_type: skill.eventType,
52
+ tool_call_id: skill.toolCallId,
53
+ exchange_context_hash: exchangeContextHash,
54
+ capability_id: skill.capabilityId,
55
+ observed_name: skill.observedName,
56
+ skill_version: null,
57
+ evidence_source: skill.evidenceSource,
58
+ ordinal: explicitSkills.length + ordinal,
59
+ }));
60
+ const skillCount = explicitSkills.length + automaticSkills.length;
61
+ const reportedOutputEvents = outputEvents.map(({ item, ordinal }) => {
62
+ const observedName = item.namespace
63
+ ? `${item.namespace}.${item.name}`
64
+ : item.name;
65
+ return {
66
+ event_type: "tool_call",
67
+ tool_call_id: item.callId,
68
+ exchange_context_hash: exchangeContextHash,
69
+ capability_id: observedName,
70
+ observed_name: observedName,
71
+ skill_version: null,
72
+ evidence_source: "model_tool_call",
73
+ ordinal: skillCount + ordinal,
74
+ };
75
+ });
76
+ return [
77
+ ...reportedExplicitSkills,
78
+ ...reportedAutomaticSkills,
79
+ ...reportedOutputEvents,
80
+ ];
81
+ }
82
+ export function toolResultObservations(args) {
83
+ if (args.zdr)
84
+ return [];
85
+ const submitted = newlySubmittedToolResults(args.request);
86
+ if (submitted.results.length === 0)
87
+ return [];
88
+ const requestBeforeResults = {
89
+ ...args.request,
90
+ items: args.request.items.slice(0, submitted.contextEnd),
91
+ };
92
+ const exchangeContextHash = interactionPrefixChain(args.routerId, normalizeExchangeRequest(requestBeforeResults), null).at(-1) ??
93
+ null;
94
+ return submitted.results.slice(0, 1000).map(({ item, ordinal }) => ({
95
+ tool_call_id: item.callId,
96
+ exchange_context_hash: exchangeContextHash,
97
+ ordinal,
98
+ }));
99
+ }
100
+ function newlySubmittedToolResults(request) {
101
+ if (request.sourceProtocol === "anthropic_messages") {
102
+ return currentAnthropicUserTurnResults(request.items);
103
+ }
104
+ const results = [];
105
+ for (let ordinal = request.items.length - 1; ordinal >= 0; ordinal -= 1) {
106
+ const item = request.items[ordinal];
107
+ if (item.type !== "tool_result")
108
+ break;
109
+ if (isProtocolToolCallId(item.callId))
110
+ results.push({ item, ordinal });
111
+ }
112
+ const ordered = results.reverse();
113
+ return {
114
+ results: ordered,
115
+ contextEnd: ordered[0]?.ordinal ?? request.items.length,
116
+ };
117
+ }
118
+ function currentAnthropicUserTurnResults(items) {
119
+ const results = [];
120
+ let contextEnd = 0;
121
+ for (let ordinal = items.length - 1; ordinal >= 0; ordinal -= 1) {
122
+ const item = items[ordinal];
123
+ if (isAssistantOutputItem(item)) {
124
+ contextEnd = ordinal + 1;
125
+ break;
126
+ }
127
+ if (item.type === "tool_result" && isProtocolToolCallId(item.callId)) {
128
+ results.push({ item, ordinal });
129
+ }
130
+ }
131
+ return { results: results.reverse(), contextEnd };
132
+ }
133
+ function isAssistantOutputItem(item) {
134
+ return item.type === "reasoning"
135
+ || item.type === "tool_call"
136
+ || item.type === "hosted_tool_call"
137
+ || (item.type === "message" && item.role === "assistant");
138
+ }
139
+ function outputExchangeContextHash(routerId, request, output) {
140
+ const outputItems = clientVisibleOutputItems(request.sourceProtocol, output);
141
+ const completedRequest = normalizeExchangeRequest({
142
+ ...request,
143
+ items: [...request.items, ...outputItems],
144
+ });
145
+ return interactionPrefixChain(routerId, completedRequest, null).at(-1) ?? null;
146
+ }
147
+ function clientVisibleOutputItems(sourceProtocol, output) {
148
+ if (sourceProtocol === "openai_chat") {
149
+ return internalChatCompletionsToInteraction({
150
+ model: "dari/routing",
151
+ messages: [assistantMessagePayload(output)],
152
+ }).items;
153
+ }
154
+ if (sourceProtocol === "anthropic_messages") {
155
+ return anthropicMessagesToInteraction({
156
+ model: "dari/routing",
157
+ max_tokens: 1,
158
+ messages: [{ role: "assistant", content: anthropicContent(output) }],
159
+ }).items;
160
+ }
161
+ return output.items;
162
+ }
163
+ function normalizeExchangeRequest(request) {
164
+ // Claude Code may omit thinking blocks when it submits tool results even
165
+ // though those blocks were present in the preceding Anthropic response.
166
+ const items = request.sourceProtocol === "anthropic_messages"
167
+ ? request.items.filter((item) => item.type !== "reasoning")
168
+ : request.items;
169
+ return {
170
+ ...request,
171
+ instructions: undefined,
172
+ tools: [],
173
+ items: items.map(normalizeExchangeItem),
174
+ };
175
+ }
176
+ // Correlation follows semantic tool history, not wire-only item IDs, message
177
+ // phases, content container shapes, or JSON object insertion order.
178
+ function normalizeExchangeItem(item) {
179
+ if (item.type === "reasoning") {
180
+ const { id: _id, ...rest } = item;
181
+ return rest;
182
+ }
183
+ if (item.type === "message") {
184
+ const { id: _id, phase: _phase, ...rest } = item;
185
+ return {
186
+ ...rest,
187
+ ...normalizeContent(item.content, item.contentShape, item.role === "assistant"),
188
+ };
189
+ }
190
+ if (item.type === "tool_call") {
191
+ const { id: _id, ...rest } = item;
192
+ return {
193
+ ...rest,
194
+ input: normalizeFunctionInput(item.kind, item.input),
195
+ };
196
+ }
197
+ if (item.type === "tool_result") {
198
+ const { id: _id, ...rest } = item;
199
+ if (!Array.isArray(item.output))
200
+ return rest;
201
+ const normalized = normalizeContent(item.output, item.outputShape);
202
+ return { ...rest, output: normalized.content, outputShape: normalized.contentShape };
203
+ }
204
+ if (item.type === "hosted_tool_call") {
205
+ const { id: _id, source: _source, payload: { id: _payloadId, status: _payloadStatus, ...payload }, ...rest } = item;
206
+ return {
207
+ ...rest,
208
+ id: "",
209
+ payload: item.status ? { ...payload, status: item.status } : payload,
210
+ };
211
+ }
212
+ return item;
213
+ }
214
+ function normalizeContent(content, contentShape, collapseEmpty = false) {
215
+ if (collapseEmpty
216
+ && content.every((part) => part.type === "text" && part.text.length === 0 && !part.annotations?.length)) {
217
+ return { content: [], contentShape: "null" };
218
+ }
219
+ if (contentShape === "null" || contentShape === "absent") {
220
+ return { content, contentShape };
221
+ }
222
+ if (content.every((part) => part.type === "text" && !part.annotations?.length)) {
223
+ return {
224
+ content: [{
225
+ type: "text",
226
+ text: content
227
+ .map((part) => (part.type === "text" ? part.text : ""))
228
+ .join(""),
229
+ }],
230
+ contentShape: "string",
231
+ };
232
+ }
233
+ return { content, contentShape: "parts" };
234
+ }
235
+ function normalizeFunctionInput(kind, input) {
236
+ if (kind === "custom" || input === undefined)
237
+ return input;
238
+ if (isRecord(input))
239
+ return stableStringify(input);
240
+ try {
241
+ const parsed = JSON.parse(input);
242
+ return isRecord(parsed) ? stableStringify(parsed) : input;
243
+ }
244
+ catch {
245
+ return input;
246
+ }
247
+ }
@@ -0,0 +1,57 @@
1
+ import type { Provider } from "@mupt-ai/dari-router/policy-engine";
2
+ import { HttpError } from "../http/errors.js";
3
+ import { type RequestTags } from "../platform/platform.js";
4
+ import type { ProviderKeySource, ResolvedRouter } from "../types.js";
5
+ export type RoutingUsageContext = {
6
+ organizationId: string;
7
+ routerId: string;
8
+ provider: Provider;
9
+ model: string;
10
+ tags: RequestTags;
11
+ startedAt: Date;
12
+ };
13
+ /**
14
+ * Reject requests that need model selection when the organization is not on
15
+ * the Autorouting plan. Without it, Gateway serves only routers with a single
16
+ * model and thinking level, which never invoke the selector.
17
+ */
18
+ export declare function assertAutoroutingAllowed(credentials: ResolvedRouter, selects: boolean): void;
19
+ /**
20
+ * Whether the router offers more than one model/thinking-level pair. A model
21
+ * without listed levels serves every level it supports, so it counts as
22
+ * selecting, matching candidate preparation and the CLI.
23
+ */
24
+ export declare function routerSelectsModels(credentials: ResolvedRouter): boolean;
25
+ /**
26
+ * Reject billable requests before the routing decision once the org's
27
+ * credits are exhausted, unless a personal subscription serves one of the
28
+ * router's models: those owe no credits, so they keep serving and routing
29
+ * narrows to them. Managed and own-key requests stay gated (own-key requests
30
+ * owe the router fee), and refusing before selection keeps an exhausted org
31
+ * from burning selector-model spend.
32
+ */
33
+ export declare function assertCreditAvailableBeforeRouting(billable: boolean, credentials: ResolvedRouter): void;
34
+ /**
35
+ * Decide whether this routed call bills org credits: returns the usage
36
+ * context for billable managed-key selections and undefined otherwise.
37
+ * Throws 402 when the selection needs a managed key while the org's
38
+ * credits are exhausted.
39
+ */
40
+ export declare function usageContextFor(args: {
41
+ billable: boolean;
42
+ credentials: ResolvedRouter;
43
+ routerId: string;
44
+ selectedModel: string;
45
+ keySource: ProviderKeySource;
46
+ tags: RequestTags;
47
+ }): RoutingUsageContext | undefined;
48
+ /**
49
+ * Bill one managed-key routed call to the org's credits through the
50
+ * platform. Stream billing calls this from the protocol stream's onFinal hook; the
51
+ * non-stream path awaits it before responding. Never throws: the provider
52
+ * spend already happened, so a recording failure must not fail an
53
+ * otherwise-successful completion — it is logged and reported operationally
54
+ * instead.
55
+ */
56
+ export declare function reportRoutingUsage(context: RoutingUsageContext, message: Record<string, unknown>): Promise<void>;
57
+ export declare function creditExhaustedError(): HttpError;
@@ -0,0 +1,136 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { HttpError } from "../http/errors.js";
3
+ import { executionProviderForModel, keySourceForModel } from "../models/execution_provider.js";
4
+ import { captureOperationalError } from "../observability/errors.js";
5
+ import { gatewayPlatform } from "../platform/platform.js";
6
+ /**
7
+ * Reject requests that need model selection when the organization is not on
8
+ * the Autorouting plan. Without it, Gateway serves only routers with a single
9
+ * model and thinking level, which never invoke the selector.
10
+ */
11
+ export function assertAutoroutingAllowed(credentials, selects) {
12
+ if (selects && credentials.autorouting_enabled === false) {
13
+ throw new HttpError(402, "Autorouting requires the Autorouting plan. Subscribe at https://app.dari.dev/usage, or keep one model and thinking level on this router.", "autorouting_plan_required");
14
+ }
15
+ }
16
+ /**
17
+ * Whether the router offers more than one model/thinking-level pair. A model
18
+ * without listed levels serves every level it supports, so it counts as
19
+ * selecting, matching candidate preparation and the CLI.
20
+ */
21
+ export function routerSelectsModels(credentials) {
22
+ let pairs = 0;
23
+ for (const model of credentials.enabled_models) {
24
+ const levels = credentials.model_thinking_levels?.[model]?.length ?? 0;
25
+ if (levels === 0)
26
+ return true;
27
+ pairs += levels;
28
+ }
29
+ return pairs > 1;
30
+ }
31
+ /**
32
+ * Reject billable requests before the routing decision once the org's
33
+ * credits are exhausted, unless a personal subscription serves one of the
34
+ * router's models: those owe no credits, so they keep serving and routing
35
+ * narrows to them. Managed and own-key requests stay gated (own-key requests
36
+ * owe the router fee), and refusing before selection keeps an exhausted org
37
+ * from burning selector-model spend.
38
+ */
39
+ export function assertCreditAvailableBeforeRouting(billable, credentials) {
40
+ if (billable &&
41
+ credentials.credit_exhausted &&
42
+ !credentials.enabled_models.some((model) => keySourceForModel(credentials, model) === "subscription")) {
43
+ throw creditExhaustedError();
44
+ }
45
+ }
46
+ /**
47
+ * Decide whether this routed call bills org credits: returns the usage
48
+ * context for billable managed-key selections and undefined otherwise.
49
+ * Throws 402 when the selection needs a managed key while the org's
50
+ * credits are exhausted.
51
+ */
52
+ export function usageContextFor(args) {
53
+ if (!args.billable || args.keySource !== "managed")
54
+ return undefined;
55
+ if (args.credentials.credit_exhausted)
56
+ throw creditExhaustedError();
57
+ return {
58
+ organizationId: args.credentials.organization_id,
59
+ routerId: args.routerId,
60
+ provider: executionProviderForModel(args.credentials, args.selectedModel),
61
+ model: args.selectedModel,
62
+ tags: args.tags,
63
+ startedAt: new Date(),
64
+ };
65
+ }
66
+ /**
67
+ * Bill one managed-key routed call to the org's credits through the
68
+ * platform. Stream billing calls this from the protocol stream's onFinal hook; the
69
+ * non-stream path awaits it before responding. Never throws: the provider
70
+ * spend already happened, so a recording failure must not fail an
71
+ * otherwise-successful completion — it is logged and reported operationally
72
+ * instead.
73
+ */
74
+ export async function reportRoutingUsage(context, message) {
75
+ const payload = usagePayload(context, message);
76
+ if (payload.total_cost_usd === "0") {
77
+ console.error("Routing usage billed at zero cost; model pricing may be missing.", usageLogFields(payload));
78
+ }
79
+ try {
80
+ await gatewayPlatform().recordUsage(payload);
81
+ }
82
+ catch (error) {
83
+ reportUsageFailure(error, payload);
84
+ }
85
+ }
86
+ function reportUsageFailure(error, payload) {
87
+ console.error("Routing usage not recorded.", usageLogFields(payload));
88
+ captureOperationalError(error, {
89
+ code: "billing_usage_report_failed",
90
+ routerId: typeof payload.router_id === "string" ? payload.router_id : undefined,
91
+ });
92
+ }
93
+ export function creditExhaustedError() {
94
+ return new HttpError(402, "Organization credits are exhausted.", "credit_exhausted");
95
+ }
96
+ function usagePayload(context, message) {
97
+ const usage = isRecord(message.usage) ? message.usage : {};
98
+ const cost = isRecord(usage.cost) ? usage.cost : {};
99
+ const responseId = typeof message.responseId === "string" && message.responseId ? message.responseId : undefined;
100
+ return {
101
+ organization_id: context.organizationId,
102
+ router_id: context.routerId,
103
+ provider: context.provider,
104
+ model: context.model,
105
+ provider_request_id: responseId ?? `routing-${randomUUID()}`,
106
+ input_tokens: tokens(usage.input),
107
+ cached_input_tokens: tokens(usage.cacheRead),
108
+ output_tokens: tokens(usage.output),
109
+ usage_estimated: usage.estimated === true,
110
+ input_cost_usd: usd(cost.input),
111
+ cached_input_cost_usd: usd(cost.cacheRead),
112
+ output_cost_usd: usd(cost.output),
113
+ total_cost_usd: usd(cost.total) ?? "0",
114
+ started_at: context.startedAt.toISOString(),
115
+ ended_at: new Date().toISOString(),
116
+ tags: context.tags,
117
+ };
118
+ }
119
+ function usageLogFields(payload) {
120
+ return {
121
+ organization_id: payload.organization_id,
122
+ router_id: payload.router_id,
123
+ provider_request_id: payload.provider_request_id,
124
+ model: payload.model,
125
+ total_cost_usd: payload.total_cost_usd,
126
+ };
127
+ }
128
+ function tokens(value) {
129
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : 0;
130
+ }
131
+ function usd(value) {
132
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? String(value) : null;
133
+ }
134
+ function isRecord(value) {
135
+ return typeof value === "object" && value !== null && !Array.isArray(value);
136
+ }