@mupt-ai/dari-gateway 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (369) hide show
  1. package/README.md +8 -1
  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 +23 -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 +81 -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 +6 -0
  31. package/dist/index.js +8 -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 +37 -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 +65 -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 +53 -0
  48. package/dist/interaction/capabilities.d.ts +3 -0
  49. package/dist/interaction/capabilities.js +24 -0
  50. package/dist/interaction/image.d.ts +4 -0
  51. package/dist/interaction/image.js +13 -0
  52. package/dist/interaction/portable_reasoning_state.d.ts +4 -0
  53. package/dist/interaction/portable_reasoning_state.js +18 -0
  54. package/dist/interaction/provider_continuation_state.d.ts +6 -0
  55. package/dist/interaction/provider_continuation_state.js +27 -0
  56. package/dist/interaction/tool_identity.d.ts +10 -0
  57. package/dist/interaction/tool_identity.js +41 -0
  58. package/dist/interaction/types.d.ts +203 -0
  59. package/dist/interaction/types.js +5 -0
  60. package/dist/models/context_limits.d.ts +2 -0
  61. package/dist/models/context_limits.js +7 -0
  62. package/dist/models/execution_provider.d.ts +8 -0
  63. package/dist/models/execution_provider.js +13 -0
  64. package/dist/models/gateways.d.ts +11 -0
  65. package/dist/models/gateways.js +45 -0
  66. package/dist/models/ids.d.ts +1 -0
  67. package/dist/models/ids.js +15 -0
  68. package/dist/models/provider_defaults.d.ts +4 -0
  69. package/dist/models/provider_defaults.js +41 -0
  70. package/dist/models/runtime_overrides.d.ts +4 -0
  71. package/dist/models/runtime_overrides.js +209 -0
  72. package/dist/observability/errors.d.ts +8 -0
  73. package/dist/observability/errors.js +11 -0
  74. package/dist/platform/platform.d.ts +152 -0
  75. package/dist/platform/platform.js +11 -0
  76. package/dist/platform/routing_contract.d.ts +2 -0
  77. package/dist/platform/routing_contract.js +5 -0
  78. package/dist/protocol/input/anthropic_messages.d.ts +2 -0
  79. package/dist/protocol/input/anthropic_messages.js +494 -0
  80. package/dist/protocol/input/chat_completions.d.ts +4 -0
  81. package/dist/protocol/input/chat_completions.js +212 -0
  82. package/dist/protocol/input/chat_completions_validation.d.ts +2 -0
  83. package/dist/protocol/input/chat_completions_validation.js +257 -0
  84. package/dist/protocol/input/openai_responses.d.ts +2 -0
  85. package/dist/protocol/input/openai_responses.js +666 -0
  86. package/dist/protocol/input/router_model.d.ts +7 -0
  87. package/dist/protocol/input/router_model.js +43 -0
  88. package/dist/protocol/output/anthropic_messages.d.ts +10 -0
  89. package/dist/protocol/output/anthropic_messages.js +15 -0
  90. package/dist/protocol/output/anthropic_messages_output.d.ts +10 -0
  91. package/dist/protocol/output/anthropic_messages_output.js +124 -0
  92. package/dist/protocol/output/anthropic_messages_stream.d.ts +16 -0
  93. package/dist/protocol/output/anthropic_messages_stream.js +267 -0
  94. package/dist/protocol/output/chat_completions.d.ts +29 -0
  95. package/dist/protocol/output/chat_completions.js +406 -0
  96. package/dist/protocol/output/chat_reasoning_detail.d.ts +2 -0
  97. package/dist/protocol/output/chat_reasoning_detail.js +13 -0
  98. package/dist/protocol/output/delivered_stream_snapshot.d.ts +10 -0
  99. package/dist/protocol/output/delivered_stream_snapshot.js +31 -0
  100. package/dist/protocol/output/openai_responses.d.ts +10 -0
  101. package/dist/protocol/output/openai_responses.js +14 -0
  102. package/dist/protocol/output/openai_responses_output.d.ts +14 -0
  103. package/dist/protocol/output/openai_responses_output.js +125 -0
  104. package/dist/protocol/output/openai_responses_stream.d.ts +18 -0
  105. package/dist/protocol/output/openai_responses_stream.js +546 -0
  106. package/dist/protocol/output/sse_stream.d.ts +31 -0
  107. package/dist/protocol/output/sse_stream.js +67 -0
  108. package/dist/providers/anthropic_safeguards.d.ts +11 -0
  109. package/dist/providers/anthropic_safeguards.js +81 -0
  110. package/dist/providers/aws_assume_role.d.ts +17 -0
  111. package/dist/providers/aws_assume_role.js +107 -0
  112. package/dist/providers/cancel_estimate.d.ts +43 -0
  113. package/dist/providers/cancel_estimate.js +89 -0
  114. package/dist/providers/execution.d.ts +72 -0
  115. package/dist/providers/execution.js +491 -0
  116. package/dist/providers/model_capabilities.d.ts +11 -0
  117. package/dist/providers/model_capabilities.js +29 -0
  118. package/dist/providers/pi.d.ts +65 -0
  119. package/dist/providers/pi.js +319 -0
  120. package/dist/providers/pi_context.d.ts +7 -0
  121. package/dist/providers/pi_context.js +405 -0
  122. package/dist/providers/pi_options.d.ts +11 -0
  123. package/dist/providers/pi_options.js +398 -0
  124. package/dist/providers/pi_output.d.ts +2 -0
  125. package/dist/providers/pi_output.js +453 -0
  126. package/dist/providers/provider_attempt_runner.d.ts +55 -0
  127. package/dist/providers/provider_attempt_runner.js +90 -0
  128. package/dist/providers/provider_call.d.ts +77 -0
  129. package/dist/providers/provider_call.js +292 -0
  130. package/dist/providers/provider_reported_cost.d.ts +10 -0
  131. package/dist/providers/provider_reported_cost.js +123 -0
  132. package/dist/providers/reasoning.d.ts +12 -0
  133. package/dist/providers/reasoning.js +30 -0
  134. package/dist/providers/response_capture.d.ts +23 -0
  135. package/dist/providers/response_capture.js +47 -0
  136. package/dist/providers/stream_finalization.d.ts +46 -0
  137. package/dist/providers/stream_finalization.js +165 -0
  138. package/dist/providers/subscription_limits.d.ts +31 -0
  139. package/dist/providers/subscription_limits.js +163 -0
  140. package/dist/providers/subscription_usage.d.ts +10 -0
  141. package/dist/providers/subscription_usage.js +79 -0
  142. package/dist/runtime/claude_code/auxiliary_routing.d.ts +3 -0
  143. package/dist/runtime/claude_code/auxiliary_routing.js +60 -0
  144. package/dist/runtime/customer_disconnect.d.ts +20 -0
  145. package/dist/runtime/customer_disconnect.js +49 -0
  146. package/dist/runtime/explicit_model.d.ts +4 -0
  147. package/dist/runtime/explicit_model.js +53 -0
  148. package/dist/runtime/harness_identity/conversation.d.ts +33 -0
  149. package/dist/runtime/harness_identity/conversation.js +39 -0
  150. package/dist/runtime/harness_identity/extract.d.ts +8 -0
  151. package/dist/runtime/harness_identity/extract.js +214 -0
  152. package/dist/runtime/harness_identity/index.d.ts +2 -0
  153. package/dist/runtime/harness_identity/index.js +2 -0
  154. package/dist/runtime/prepare_routed_completion.d.ts +33 -0
  155. package/dist/runtime/prepare_routed_completion.js +167 -0
  156. package/dist/runtime/provider_stream_lifecycle.d.ts +31 -0
  157. package/dist/runtime/provider_stream_lifecycle.js +88 -0
  158. package/dist/runtime/provider_stream_outcome.d.ts +23 -0
  159. package/dist/runtime/provider_stream_outcome.js +55 -0
  160. package/dist/runtime/request_tags.d.ts +7 -0
  161. package/dist/runtime/request_tags.js +33 -0
  162. package/dist/runtime/routed_completion.d.ts +17 -0
  163. package/dist/runtime/routed_completion.js +80 -0
  164. package/dist/runtime/routed_protocol.d.ts +30 -0
  165. package/dist/runtime/routed_protocol.js +1 -0
  166. package/dist/runtime/routed_runtime.d.ts +40 -0
  167. package/dist/runtime/routed_runtime.js +535 -0
  168. package/dist/runtime/routing_lease.d.ts +26 -0
  169. package/dist/runtime/routing_lease.js +210 -0
  170. package/dist/runtime/runtime_reporter.d.ts +71 -0
  171. package/dist/runtime/runtime_reporter.js +35 -0
  172. package/dist/self_host/config.d.ts +46 -0
  173. package/dist/self_host/config.js +105 -0
  174. package/dist/self_host/main.d.ts +16 -0
  175. package/dist/self_host/main.js +95 -0
  176. package/dist/self_host/platform.d.ts +13 -0
  177. package/dist/self_host/platform.js +50 -0
  178. package/dist/self_host/prefix_store.d.ts +46 -0
  179. package/dist/self_host/prefix_store.js +261 -0
  180. package/dist/self_host/resolve.d.ts +28 -0
  181. package/dist/self_host/resolve.js +171 -0
  182. package/dist/self_host/subscriptions/access.d.ts +10 -0
  183. package/dist/self_host/subscriptions/access.js +37 -0
  184. package/dist/self_host/subscriptions/http.d.ts +15 -0
  185. package/dist/self_host/subscriptions/http.js +187 -0
  186. package/dist/self_host/subscriptions/index.d.ts +24 -0
  187. package/dist/self_host/subscriptions/index.js +24 -0
  188. package/dist/self_host/subscriptions/oauth.d.ts +55 -0
  189. package/dist/self_host/subscriptions/oauth.js +281 -0
  190. package/dist/self_host/subscriptions/store.d.ts +78 -0
  191. package/dist/self_host/subscriptions/store.js +235 -0
  192. package/dist/strategies/auto_routing_selector.d.ts +12 -0
  193. package/dist/strategies/auto_routing_selector.js +61 -0
  194. package/dist/strategies/eval_relevance.d.ts +17 -0
  195. package/dist/strategies/eval_relevance.js +32 -0
  196. package/dist/strategies/fallback_decision.d.ts +8 -0
  197. package/dist/strategies/fallback_decision.js +17 -0
  198. package/dist/strategies/priority_selector.d.ts +7 -0
  199. package/dist/strategies/priority_selector.js +37 -0
  200. package/dist/strategies/route_decision.d.ts +22 -0
  201. package/dist/strategies/route_decision.js +65 -0
  202. package/dist/strategies/routing.d.ts +74 -0
  203. package/dist/strategies/routing.js +280 -0
  204. package/dist/strategies/selector_projection.d.ts +15 -0
  205. package/dist/strategies/selector_projection.js +161 -0
  206. package/dist/telemetry/background.d.ts +7 -0
  207. package/dist/telemetry/background.js +33 -0
  208. package/dist/telemetry/benchmark_replay.d.ts +37 -0
  209. package/dist/telemetry/benchmark_replay.js +108 -0
  210. package/dist/telemetry/billing_finalization.d.ts +64 -0
  211. package/dist/telemetry/billing_finalization.js +96 -0
  212. package/dist/telemetry/ingest_values.d.ts +4 -0
  213. package/dist/telemetry/ingest_values.js +12 -0
  214. package/dist/telemetry/model_cost.d.ts +3 -0
  215. package/dist/telemetry/model_cost.js +15 -0
  216. package/dist/telemetry/prefix_state.d.ts +47 -0
  217. package/dist/telemetry/prefix_state.js +116 -0
  218. package/dist/telemetry/report.d.ts +88 -0
  219. package/dist/telemetry/report.js +621 -0
  220. package/dist/telemetry/requests/interaction_observability.d.ts +45 -0
  221. package/dist/telemetry/requests/interaction_observability.js +180 -0
  222. package/dist/telemetry/requests/retained_interaction_request.d.ts +8 -0
  223. package/dist/telemetry/requests/retained_interaction_request.js +10 -0
  224. package/dist/telemetry/scrub.d.ts +2 -0
  225. package/dist/telemetry/scrub.js +31 -0
  226. package/dist/telemetry/skills/claude/catalog.d.ts +6 -0
  227. package/dist/telemetry/skills/claude/catalog.js +99 -0
  228. package/dist/telemetry/skills/claude/file_reads.d.ts +4 -0
  229. package/dist/telemetry/skills/claude/file_reads.js +110 -0
  230. package/dist/telemetry/skills/claude/observations.d.ts +3 -0
  231. package/dist/telemetry/skills/claude/observations.js +366 -0
  232. package/dist/telemetry/skills/codex/catalog.d.ts +11 -0
  233. package/dist/telemetry/skills/codex/catalog.js +138 -0
  234. package/dist/telemetry/skills/codex/exec_file_reads.d.ts +9 -0
  235. package/dist/telemetry/skills/codex/exec_file_reads.js +36 -0
  236. package/dist/telemetry/skills/codex/explicit.d.ts +3 -0
  237. package/dist/telemetry/skills/codex/explicit.js +90 -0
  238. package/dist/telemetry/skills/codex/file_reads.d.ts +4 -0
  239. package/dist/telemetry/skills/codex/file_reads.js +37 -0
  240. package/dist/telemetry/skills/codex/identity.d.ts +2 -0
  241. package/dist/telemetry/skills/codex/identity.js +20 -0
  242. package/dist/telemetry/skills/observations.d.ts +21 -0
  243. package/dist/telemetry/skills/observations.js +41 -0
  244. package/dist/telemetry/skills/paths.d.ts +11 -0
  245. package/dist/telemetry/skills/paths.js +61 -0
  246. package/dist/telemetry/skills/pi.d.ts +6 -0
  247. package/dist/telemetry/skills/pi.js +223 -0
  248. package/dist/telemetry/skills/shell_file_reads.d.ts +7 -0
  249. package/dist/telemetry/skills/shell_file_reads.js +226 -0
  250. package/dist/telemetry/skills/shell_words.d.ts +13 -0
  251. package/dist/telemetry/skills/shell_words.js +189 -0
  252. package/dist/telemetry/tools/input.d.ts +2 -0
  253. package/dist/telemetry/tools/input.js +14 -0
  254. package/dist/telemetry/tools/observations.d.ts +29 -0
  255. package/dist/telemetry/tools/observations.js +247 -0
  256. package/dist/telemetry/usage.d.ts +57 -0
  257. package/dist/telemetry/usage.js +136 -0
  258. package/dist/types.d.ts +94 -0
  259. package/dist/types.js +1 -0
  260. package/package.json +22 -5
  261. package/src/bun.ts +4 -0
  262. package/src/context/eval_catalog.ts +1 -1
  263. package/src/context/interaction_fingerprint.ts +9 -9
  264. package/src/context/interaction_fingerprint_format.ts +1 -1
  265. package/src/context/interaction_lease_depth.ts +1 -1
  266. package/src/context/pricing.ts +1 -1
  267. package/src/context/provider_prompt_accounting.ts +5 -5
  268. package/src/context/routing_context.ts +17 -17
  269. package/src/http/auth.ts +1 -1
  270. package/src/http/handler.ts +8 -8
  271. package/src/http/serve.ts +3 -3
  272. package/src/index.ts +10 -9
  273. package/src/ingress/anthropic_messages.ts +12 -12
  274. package/src/ingress/anthropic_models.ts +1 -1
  275. package/src/ingress/chat_completions.ts +9 -9
  276. package/src/ingress/claude_code/request_context.ts +2 -2
  277. package/src/ingress/codex.ts +10 -10
  278. package/src/ingress/codex_models.ts +4 -4
  279. package/src/ingress/json.ts +1 -1
  280. package/src/ingress/models.ts +10 -10
  281. package/src/interaction/capabilities.ts +1 -1
  282. package/src/interaction/portable_reasoning_state.ts +1 -1
  283. package/src/interaction/provider_continuation_state.ts +1 -1
  284. package/src/interaction/tool_identity.ts +2 -2
  285. package/src/interaction/types.ts +3 -0
  286. package/src/models/context_limits.ts +1 -1
  287. package/src/models/execution_provider.ts +1 -1
  288. package/src/models/ids.ts +1 -1
  289. package/src/models/provider_defaults.ts +1 -1
  290. package/src/models/runtime_overrides.ts +1 -1
  291. package/src/observability/errors.ts +2 -2
  292. package/src/platform/platform.ts +36 -4
  293. package/src/protocol/input/anthropic_messages.ts +7 -7
  294. package/src/protocol/input/chat_completions.ts +5 -5
  295. package/src/protocol/input/chat_completions_validation.ts +4 -4
  296. package/src/protocol/input/openai_responses.ts +6 -6
  297. package/src/protocol/output/anthropic_messages.ts +3 -3
  298. package/src/protocol/output/anthropic_messages_output.ts +6 -4
  299. package/src/protocol/output/anthropic_messages_stream.ts +7 -7
  300. package/src/protocol/output/chat_completions.ts +13 -13
  301. package/src/protocol/output/chat_reasoning_detail.ts +1 -1
  302. package/src/protocol/output/openai_responses.ts +3 -3
  303. package/src/protocol/output/openai_responses_output.ts +4 -2
  304. package/src/protocol/output/openai_responses_stream.ts +6 -6
  305. package/src/protocol/output/sse_stream.ts +3 -3
  306. package/src/providers/anthropic_safeguards.ts +2 -2
  307. package/src/providers/aws_assume_role.ts +2 -2
  308. package/src/providers/cancel_estimate.ts +1 -1
  309. package/src/providers/execution.ts +17 -17
  310. package/src/providers/pi.ts +115 -41
  311. package/src/providers/pi_context.ts +6 -6
  312. package/src/providers/pi_options.ts +283 -453
  313. package/src/providers/pi_output.ts +6 -5
  314. package/src/providers/provider_attempt_runner.ts +1 -1
  315. package/src/providers/provider_call.ts +37 -18
  316. package/src/providers/provider_reported_cost.ts +138 -0
  317. package/src/providers/reasoning.ts +2 -2
  318. package/src/providers/stream_finalization.ts +10 -10
  319. package/src/providers/subscription_limits.ts +3 -3
  320. package/src/runtime/claude_code/auxiliary_routing.ts +5 -5
  321. package/src/runtime/customer_disconnect.ts +1 -1
  322. package/src/runtime/explicit_model.ts +31 -4
  323. package/src/runtime/harness_identity/conversation.ts +1 -1
  324. package/src/runtime/harness_identity/extract.ts +3 -3
  325. package/src/runtime/harness_identity/index.ts +2 -2
  326. package/src/runtime/prepare_routed_completion.ts +46 -10
  327. package/src/runtime/provider_stream_lifecycle.ts +1 -1
  328. package/src/runtime/provider_stream_outcome.ts +2 -2
  329. package/src/runtime/request_tags.ts +34 -0
  330. package/src/runtime/routed_completion.ts +13 -11
  331. package/src/runtime/routed_protocol.ts +2 -2
  332. package/src/runtime/routed_runtime.ts +38 -20
  333. package/src/runtime/routing_lease.ts +7 -7
  334. package/src/runtime/runtime_reporter.ts +3 -3
  335. package/src/self_host/main.ts +19 -14
  336. package/src/self_host/platform.ts +5 -5
  337. package/src/self_host/prefix_store.ts +2 -2
  338. package/src/self_host/resolve.ts +11 -11
  339. package/src/self_host/subscriptions/access.ts +2 -2
  340. package/src/self_host/subscriptions/http.ts +2 -2
  341. package/src/self_host/subscriptions/index.ts +4 -4
  342. package/src/self_host/subscriptions/store.ts +1 -1
  343. package/src/strategies/auto_routing_selector.ts +2 -2
  344. package/src/strategies/eval_relevance.ts +3 -3
  345. package/src/strategies/priority_selector.ts +1 -1
  346. package/src/strategies/route_decision.ts +7 -7
  347. package/src/strategies/routing.ts +11 -11
  348. package/src/strategies/selector_projection.ts +2 -2
  349. package/src/telemetry/billing_finalization.ts +3 -3
  350. package/src/telemetry/model_cost.ts +1 -1
  351. package/src/telemetry/prefix_state.ts +4 -4
  352. package/src/telemetry/report.ts +30 -25
  353. package/src/telemetry/requests/interaction_observability.ts +1 -1
  354. package/src/telemetry/requests/retained_interaction_request.ts +1 -1
  355. package/src/telemetry/skills/claude/catalog.ts +1 -1
  356. package/src/telemetry/skills/claude/file_reads.ts +6 -6
  357. package/src/telemetry/skills/claude/observations.ts +3 -3
  358. package/src/telemetry/skills/codex/catalog.ts +3 -3
  359. package/src/telemetry/skills/codex/exec_file_reads.ts +4 -4
  360. package/src/telemetry/skills/codex/explicit.ts +3 -3
  361. package/src/telemetry/skills/codex/file_reads.ts +5 -5
  362. package/src/telemetry/skills/observations.ts +7 -7
  363. package/src/telemetry/skills/pi.ts +3 -3
  364. package/src/telemetry/skills/shell_file_reads.ts +2 -2
  365. package/src/telemetry/skills/shell_words.ts +1 -1
  366. package/src/telemetry/tools/input.ts +1 -1
  367. package/src/telemetry/tools/observations.ts +9 -9
  368. package/src/telemetry/usage.ts +9 -5
  369. package/src/types.ts +15 -0
@@ -1,9 +1,9 @@
1
1
  import { isRecord } from "@mupt-ai/dari-router/policy-engine";
2
2
 
3
- import { HttpError } from "../http/errors";
4
- import { providerToolCallIdentity, toolsByWireName } from "../interaction/tool_identity";
5
- import { isAnthropicEffort } from "../interaction/provider_continuation_state";
6
- import { isBedrockApi, isOpenAIResponsesApi } from "./model_capabilities";
3
+ import { HttpError } from "../http/errors.js";
4
+ import { providerToolCallIdentity, toolsByWireName } from "../interaction/tool_identity.js";
5
+ import { isAnthropicEffort } from "../interaction/provider_continuation_state.js";
6
+ import { isBedrockApi, isOpenAIResponsesApi } from "./model_capabilities.js";
7
7
  import type {
8
8
  InteractionItem,
9
9
  InteractionOutput,
@@ -13,7 +13,7 @@ import type {
13
13
  ProviderContinuationState,
14
14
  ProviderIdentity,
15
15
  ToolKind,
16
- } from "../interaction/types";
16
+ } from "../interaction/types.js";
17
17
 
18
18
  export function piMessageToInteractionOutput(
19
19
  message: Record<string, unknown>,
@@ -465,6 +465,7 @@ function interactionUsage(value: unknown, source: ProviderIdentity): Interaction
465
465
  ? calculatedTotalCost
466
466
  : finiteNumber(cost.total) ?? calculatedTotalCost,
467
467
  },
468
+ ...(usage.costSource === "provider" ? { costSource: "provider" as const } : {}),
468
469
  };
469
470
  }
470
471
 
@@ -1,4 +1,4 @@
1
- import { subscriptionUsageLimitFromFailure } from "./subscription_limits";
1
+ import { subscriptionUsageLimitFromFailure } from "./subscription_limits.js";
2
2
 
3
3
  export type ProviderAttemptFailure =
4
4
  | {
@@ -8,44 +8,46 @@ import {
8
8
  type RoutingDecision,
9
9
  } from "@mupt-ai/dari-router/policy-engine";
10
10
 
11
- import { executionProviderForModel, keySourceForModel } from "../models/execution_provider";
12
- import { HttpError } from "../http/errors";
13
- import type { InteractionRequest, ProviderIdentity } from "../interaction/types";
11
+ import { executionProviderForModel, keySourceForModel } from "../models/execution_provider.js";
12
+ import { HttpError } from "../http/errors.js";
13
+ import type { RequestTags } from "../platform/platform.js";
14
+ import type { InteractionRequest, ProviderIdentity } from "../interaction/types.js";
14
15
  import {
15
16
  CustomerDisconnectedError,
16
17
  createProviderAttemptAbort,
17
18
  type ProviderAttemptAbort,
18
- } from "../runtime/customer_disconnect";
19
+ } from "../runtime/customer_disconnect.js";
19
20
  import {
20
21
  isProviderFailureMessage,
21
22
  providerFailureFromCaught,
22
23
  providerFailureFromEvent,
23
- } from "../runtime/provider_stream_outcome";
24
- import { providerAuthForSelectedModel } from "../strategies/routing";
25
- import { scrubSecretValues } from "../telemetry/scrub";
24
+ } from "../runtime/provider_stream_outcome.js";
25
+ import { providerAuthForSelectedModel } from "../strategies/routing.js";
26
+ import { scrubSecretValues } from "../telemetry/scrub.js";
26
27
  import {
27
28
  usageContextFor,
28
29
  type RoutingUsageContext,
29
- } from "../telemetry/usage";
30
+ } from "../telemetry/usage.js";
30
31
  import type {
31
32
  ProviderAuth,
32
33
  ProviderKeySource,
33
34
  ResolvedRouter,
34
35
  ResolvedRouterModelConfig,
35
- } from "../types";
36
- import { awsRoleCredentials } from "./aws_assume_role";
36
+ } from "../types.js";
37
+ import { awsRoleCredentials } from "./aws_assume_role.js";
37
38
  import {
38
39
  attachProviderResponseCapture,
39
40
  createProviderResponseCapture,
40
41
  type ProviderFetch,
41
42
  type ProviderResponseCapture,
42
- } from "./response_capture";
43
+ } from "./response_capture.js";
43
44
  import {
44
45
  completeInteractionModel,
45
46
  streamInteractionModel,
47
+ type ProviderEndpoint,
46
48
  type ProviderEventStream,
47
- } from "./pi";
48
- import { promptTokenEstimateFor, type PromptTokenEstimate } from "./cancel_estimate";
49
+ } from "./pi.js";
50
+ import { promptTokenEstimateFor, type PromptTokenEstimate } from "./cancel_estimate.js";
49
51
 
50
52
  type ProviderCallConfig = {
51
53
  model: string;
@@ -57,6 +59,8 @@ type ProviderCallConfig = {
57
59
  sessionId: string;
58
60
  reasoningEffort: ReasoningEffort;
59
61
  promptEstimate: PromptTokenEstimate | undefined;
62
+ // A platform-supplied endpoint replacing the model's configured one.
63
+ endpoint?: ProviderEndpoint;
60
64
  };
61
65
 
62
66
  export type ProviderExecution = ProviderCallConfig & {
@@ -101,6 +105,7 @@ export function prepareProviderExecution(args: {
101
105
  conversationId: string;
102
106
  promptCacheKey?: string;
103
107
  costEstimates?: readonly CandidateCostEstimate[];
108
+ tags?: RequestTags;
104
109
  }): ProviderExecution {
105
110
  const model = args.decision.selectedModel;
106
111
  const promptEstimate = promptTokenEstimateFor(args.costEstimates, model, args.decision.reasoningEffort);
@@ -109,11 +114,19 @@ export function prepareProviderExecution(args: {
109
114
  throw new HttpError(400, "The prompt exceeds this model's standard-price context range. Shorten or compact the conversation.", "context_length_exceeded");
110
115
  }
111
116
  const provider = executionProviderForModel(args.credentials, model);
112
- const providerKeySource = keySourceForModel(args.credentials, model);
113
- const auth = providerAuthForSelectedModel({
114
- selectedModel: model,
115
- credentials: args.credentials,
116
- });
117
+ const supplied = args.credentials.provider_credentials?.[model];
118
+ const providerKeySource = supplied ? "user" : keySourceForModel(args.credentials, model);
119
+ if (args.credentials.strict === true && providerKeySource === "subscription") {
120
+ throw new HttpError(
121
+ 400,
122
+ `Strict routers do not serve '${model}' from a personal subscription.`,
123
+ "strict_subscription_refused",
124
+ "model",
125
+ );
126
+ }
127
+ const auth: ProviderAuth = supplied
128
+ ? { type: "bearer", apiKey: supplied.apiKey.trim() }
129
+ : providerAuthForSelectedModel({ selectedModel: model, credentials: args.credentials });
117
130
  return {
118
131
  model,
119
132
  apiKey: auth.type === "bearer" ? auth.apiKey : "",
@@ -130,6 +143,9 @@ export function prepareProviderExecution(args: {
130
143
  : args.conversationId,
131
144
  reasoningEffort: args.decision.reasoningEffort,
132
145
  promptEstimate,
146
+ ...(supplied?.baseUrl || supplied?.headers
147
+ ? { endpoint: { baseUrl: supplied.baseUrl, headers: supplied.headers } }
148
+ : {}),
133
149
  provider,
134
150
  providerKeySource,
135
151
  ...(providerKeySource === "subscription" && args.credentials.attributed_user_id
@@ -144,6 +160,7 @@ export function prepareProviderExecution(args: {
144
160
  routerId: args.routerId,
145
161
  selectedModel: model,
146
162
  keySource: providerKeySource,
163
+ tags: args.tags ?? {},
147
164
  }),
148
165
  headers: {
149
166
  "X-Dari-Selected-Model": model,
@@ -212,6 +229,7 @@ export async function startProviderStream(args: {
212
229
  config: args.execution.config,
213
230
  sessionId: args.execution.sessionId,
214
231
  selectedReasoningEffort: args.execution.reasoningEffort,
232
+ endpoint: args.execution.endpoint,
215
233
  signal: customerAbort.signal,
216
234
  fetch: responseCapture?.fetch,
217
235
  });
@@ -306,6 +324,7 @@ async function completeProvider(args: {
306
324
  config: args.execution.config,
307
325
  sessionId: args.execution.sessionId,
308
326
  selectedReasoningEffort: args.execution.reasoningEffort,
327
+ endpoint: args.execution.endpoint,
309
328
  signal: args.signal,
310
329
  fetch: args.fetch,
311
330
  });
@@ -0,0 +1,138 @@
1
+ import { isRecord } from "@mupt-ai/dari-router/policy-engine";
2
+
3
+ import { gatewayFor } from "../models/gateways.js";
4
+ import { providerFailureFromCaught } from "../runtime/provider_stream_outcome.js";
5
+ import type { ProviderFetch } from "./response_capture.js";
6
+
7
+ // Gateways bill per call and say what they billed: OpenRouter as `usage.cost`
8
+ // on the final chunk, Vercel AI Gateway as `providerMetadata.gateway.cost`.
9
+ // Pi prices usage from model rates and drops both, so the amount is read off
10
+ // the raw response bytes and replaces the priced total.
11
+
12
+ export function reportsProviderCost(model: Record<string, unknown>): boolean {
13
+ return gatewayFor(model.provider) !== undefined;
14
+ }
15
+
16
+ export type ProviderReportedCostTap = {
17
+ fetch: ProviderFetch;
18
+ cost: () => number | undefined;
19
+ };
20
+
21
+ export function providerReportedCostTap(
22
+ fetch: ProviderFetch = globalThis.fetch,
23
+ ): ProviderReportedCostTap {
24
+ let cost: number | undefined;
25
+ return {
26
+ cost: () => cost,
27
+ fetch: async (input, init) => {
28
+ const response = await fetch(input, init);
29
+ if (!response.ok || response.body === null) return response;
30
+ cost = undefined;
31
+ return new Response(
32
+ response.body.pipeThrough(costScanner((value) => {
33
+ cost = value;
34
+ })),
35
+ { status: response.status, statusText: response.statusText, headers: response.headers },
36
+ );
37
+ },
38
+ };
39
+ }
40
+
41
+ export function messageWithProviderReportedCost(
42
+ message: Record<string, unknown>,
43
+ cost: number | undefined,
44
+ ): Record<string, unknown> {
45
+ if (cost === undefined) return message;
46
+ // A thrown error (or other record without a stop reason) becomes a failure
47
+ // message first: spreading an Error drops its non-enumerable `message`, and
48
+ // failure handling keeps usage only on messages that stopped in error.
49
+ const base = message.stopReason === undefined
50
+ ? { ...providerFailureFromCaught(message), ...(isRecord(message.usage) ? { usage: message.usage } : {}) }
51
+ : message;
52
+ const usage = isRecord(base.usage) ? base.usage : {};
53
+ return {
54
+ ...base,
55
+ usage: {
56
+ ...usage,
57
+ cost: { ...(isRecord(usage.cost) ? usage.cost : {}), total: cost },
58
+ costSource: "provider",
59
+ },
60
+ };
61
+ }
62
+
63
+ // Aborted and failed calls end in an error event carrying the partial
64
+ // message; it keeps whatever cost the provider reported before the end.
65
+ export async function* eventsWithProviderReportedCost(
66
+ events: AsyncIterable<Record<string, unknown>>,
67
+ tap: ProviderReportedCostTap,
68
+ ): AsyncIterable<Record<string, unknown>> {
69
+ for await (const event of events) {
70
+ if (event.type === "done" && isRecord(event.message)) {
71
+ yield { ...event, message: messageWithProviderReportedCost(event.message, tap.cost()) };
72
+ continue;
73
+ }
74
+ if (event.type === "error" && isRecord(event.error)) {
75
+ yield { ...event, error: messageWithProviderReportedCost(event.error, tap.cost()) };
76
+ continue;
77
+ }
78
+ yield event;
79
+ }
80
+ }
81
+
82
+ export function reportedCost(body: unknown): number | undefined {
83
+ if (!isRecord(body)) return undefined;
84
+ const gateway = isRecord(body.providerMetadata) && isRecord(body.providerMetadata.gateway)
85
+ ? body.providerMetadata.gateway
86
+ : undefined;
87
+ return usd(isRecord(body.usage) ? body.usage.cost : undefined) ?? usd(gateway?.cost);
88
+ }
89
+
90
+ function usd(value: unknown): number | undefined {
91
+ const amount = typeof value === "string" && value.trim() !== "" ? Number(value) : value;
92
+ return typeof amount === "number" && Number.isFinite(amount) && amount >= 0 ? amount : undefined;
93
+ }
94
+
95
+ // Reads SSE `data:` lines and plain JSON bodies alike; the last reported
96
+ // amount wins.
97
+ function costScanner(onCost: (cost: number) => void): TransformStream<Uint8Array, Uint8Array> {
98
+ const decoder = new TextDecoder();
99
+ let pending = "";
100
+ let body = "";
101
+ let sse = false;
102
+ const scan = (line: string) => {
103
+ if (!line.startsWith("data:") || !line.includes("cost")) return;
104
+ try {
105
+ const cost = reportedCost(JSON.parse(line.slice("data:".length)));
106
+ if (cost !== undefined) onCost(cost);
107
+ } catch {
108
+ // Not JSON: [DONE] or a keep-alive.
109
+ }
110
+ };
111
+ return new TransformStream({
112
+ transform(chunk, controller) {
113
+ controller.enqueue(chunk);
114
+ const text = decoder.decode(chunk, { stream: true });
115
+ pending += text;
116
+ const lines = pending.split("\n");
117
+ pending = lines.pop() ?? "";
118
+ for (const line of lines) {
119
+ const trimmed = line.trimEnd();
120
+ if (trimmed.startsWith("data:")) sse = true;
121
+ scan(trimmed);
122
+ }
123
+ if (!sse && body.length < 1024 * 1024) body += text;
124
+ },
125
+ flush() {
126
+ const tail = decoder.decode();
127
+ scan((pending + tail).trimEnd());
128
+ body += tail;
129
+ if (sse || !body.includes("cost")) return;
130
+ try {
131
+ const cost = reportedCost(JSON.parse(body));
132
+ if (cost !== undefined) onCost(cost);
133
+ } catch {
134
+ // Not a JSON body.
135
+ }
136
+ },
137
+ });
138
+ }
@@ -1,6 +1,6 @@
1
1
  import type { ReasoningEffort } from "@mupt-ai/dari-router/policy-engine";
2
- import { HttpError } from "../http/errors";
3
- import type { InteractionRequest } from "../interaction/types";
2
+ import { HttpError } from "../http/errors.js";
3
+ import type { InteractionRequest } from "../interaction/types.js";
4
4
 
5
5
  type ReasoningRequest = Pick<InteractionRequest, "reasoning">;
6
6
 
@@ -3,32 +3,32 @@ import type {
3
3
  RoutingDecision,
4
4
  } from "@mupt-ai/dari-router/policy-engine";
5
5
 
6
- import type { InteractionRequest, ProviderIdentity } from "../interaction/types";
6
+ import type { InteractionRequest, ProviderIdentity } from "../interaction/types.js";
7
7
  import {
8
8
  captureOperationalError,
9
9
  type OperationalErrorCode,
10
- } from "../observability/errors";
11
- import type { ProviderStreamOutcome } from "../runtime/provider_stream_outcome";
12
- import type { RoutedProtocol } from "../runtime/routed_protocol";
10
+ } from "../observability/errors.js";
11
+ import type { ProviderStreamOutcome } from "../runtime/provider_stream_outcome.js";
12
+ import type { RoutedProtocol } from "../runtime/routed_protocol.js";
13
13
  import {
14
14
  reportedExecution,
15
15
  type FrozenProviderTerminal,
16
16
  type ProviderAttemptRecord,
17
17
  type ReportableProviderOutcome,
18
18
  type RoutedRuntimeReporter,
19
- } from "../runtime/runtime_reporter";
20
- import type { ProviderKeySource } from "../types";
19
+ } from "../runtime/runtime_reporter.js";
20
+ import type { ProviderKeySource } from "../types.js";
21
21
  import {
22
22
  reportRoutingUsage,
23
23
  type RoutingUsageContext,
24
- } from "../telemetry/usage";
25
- import { customerCanceledProviderMessage } from "../runtime/customer_disconnect";
24
+ } from "../telemetry/usage.js";
25
+ import { customerCanceledProviderMessage } from "../runtime/customer_disconnect.js";
26
26
  import {
27
27
  estimatedCancelUsage,
28
28
  partialInteractionOutput,
29
29
  type DeliveredOutput,
30
30
  type PromptTokenEstimate,
31
- } from "./cancel_estimate";
31
+ } from "./cancel_estimate.js";
32
32
 
33
33
  // Structurally a subset of ProviderExecution. The narrowing is deliberate: it
34
34
  // keeps apiKey and auth out of the finalizer.
@@ -138,7 +138,7 @@ async function reportTerminalOnce(
138
138
  ? args.reportBarrier
139
139
  : Promise.race([
140
140
  args.reportBarrier,
141
- Bun.sleep(args.cancelBarrierCapMs ?? CANCEL_REPORT_BARRIER_CAP_MS),
141
+ new Promise((resolve) => setTimeout(resolve, args.cancelBarrierCapMs ?? CANCEL_REPORT_BARRIER_CAP_MS)),
142
142
  ]));
143
143
  } catch (error) {
144
144
  capture(error, "routing_report_barrier_failed");
@@ -1,8 +1,8 @@
1
1
  import { isRecord } from "@mupt-ai/dari-router/policy-engine";
2
2
 
3
- import { providerFailureMessage } from "../runtime/provider_stream_outcome";
4
- import type { ProviderKeySource } from "../types";
5
- import type { CapturedProviderResponse } from "./response_capture";
3
+ import { providerFailureMessage } from "../runtime/provider_stream_outcome.js";
4
+ import type { ProviderKeySource } from "../types.js";
5
+ import type { CapturedProviderResponse } from "./response_capture.js";
6
6
 
7
7
  export type SubscriptionOAuthProvider = "anthropic_claude_code" | "openai_codex";
8
8
 
@@ -4,11 +4,11 @@ import {
4
4
  type RoutingDecision,
5
5
  } from "@mupt-ai/dari-router/policy-engine";
6
6
 
7
- import type { RoutedDecision } from "../../strategies/route_decision";
8
- import { isBackgroundRequest } from "../../interaction/types";
9
- import { HttpError } from "../../http/errors";
10
- import { assertCreditAvailableBeforeRouting } from "../../telemetry/usage";
11
- import type { PreparedRoutedCompletion } from "../prepare_routed_completion";
7
+ import type { RoutedDecision } from "../../strategies/route_decision.js";
8
+ import { isBackgroundRequest } from "../../interaction/types.js";
9
+ import { HttpError } from "../../http/errors.js";
10
+ import { assertCreditAvailableBeforeRouting } from "../../telemetry/usage.js";
11
+ import type { PreparedRoutedCompletion } from "../prepare_routed_completion.js";
12
12
  import { leaseFallbackDecision } from "../routing_lease.js";
13
13
 
14
14
  // Explicit background classes bypass the selector and every lease path.
@@ -1,4 +1,4 @@
1
- import { HttpError } from "../http/errors";
1
+ import { HttpError } from "../http/errors.js";
2
2
 
3
3
  const CUSTOMER_DISCONNECTED_MESSAGE = "Client disconnected.";
4
4
 
@@ -1,9 +1,9 @@
1
1
  import type { RoutingCandidate, RoutingDecision } from "@mupt-ai/dari-router/policy-engine";
2
2
 
3
- import { HttpError } from "../http/errors";
4
- import { explicitModelId } from "../protocol/input/router_model";
5
- import type { RoutedDecision } from "../strategies/route_decision";
6
- import type { PreparedRoutedCompletion } from "./prepare_routed_completion";
3
+ import { HttpError } from "../http/errors.js";
4
+ import { explicitModelId } from "../protocol/input/router_model.js";
5
+ import type { RoutedDecision } from "../strategies/route_decision.js";
6
+ import type { PreparedRoutedCompletion } from "./prepare_routed_completion.js";
7
7
 
8
8
  /* A request for `dari/model/<id>` is served by exactly that enabled model,
9
9
  with no selector, lease, or fallback: the user chose it, like picking a
@@ -57,3 +57,30 @@ function pickCandidate(
57
57
  }
58
58
  return candidates[0]!;
59
59
  }
60
+
61
+ /* A strict router serves only the pinned `dari/model/<id>` at exactly the
62
+ requested thinking level (the model's default when none is sent), and
63
+ refuses everything else instead of substituting. */
64
+ export function strictModelDecision(prepared: PreparedRoutedCompletion): RoutedDecision {
65
+ const modelId = explicitModelId(prepared.interaction.model);
66
+ if (modelId === null) {
67
+ throw new HttpError(
68
+ 400,
69
+ "This router is strict: request a pinned model as 'dari/model/<id>'.",
70
+ "strict_model_required",
71
+ "model",
72
+ );
73
+ }
74
+ const { decision } = explicitModelDecision(prepared)!;
75
+ const requested = prepared.interaction.reasoning?.effort
76
+ ?? prepared.credentials.model_configs?.[modelId]?.default_thinking_level;
77
+ if (requested !== undefined && requested !== null && decision.reasoningEffort !== requested) {
78
+ throw new HttpError(
79
+ 400,
80
+ `Model '${modelId}' cannot serve thinking level '${requested}' on this router.`,
81
+ "thinking_level_unavailable",
82
+ "reasoning_effort",
83
+ );
84
+ }
85
+ return { decision };
86
+ }
@@ -1,6 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
2
 
3
- import type { SourceProtocol } from "../../interaction/types";
3
+ import type { SourceProtocol } from "../../interaction/types.js";
4
4
 
5
5
  export type HarnessConversationIdentity =
6
6
  | {
@@ -1,12 +1,12 @@
1
1
  import { isRecord } from "@mupt-ai/dari-router/policy-engine";
2
2
 
3
- import { HttpError } from "../../http/errors";
3
+ import { HttpError } from "../../http/errors.js";
4
4
  import type {
5
5
  AgentRequestClass,
6
6
  AgentRequestContext,
7
7
  InteractionRequestPurpose,
8
- } from "../../interaction/types";
9
- import type { HarnessConversationIdentity } from "./conversation";
8
+ } from "../../interaction/types.js";
9
+ import type { HarnessConversationIdentity } from "./conversation.js";
10
10
 
11
11
  const MAX_ID_CHARS = 200;
12
12
 
@@ -3,7 +3,7 @@ export {
3
3
  scopedHarnessSessionId,
4
4
  type HarnessConversationIdentity,
5
5
  type HarnessConversationScope,
6
- } from "./conversation";
6
+ } from "./conversation.js";
7
7
  export {
8
8
  chatCompletionsConversationIdentity,
9
9
  claudeCodeConversationIdentity,
@@ -11,4 +11,4 @@ export {
11
11
  codexRequestPurpose,
12
12
  codexRequestContext,
13
13
  openCodeRequestContext,
14
- } from "./extract";
14
+ } from "./extract.js";
@@ -1,24 +1,27 @@
1
1
  import type { CandidatePruningAudit } from "@mupt-ai/dari-router/policy-engine";
2
2
 
3
- import { interactionConversationPrefix } from "../context/interaction_fingerprint";
3
+ import { interactionConversationPrefix } from "../context/interaction_fingerprint.js";
4
4
  import {
5
5
  buildRoutingContext,
6
6
  derivePrefixLookupHashes,
7
7
  type ImageCapabilityPrunedModel,
8
8
  type RoutingContext,
9
- } from "../context/routing_context";
10
- import { isBackgroundRequest, type InteractionRequest } from "../interaction/types";
11
- import { resolveRouter } from "../strategies/routing";
12
- import { RequestReporter, type ReportedCandidatePruning } from "../telemetry/report";
13
- import { RoutingBillingFinalization } from "../telemetry/billing_finalization";
14
- import type { ClientHarness, ResolvedRouter } from "../types";
15
- import type { RoutedRuntimeReporter } from "./runtime_reporter";
16
- import type { RoutedProtocol } from "./routed_protocol";
9
+ } from "../context/routing_context.js";
10
+ import { isBackgroundRequest, type InteractionRequest } from "../interaction/types.js";
11
+ import { executionProviderForModel } from "../models/execution_provider.js";
12
+ import { gatewayPlatform, type RequestTags } from "../platform/platform.js";
13
+ import { resolveRouter } from "../strategies/routing.js";
14
+ import { RequestReporter, type ReportedCandidatePruning } from "../telemetry/report.js";
15
+ import { RoutingBillingFinalization } from "../telemetry/billing_finalization.js";
16
+ import type { ClientHarness, ProviderCredential, ResolvedRouter } from "../types.js";
17
+ import { requestTags } from "./request_tags.js";
18
+ import type { RoutedRuntimeReporter } from "./runtime_reporter.js";
19
+ import type { RoutedProtocol } from "./routed_protocol.js";
17
20
  import {
18
21
  scopedHarnessConversationId,
19
22
  scopedHarnessSessionId,
20
23
  type HarnessConversationIdentity,
21
- } from "./harness_identity";
24
+ } from "./harness_identity/index.js";
22
25
 
23
26
  export type PreparedRoutedCompletion = {
24
27
  requestId: string;
@@ -28,6 +31,7 @@ export type PreparedRoutedCompletion = {
28
31
  protocol: RoutedProtocol;
29
32
  credentials: ResolvedRouter;
30
33
  billable: boolean;
34
+ tags: RequestTags;
31
35
  harnessConversationIdentity?: HarnessConversationIdentity;
32
36
  routing: RoutingContext;
33
37
  reporter: RoutedRuntimeReporter;
@@ -42,9 +46,11 @@ export async function prepareRoutedCompletion(args: {
42
46
  startedMs: number;
43
47
  requestId: string;
44
48
  clientRequestId: string | null;
49
+ tagsHeader: string | null;
45
50
  clientHarness?: ClientHarness;
46
51
  harnessConversationIdentity?: HarnessConversationIdentity;
47
52
  }): Promise<PreparedRoutedCompletion> {
53
+ const tags = requestTags(args.tagsHeader);
48
54
  // Eval relevance draws from the public catalog, which resolve only returns
49
55
  // on request.
50
56
  const resolveRequest = {
@@ -119,6 +125,7 @@ export async function prepareRoutedCompletion(args: {
119
125
  }
120
126
  credentials = conversationCredentials;
121
127
  }
128
+ credentials = await withProviderCredentials(credentials, args.apiKey);
122
129
  const billable = true;
123
130
  const routing = await buildRoutingContext({
124
131
  routerId: prefixFingerprintRouterId,
@@ -131,6 +138,7 @@ export async function prepareRoutedCompletion(args: {
131
138
  const reporter = new RequestReporter({
132
139
  requestId: args.requestId,
133
140
  clientRequestId: args.clientRequestId,
141
+ tags,
134
142
  credentials,
135
143
  routerId: canonicalRouterId,
136
144
  interaction: args.interaction,
@@ -168,12 +176,40 @@ export async function prepareRoutedCompletion(args: {
168
176
  protocol: args.protocol,
169
177
  credentials,
170
178
  billable,
179
+ tags,
171
180
  harnessConversationIdentity: args.harnessConversationIdentity,
172
181
  routing,
173
182
  reporter,
174
183
  };
175
184
  }
176
185
 
186
+ // Ask the platform for each enabled model's credential up front, so provider
187
+ // execution and fallback stay synchronous.
188
+ async function withProviderCredentials(
189
+ credentials: ResolvedRouter,
190
+ apiKey: string,
191
+ ): Promise<ResolvedRouter> {
192
+ const platform = gatewayPlatform();
193
+ if (platform.resolveProviderCredential === undefined) return credentials;
194
+ const resolved = await Promise.all(
195
+ credentials.enabled_models.map(async (model) => [
196
+ model,
197
+ await platform.resolveProviderCredential!({
198
+ provider: executionProviderForModel(credentials, model),
199
+ model,
200
+ routerId: credentials.router_id,
201
+ organizationId: credentials.organization_id,
202
+ apiKey,
203
+ }),
204
+ ] as const),
205
+ );
206
+ const provider_credentials: Record<string, ProviderCredential> = {};
207
+ for (const [model, credential] of resolved) {
208
+ if (credential?.apiKey.trim()) provider_credentials[model] = credential;
209
+ }
210
+ return { ...credentials, provider_credentials };
211
+ }
212
+
177
213
  // Core's cache-aware pruning audit, extended with the context layer's image
178
214
  // capability exclusions. The exclusions survive on their own when custom
179
215
  // routing produced no core audit (missing config), so the error path keeps
@@ -1,4 +1,4 @@
1
- import type { ProviderStreamOutcome } from "./provider_stream_outcome";
1
+ import type { ProviderStreamOutcome } from "./provider_stream_outcome.js";
2
2
 
3
3
  type LifecycleArgs = {
4
4
  partialResponse: () => Record<string, unknown>;
@@ -1,6 +1,6 @@
1
1
  import { isRecord } from "@mupt-ai/dari-router/policy-engine";
2
- import type { InteractionOutput } from "../interaction/types";
3
- import { scrubSecrets } from "../telemetry/scrub";
2
+ import type { InteractionOutput } from "../interaction/types.js";
3
+ import { scrubSecrets } from "../telemetry/scrub.js";
4
4
 
5
5
  export type ProviderStreamOutcome =
6
6
  | {
@@ -0,0 +1,34 @@
1
+ import { HttpError } from "../http/errors.js";
2
+ import type { RequestTags } from "../platform/platform.js";
3
+
4
+ const TAGS_MAX_CHARS = 2048;
5
+ const TAGS_MAX_COUNT = 32;
6
+ const TAG_KEY = /^[A-Za-z0-9_.:-]{1,64}$/;
7
+ const TAG_VALUE_MAX_CHARS = 256;
8
+
9
+ /**
10
+ * Parse `X-Dari-Tags: run=r1,trial=3,setting=low` into attribution labels for
11
+ * the platform's request and usage records. Malformed or oversized headers
12
+ * are refused rather than silently dropped, so cost is never misattributed.
13
+ */
14
+ export function requestTags(header: string | null): RequestTags {
15
+ const tags: RequestTags = {};
16
+ if (header === null || header.trim() === "") return tags;
17
+ if (header.length > TAGS_MAX_CHARS) throw invalidTags(`X-Dari-Tags exceeds ${TAGS_MAX_CHARS} characters.`);
18
+ const pairs = header.split(",");
19
+ if (pairs.length > TAGS_MAX_COUNT) throw invalidTags(`X-Dari-Tags has more than ${TAGS_MAX_COUNT} tags.`);
20
+ for (const pair of pairs) {
21
+ const separator = pair.indexOf("=");
22
+ const key = pair.slice(0, separator).trim();
23
+ const value = pair.slice(separator + 1).trim();
24
+ if (separator < 0 || !TAG_KEY.test(key) || value.length > TAG_VALUE_MAX_CHARS) {
25
+ throw invalidTags("X-Dari-Tags must be comma-separated key=value pairs.");
26
+ }
27
+ tags[key] = value;
28
+ }
29
+ return tags;
30
+ }
31
+
32
+ function invalidTags(message: string): HttpError {
33
+ return new HttpError(400, message, "invalid_tags");
34
+ }