@srouterhq/server 0.0.0-stage → 0.9.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 (1027) hide show
  1. package/LICENSE +21 -0
  2. package/client/dist/assets/am-TQ7Jmdqe.js +1 -0
  3. package/client/dist/assets/ar-Bf5a7yfC.js +1 -0
  4. package/client/dist/assets/az-CrosE1xH.js +1 -0
  5. package/client/dist/assets/bg-BvWnmcUz.js +1 -0
  6. package/client/dist/assets/bn-ams5TsJp.js +1 -0
  7. package/client/dist/assets/cs-Dh67CaJ_.js +1 -0
  8. package/client/dist/assets/da-9WlT8Iq7.js +1 -0
  9. package/client/dist/assets/de-lWhJJzz7.js +1 -0
  10. package/client/dist/assets/el-Cu7_GhNq.js +1 -0
  11. package/client/dist/assets/es-BZuzsBcP.js +1 -0
  12. package/client/dist/assets/fa-Dwdn-jKS.js +1 -0
  13. package/client/dist/assets/fi-BFrFyOTy.js +1 -0
  14. package/client/dist/assets/fr-XhbtmPpj.js +1 -0
  15. package/client/dist/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  16. package/client/dist/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  17. package/client/dist/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  18. package/client/dist/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  19. package/client/dist/assets/geist-mono-cyrillic-ext-wght-normal-I4S5GZfc.woff2 +0 -0
  20. package/client/dist/assets/geist-mono-cyrillic-wght-normal-BmXc_FBt.woff2 +0 -0
  21. package/client/dist/assets/geist-mono-latin-ext-wght-normal-DrnZ1wKl.woff2 +0 -0
  22. package/client/dist/assets/geist-mono-latin-wght-normal-B_7UjwxQ.woff2 +0 -0
  23. package/client/dist/assets/geist-mono-symbols2-wght-normal-GZpp1pK2.woff2 +0 -0
  24. package/client/dist/assets/geist-mono-vietnamese-wght-normal-D8KDMBhC.woff2 +0 -0
  25. package/client/dist/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  26. package/client/dist/assets/gu-D0uhLUB8.js +1 -0
  27. package/client/dist/assets/ha-CVlm9pqp.js +1 -0
  28. package/client/dist/assets/he-CMIFeoWr.js +1 -0
  29. package/client/dist/assets/hi-ENTwhBpn.js +1 -0
  30. package/client/dist/assets/hr-fc03vMy_.js +1 -0
  31. package/client/dist/assets/hu-YBo2BIYt.js +1 -0
  32. package/client/dist/assets/id-BEWXYX69.js +1 -0
  33. package/client/dist/assets/ig-BKLZFKka.js +1 -0
  34. package/client/dist/assets/index-D9I4KtvR.js +145 -0
  35. package/client/dist/assets/index-txhEmCdb.css +2 -0
  36. package/client/dist/assets/it-Bzmei1Y2.js +1 -0
  37. package/client/dist/assets/ja-BfcpuqEl.js +1 -0
  38. package/client/dist/assets/ka-CnWjRgLo.js +1 -0
  39. package/client/dist/assets/km-BAjPR3Qj.js +1 -0
  40. package/client/dist/assets/kn-B15WKU5m.js +1 -0
  41. package/client/dist/assets/ko-C61fAB-w.js +1 -0
  42. package/client/dist/assets/lt-CYTr4_YW.js +1 -0
  43. package/client/dist/assets/ml-CtqxgyH3.js +1 -0
  44. package/client/dist/assets/mr-X5TMY7aR.js +1 -0
  45. package/client/dist/assets/ms-DSg8RqPS.js +1 -0
  46. package/client/dist/assets/my-D7RqR8uE.js +1 -0
  47. package/client/dist/assets/ne-DaflxkDj.js +1 -0
  48. package/client/dist/assets/nl-BOqqtsC8.js +1 -0
  49. package/client/dist/assets/no-BSHp_StB.js +1 -0
  50. package/client/dist/assets/or-BkLR9Vub.js +1 -0
  51. package/client/dist/assets/pa-CarJ2XgI.js +1 -0
  52. package/client/dist/assets/pl-C2n0V-0k.js +1 -0
  53. package/client/dist/assets/pt-BR-CqJdNhg2.js +1 -0
  54. package/client/dist/assets/pt-PT-C_HpMyKz.js +1 -0
  55. package/client/dist/assets/ro-DVRZqSAS.js +1 -0
  56. package/client/dist/assets/ru-DwAJG7VC.js +1 -0
  57. package/client/dist/assets/si-BxUezXm0.js +1 -0
  58. package/client/dist/assets/sk-eI1VcIfY.js +1 -0
  59. package/client/dist/assets/sr-CJF3auCr.js +1 -0
  60. package/client/dist/assets/srouter-logo-C6ZfjGIi.svg +14 -0
  61. package/client/dist/assets/sv-nqvwHAq_.js +1 -0
  62. package/client/dist/assets/sw-DWynE2pW.js +1 -0
  63. package/client/dist/assets/ta-BVM12knC.js +1 -0
  64. package/client/dist/assets/te-oyzEkF7_.js +1 -0
  65. package/client/dist/assets/th-COVeE0ZQ.js +1 -0
  66. package/client/dist/assets/tl-DsJANnQz.js +1 -0
  67. package/client/dist/assets/tr-B-Fl-urO.js +1 -0
  68. package/client/dist/assets/uk-C5qketAO.js +1 -0
  69. package/client/dist/assets/ur-QU9KugfV.js +1 -0
  70. package/client/dist/assets/uz-DL9cG_mY.js +1 -0
  71. package/client/dist/assets/vi-CQiVy3Pu.js +1 -0
  72. package/client/dist/assets/yo-DqazMdrZ.js +1 -0
  73. package/client/dist/assets/zh-CN-DnYL_Kio.js +1 -0
  74. package/client/dist/assets/zh-TW-5bdbosKj.js +1 -0
  75. package/client/dist/favicon.svg +14 -0
  76. package/client/dist/icons.svg +24 -0
  77. package/client/dist/index.html +37 -0
  78. package/dist/app.d.ts +4 -0
  79. package/dist/app.d.ts.map +1 -0
  80. package/dist/app.js +372 -0
  81. package/dist/app.js.map +1 -0
  82. package/dist/db/index.d.ts +49 -0
  83. package/dist/db/index.d.ts.map +1 -0
  84. package/dist/db/index.js +203 -0
  85. package/dist/db/index.js.map +1 -0
  86. package/dist/db/migrate/TEMPLATE.d.ts +4 -0
  87. package/dist/db/migrate/TEMPLATE.d.ts.map +1 -0
  88. package/dist/db/migrate/TEMPLATE.js +18 -0
  89. package/dist/db/migrate/TEMPLATE.js.map +1 -0
  90. package/dist/db/migrate/cli.d.ts +2 -0
  91. package/dist/db/migrate/cli.d.ts.map +1 -0
  92. package/dist/db/migrate/cli.js +177 -0
  93. package/dist/db/migrate/cli.js.map +1 -0
  94. package/dist/db/migrate/defaults.d.ts +52 -0
  95. package/dist/db/migrate/defaults.d.ts.map +1 -0
  96. package/dist/db/migrate/defaults.js +126 -0
  97. package/dist/db/migrate/defaults.js.map +1 -0
  98. package/dist/db/migrate/runner.d.ts +16 -0
  99. package/dist/db/migrate/runner.d.ts.map +1 -0
  100. package/dist/db/migrate/runner.js +178 -0
  101. package/dist/db/migrate/runner.js.map +1 -0
  102. package/dist/db/migrations/20260101_000000_legacy_baseline.d.ts +4 -0
  103. package/dist/db/migrations/20260101_000000_legacy_baseline.d.ts.map +1 -0
  104. package/dist/db/migrations/20260101_000000_legacy_baseline.js +2240 -0
  105. package/dist/db/migrations/20260101_000000_legacy_baseline.js.map +1 -0
  106. package/dist/db/migrations/20260627_000001_custom_provider_modalities.d.ts +4 -0
  107. package/dist/db/migrations/20260627_000001_custom_provider_modalities.d.ts.map +1 -0
  108. package/dist/db/migrations/20260627_000001_custom_provider_modalities.js +27 -0
  109. package/dist/db/migrations/20260627_000001_custom_provider_modalities.js.map +1 -0
  110. package/dist/db/migrations/20260627_000002_catalog_model_state.d.ts +4 -0
  111. package/dist/db/migrations/20260627_000002_catalog_model_state.d.ts.map +1 -0
  112. package/dist/db/migrations/20260627_000002_catalog_model_state.js +30 -0
  113. package/dist/db/migrations/20260627_000002_catalog_model_state.js.map +1 -0
  114. package/dist/db/migrations/20260628_120000_request_aggregates.d.ts +6 -0
  115. package/dist/db/migrations/20260628_120000_request_aggregates.d.ts.map +1 -0
  116. package/dist/db/migrations/20260628_120000_request_aggregates.js +104 -0
  117. package/dist/db/migrations/20260628_120000_request_aggregates.js.map +1 -0
  118. package/dist/db/migrations/20260630_000001_github_gpt41_context.d.ts +9 -0
  119. package/dist/db/migrations/20260630_000001_github_gpt41_context.d.ts.map +1 -0
  120. package/dist/db/migrations/20260630_000001_github_gpt41_context.js +22 -0
  121. package/dist/db/migrations/20260630_000001_github_gpt41_context.js.map +1 -0
  122. package/dist/db/migrations/20260706_000001_request_client_info.d.ts +4 -0
  123. package/dist/db/migrations/20260706_000001_request_client_info.d.ts.map +1 -0
  124. package/dist/db/migrations/20260706_000001_request_client_info.js +30 -0
  125. package/dist/db/migrations/20260706_000001_request_client_info.js.map +1 -0
  126. package/dist/db/migrations/20260706_000002_custom_model_tool_support.d.ts +17 -0
  127. package/dist/db/migrations/20260706_000002_custom_model_tool_support.d.ts.map +1 -0
  128. package/dist/db/migrations/20260706_000002_custom_model_tool_support.js +20 -0
  129. package/dist/db/migrations/20260706_000002_custom_model_tool_support.js.map +1 -0
  130. package/dist/db/migrations/20260714_000001_profile_chain_backfill.d.ts +12 -0
  131. package/dist/db/migrations/20260714_000001_profile_chain_backfill.d.ts.map +1 -0
  132. package/dist/db/migrations/20260714_000001_profile_chain_backfill.js +45 -0
  133. package/dist/db/migrations/20260714_000001_profile_chain_backfill.js.map +1 -0
  134. package/dist/db/migrations/20260720_000001_key_health_error.d.ts +5 -0
  135. package/dist/db/migrations/20260720_000001_key_health_error.d.ts.map +1 -0
  136. package/dist/db/migrations/20260720_000001_key_health_error.js +16 -0
  137. package/dist/db/migrations/20260720_000001_key_health_error.js.map +1 -0
  138. package/dist/db/migrations/20260726_000001_cooldown_probe_provenance.d.ts +4 -0
  139. package/dist/db/migrations/20260726_000001_cooldown_probe_provenance.d.ts.map +1 -0
  140. package/dist/db/migrations/20260726_000001_cooldown_probe_provenance.js +38 -0
  141. package/dist/db/migrations/20260726_000001_cooldown_probe_provenance.js.map +1 -0
  142. package/dist/db/migrations/20260726_000002_request_attempts.d.ts +4 -0
  143. package/dist/db/migrations/20260726_000002_request_attempts.d.ts.map +1 -0
  144. package/dist/db/migrations/20260726_000002_request_attempts.js +43 -0
  145. package/dist/db/migrations/20260726_000002_request_attempts.js.map +1 -0
  146. package/dist/db/migrations/20260726_000003_model_source_provenance.d.ts +4 -0
  147. package/dist/db/migrations/20260726_000003_model_source_provenance.d.ts.map +1 -0
  148. package/dist/db/migrations/20260726_000003_model_source_provenance.js +80 -0
  149. package/dist/db/migrations/20260726_000003_model_source_provenance.js.map +1 -0
  150. package/dist/db/migrations/20260726_000004_media_model_meta.d.ts +4 -0
  151. package/dist/db/migrations/20260726_000004_media_model_meta.d.ts.map +1 -0
  152. package/dist/db/migrations/20260726_000004_media_model_meta.js +34 -0
  153. package/dist/db/migrations/20260726_000004_media_model_meta.js.map +1 -0
  154. package/dist/db/migrations/20260726_000005_request_served_model.d.ts +4 -0
  155. package/dist/db/migrations/20260726_000005_request_served_model.d.ts.map +1 -0
  156. package/dist/db/migrations/20260726_000005_request_served_model.js +31 -0
  157. package/dist/db/migrations/20260726_000005_request_served_model.js.map +1 -0
  158. package/dist/db/migrations/20260726_000006_attempt_error_summary.d.ts +4 -0
  159. package/dist/db/migrations/20260726_000006_attempt_error_summary.d.ts.map +1 -0
  160. package/dist/db/migrations/20260726_000006_attempt_error_summary.js +29 -0
  161. package/dist/db/migrations/20260726_000006_attempt_error_summary.js.map +1 -0
  162. package/dist/db/migrations/20260727_000001_agent_compatibility.d.ts +4 -0
  163. package/dist/db/migrations/20260727_000001_agent_compatibility.d.ts.map +1 -0
  164. package/dist/db/migrations/20260727_000001_agent_compatibility.js +41 -0
  165. package/dist/db/migrations/20260727_000001_agent_compatibility.js.map +1 -0
  166. package/dist/db/migrations/20260728_000001_tombstone_provenance.d.ts +12 -0
  167. package/dist/db/migrations/20260728_000001_tombstone_provenance.d.ts.map +1 -0
  168. package/dist/db/migrations/20260728_000001_tombstone_provenance.js +37 -0
  169. package/dist/db/migrations/20260728_000001_tombstone_provenance.js.map +1 -0
  170. package/dist/db/migrations/20260729_000001_custom_model_endpoint_identity.d.ts +4 -0
  171. package/dist/db/migrations/20260729_000001_custom_model_endpoint_identity.d.ts.map +1 -0
  172. package/dist/db/migrations/20260729_000001_custom_model_endpoint_identity.js +145 -0
  173. package/dist/db/migrations/20260729_000001_custom_model_endpoint_identity.js.map +1 -0
  174. package/dist/db/migrations/20260802_000001_custom_endpoint_host_labels.d.ts +6 -0
  175. package/dist/db/migrations/20260802_000001_custom_endpoint_host_labels.d.ts.map +1 -0
  176. package/dist/db/migrations/20260802_000001_custom_endpoint_host_labels.js +52 -0
  177. package/dist/db/migrations/20260802_000001_custom_endpoint_host_labels.js.map +1 -0
  178. package/dist/db/migrations/20260805_000001_key_model_scope.d.ts +6 -0
  179. package/dist/db/migrations/20260805_000001_key_model_scope.d.ts.map +1 -0
  180. package/dist/db/migrations/20260805_000001_key_model_scope.js +17 -0
  181. package/dist/db/migrations/20260805_000001_key_model_scope.js.map +1 -0
  182. package/dist/db/migrations/20260805_000002_client_profiles.d.ts +4 -0
  183. package/dist/db/migrations/20260805_000002_client_profiles.d.ts.map +1 -0
  184. package/dist/db/migrations/20260805_000002_client_profiles.js +33 -0
  185. package/dist/db/migrations/20260805_000002_client_profiles.js.map +1 -0
  186. package/dist/db/migrations/20260810_000001_api_key_proxy.d.ts +4 -0
  187. package/dist/db/migrations/20260810_000001_api_key_proxy.d.ts.map +1 -0
  188. package/dist/db/migrations/20260810_000001_api_key_proxy.js +33 -0
  189. package/dist/db/migrations/20260810_000001_api_key_proxy.js.map +1 -0
  190. package/dist/db/migrations/20260819_000001_custom_model_tombstones.d.ts +4 -0
  191. package/dist/db/migrations/20260819_000001_custom_model_tombstones.d.ts.map +1 -0
  192. package/dist/db/migrations/20260819_000001_custom_model_tombstones.js +19 -0
  193. package/dist/db/migrations/20260819_000001_custom_model_tombstones.js.map +1 -0
  194. package/dist/db/migrations/20260820_000001_playground_conversations.d.ts +4 -0
  195. package/dist/db/migrations/20260820_000001_playground_conversations.d.ts.map +1 -0
  196. package/dist/db/migrations/20260820_000001_playground_conversations.js +47 -0
  197. package/dist/db/migrations/20260820_000001_playground_conversations.js.map +1 -0
  198. package/dist/db/migrations/20260823_000001_server_logs.d.ts +4 -0
  199. package/dist/db/migrations/20260823_000001_server_logs.d.ts.map +1 -0
  200. package/dist/db/migrations/20260823_000001_server_logs.js +53 -0
  201. package/dist/db/migrations/20260823_000001_server_logs.js.map +1 -0
  202. package/dist/db/migrations/20260823_000002_backups_table.d.ts +8 -0
  203. package/dist/db/migrations/20260823_000002_backups_table.d.ts.map +1 -0
  204. package/dist/db/migrations/20260823_000002_backups_table.js +22 -0
  205. package/dist/db/migrations/20260823_000002_backups_table.js.map +1 -0
  206. package/dist/db/migrations/20260823_000003_attempt_key_label.d.ts +19 -0
  207. package/dist/db/migrations/20260823_000003_attempt_key_label.d.ts.map +1 -0
  208. package/dist/db/migrations/20260823_000003_attempt_key_label.js +28 -0
  209. package/dist/db/migrations/20260823_000003_attempt_key_label.js.map +1 -0
  210. package/dist/db/migrations/20260823_000004_profile_auto_include.d.ts +14 -0
  211. package/dist/db/migrations/20260823_000004_profile_auto_include.d.ts.map +1 -0
  212. package/dist/db/migrations/20260823_000004_profile_auto_include.js +25 -0
  213. package/dist/db/migrations/20260823_000004_profile_auto_include.js.map +1 -0
  214. package/dist/db/migrations/20260901_000001_idempotency_claims.d.ts +4 -0
  215. package/dist/db/migrations/20260901_000001_idempotency_claims.d.ts.map +1 -0
  216. package/dist/db/migrations/20260901_000001_idempotency_claims.js +46 -0
  217. package/dist/db/migrations/20260901_000001_idempotency_claims.js.map +1 -0
  218. package/dist/db/migrations/20260901_000002_quota_observation_lookup.d.ts +4 -0
  219. package/dist/db/migrations/20260901_000002_quota_observation_lookup.d.ts.map +1 -0
  220. package/dist/db/migrations/20260901_000002_quota_observation_lookup.js +37 -0
  221. package/dist/db/migrations/20260901_000002_quota_observation_lookup.js.map +1 -0
  222. package/dist/db/migrations/20260901_000003_request_caller.d.ts +4 -0
  223. package/dist/db/migrations/20260901_000003_request_caller.d.ts.map +1 -0
  224. package/dist/db/migrations/20260901_000003_request_caller.js +27 -0
  225. package/dist/db/migrations/20260901_000003_request_caller.js.map +1 -0
  226. package/dist/db/migrations/20260902_000001_analytics_latency_percentile_index.d.ts +4 -0
  227. package/dist/db/migrations/20260902_000001_analytics_latency_percentile_index.d.ts.map +1 -0
  228. package/dist/db/migrations/20260902_000001_analytics_latency_percentile_index.js +35 -0
  229. package/dist/db/migrations/20260902_000001_analytics_latency_percentile_index.js.map +1 -0
  230. package/dist/db/migrations/20260903_000001_mcp_enabled_default.d.ts +4 -0
  231. package/dist/db/migrations/20260903_000001_mcp_enabled_default.d.ts.map +1 -0
  232. package/dist/db/migrations/20260903_000001_mcp_enabled_default.js +37 -0
  233. package/dist/db/migrations/20260903_000001_mcp_enabled_default.js.map +1 -0
  234. package/dist/db/migrations/20260903_000002_response_cache.d.ts +4 -0
  235. package/dist/db/migrations/20260903_000002_response_cache.d.ts.map +1 -0
  236. package/dist/db/migrations/20260903_000002_response_cache.js +53 -0
  237. package/dist/db/migrations/20260903_000002_response_cache.js.map +1 -0
  238. package/dist/db/migrations/20260904_000001_key_monthly_budget.d.ts +13 -0
  239. package/dist/db/migrations/20260904_000001_key_monthly_budget.d.ts.map +1 -0
  240. package/dist/db/migrations/20260904_000001_key_monthly_budget.js +35 -0
  241. package/dist/db/migrations/20260904_000001_key_monthly_budget.js.map +1 -0
  242. package/dist/db/migrations/20260913_000001_request_model_attribution.d.ts +4 -0
  243. package/dist/db/migrations/20260913_000001_request_model_attribution.d.ts.map +1 -0
  244. package/dist/db/migrations/20260913_000001_request_model_attribution.js +68 -0
  245. package/dist/db/migrations/20260913_000001_request_model_attribution.js.map +1 -0
  246. package/dist/db/migrations/20260914_000001_key_monthly_usage.d.ts +5 -0
  247. package/dist/db/migrations/20260914_000001_key_monthly_usage.d.ts.map +1 -0
  248. package/dist/db/migrations/20260914_000001_key_monthly_usage.js +37 -0
  249. package/dist/db/migrations/20260914_000001_key_monthly_usage.js.map +1 -0
  250. package/dist/db/migrations/20260915_000001_quota_snapshot_freshness.d.ts +4 -0
  251. package/dist/db/migrations/20260915_000001_quota_snapshot_freshness.d.ts.map +1 -0
  252. package/dist/db/migrations/20260915_000001_quota_snapshot_freshness.js +28 -0
  253. package/dist/db/migrations/20260915_000001_quota_snapshot_freshness.js.map +1 -0
  254. package/dist/db/migrations/20261004_000001_unified_key_sr_prefix.d.ts +4 -0
  255. package/dist/db/migrations/20261004_000001_unified_key_sr_prefix.d.ts.map +1 -0
  256. package/dist/db/migrations/20261004_000001_unified_key_sr_prefix.js +25 -0
  257. package/dist/db/migrations/20261004_000001_unified_key_sr_prefix.js.map +1 -0
  258. package/dist/db/migrations/20261005_000001_key_budget_period.d.ts +10 -0
  259. package/dist/db/migrations/20261005_000001_key_budget_period.d.ts.map +1 -0
  260. package/dist/db/migrations/20261005_000001_key_budget_period.js +78 -0
  261. package/dist/db/migrations/20261005_000001_key_budget_period.js.map +1 -0
  262. package/dist/db/migrations/20261005_000002_key_budget_period_auto.d.ts +21 -0
  263. package/dist/db/migrations/20261005_000002_key_budget_period_auto.d.ts.map +1 -0
  264. package/dist/db/migrations/20261005_000002_key_budget_period_auto.js +41 -0
  265. package/dist/db/migrations/20261005_000002_key_budget_period_auto.js.map +1 -0
  266. package/dist/db/model-pricing.d.ts +39 -0
  267. package/dist/db/model-pricing.d.ts.map +1 -0
  268. package/dist/db/model-pricing.js +274 -0
  269. package/dist/db/model-pricing.js.map +1 -0
  270. package/dist/db/node-sqlite.d.ts +9 -0
  271. package/dist/db/node-sqlite.d.ts.map +1 -0
  272. package/dist/db/node-sqlite.js +87 -0
  273. package/dist/db/node-sqlite.js.map +1 -0
  274. package/dist/db/types.d.ts +22 -0
  275. package/dist/db/types.d.ts.map +1 -0
  276. package/dist/db/types.js +2 -0
  277. package/dist/db/types.js.map +1 -0
  278. package/dist/docs/docs-page.d.ts +2 -0
  279. package/dist/docs/docs-page.d.ts.map +1 -0
  280. package/dist/docs/docs-page.js +319 -0
  281. package/dist/docs/docs-page.js.map +1 -0
  282. package/dist/docs/openapi.d.ts +1896 -0
  283. package/dist/docs/openapi.d.ts.map +1 -0
  284. package/dist/docs/openapi.js +1096 -0
  285. package/dist/docs/openapi.js.map +1 -0
  286. package/dist/env.d.ts +2 -0
  287. package/dist/env.d.ts.map +1 -0
  288. package/dist/env.js +9 -0
  289. package/dist/env.js.map +1 -0
  290. package/dist/index.d.ts +2 -0
  291. package/dist/index.d.ts.map +1 -0
  292. package/dist/index.js +144 -0
  293. package/dist/index.js.map +1 -0
  294. package/dist/lib/anthropic-documents.d.ts +41 -0
  295. package/dist/lib/anthropic-documents.d.ts.map +1 -0
  296. package/dist/lib/anthropic-documents.js +132 -0
  297. package/dist/lib/anthropic-documents.js.map +1 -0
  298. package/dist/lib/app-version.d.ts +5 -0
  299. package/dist/lib/app-version.d.ts.map +1 -0
  300. package/dist/lib/app-version.js +61 -0
  301. package/dist/lib/app-version.js.map +1 -0
  302. package/dist/lib/attempt-trace.d.ts +23 -0
  303. package/dist/lib/attempt-trace.d.ts.map +1 -0
  304. package/dist/lib/attempt-trace.js +32 -0
  305. package/dist/lib/attempt-trace.js.map +1 -0
  306. package/dist/lib/budget.d.ts +6 -0
  307. package/dist/lib/budget.d.ts.map +1 -0
  308. package/dist/lib/budget.js +40 -0
  309. package/dist/lib/budget.js.map +1 -0
  310. package/dist/lib/client-classifier.d.ts +10 -0
  311. package/dist/lib/client-classifier.d.ts.map +1 -0
  312. package/dist/lib/client-classifier.js +126 -0
  313. package/dist/lib/client-classifier.js.map +1 -0
  314. package/dist/lib/client-context.d.ts +10 -0
  315. package/dist/lib/client-context.d.ts.map +1 -0
  316. package/dist/lib/client-context.js +39 -0
  317. package/dist/lib/client-context.js.map +1 -0
  318. package/dist/lib/config.d.ts +33 -0
  319. package/dist/lib/config.d.ts.map +1 -0
  320. package/dist/lib/config.js +94 -0
  321. package/dist/lib/config.js.map +1 -0
  322. package/dist/lib/content.d.ts +33 -0
  323. package/dist/lib/content.d.ts.map +1 -0
  324. package/dist/lib/content.js +201 -0
  325. package/dist/lib/content.js.map +1 -0
  326. package/dist/lib/credential.d.ts +11 -0
  327. package/dist/lib/credential.d.ts.map +1 -0
  328. package/dist/lib/credential.js +18 -0
  329. package/dist/lib/credential.js.map +1 -0
  330. package/dist/lib/crypto.d.ts +31 -0
  331. package/dist/lib/crypto.d.ts.map +1 -0
  332. package/dist/lib/crypto.js +206 -0
  333. package/dist/lib/crypto.js.map +1 -0
  334. package/dist/lib/custom-provider-cleanup.d.ts +13 -0
  335. package/dist/lib/custom-provider-cleanup.d.ts.map +1 -0
  336. package/dist/lib/custom-provider-cleanup.js +43 -0
  337. package/dist/lib/custom-provider-cleanup.js.map +1 -0
  338. package/dist/lib/db-backup.d.ts +31 -0
  339. package/dist/lib/db-backup.d.ts.map +1 -0
  340. package/dist/lib/db-backup.js +255 -0
  341. package/dist/lib/db-backup.js.map +1 -0
  342. package/dist/lib/endpoint-scope.d.ts +42 -0
  343. package/dist/lib/endpoint-scope.d.ts.map +1 -0
  344. package/dist/lib/endpoint-scope.js +95 -0
  345. package/dist/lib/endpoint-scope.js.map +1 -0
  346. package/dist/lib/env-drift.d.ts +17 -0
  347. package/dist/lib/env-drift.d.ts.map +1 -0
  348. package/dist/lib/env-drift.js +108 -0
  349. package/dist/lib/env-drift.js.map +1 -0
  350. package/dist/lib/error-classify.d.ts +40 -0
  351. package/dist/lib/error-classify.d.ts.map +1 -0
  352. package/dist/lib/error-classify.js +687 -0
  353. package/dist/lib/error-classify.js.map +1 -0
  354. package/dist/lib/error-redaction.d.ts +8 -0
  355. package/dist/lib/error-redaction.d.ts.map +1 -0
  356. package/dist/lib/error-redaction.js +45 -0
  357. package/dist/lib/error-redaction.js.map +1 -0
  358. package/dist/lib/fallback-loop.d.ts +306 -0
  359. package/dist/lib/fallback-loop.d.ts.map +1 -0
  360. package/dist/lib/fallback-loop.js +1336 -0
  361. package/dist/lib/fallback-loop.js.map +1 -0
  362. package/dist/lib/file-permissions.d.ts +98 -0
  363. package/dist/lib/file-permissions.d.ts.map +1 -0
  364. package/dist/lib/file-permissions.js +159 -0
  365. package/dist/lib/file-permissions.js.map +1 -0
  366. package/dist/lib/gemini-wire.d.ts +76 -0
  367. package/dist/lib/gemini-wire.d.ts.map +1 -0
  368. package/dist/lib/gemini-wire.js +392 -0
  369. package/dist/lib/gemini-wire.js.map +1 -0
  370. package/dist/lib/guardrails.d.ts +37 -0
  371. package/dist/lib/guardrails.d.ts.map +1 -0
  372. package/dist/lib/guardrails.js +106 -0
  373. package/dist/lib/guardrails.js.map +1 -0
  374. package/dist/lib/header-value.d.ts +8 -0
  375. package/dist/lib/header-value.d.ts.map +1 -0
  376. package/dist/lib/header-value.js +55 -0
  377. package/dist/lib/header-value.js.map +1 -0
  378. package/dist/lib/image-normalize.d.ts +21 -0
  379. package/dist/lib/image-normalize.d.ts.map +1 -0
  380. package/dist/lib/image-normalize.js +218 -0
  381. package/dist/lib/image-normalize.js.map +1 -0
  382. package/dist/lib/inbound-chat.d.ts +48 -0
  383. package/dist/lib/inbound-chat.d.ts.map +1 -0
  384. package/dist/lib/inbound-chat.js +406 -0
  385. package/dist/lib/inbound-chat.js.map +1 -0
  386. package/dist/lib/key-parser.d.ts +79 -0
  387. package/dist/lib/key-parser.d.ts.map +1 -0
  388. package/dist/lib/key-parser.js +701 -0
  389. package/dist/lib/key-parser.js.map +1 -0
  390. package/dist/lib/key-proxy.d.ts +49 -0
  391. package/dist/lib/key-proxy.d.ts.map +1 -0
  392. package/dist/lib/key-proxy.js +92 -0
  393. package/dist/lib/key-proxy.js.map +1 -0
  394. package/dist/lib/log-redaction.d.ts +28 -0
  395. package/dist/lib/log-redaction.d.ts.map +1 -0
  396. package/dist/lib/log-redaction.js +166 -0
  397. package/dist/lib/log-redaction.js.map +1 -0
  398. package/dist/lib/model-scope.d.ts +9 -0
  399. package/dist/lib/model-scope.d.ts.map +1 -0
  400. package/dist/lib/model-scope.js +29 -0
  401. package/dist/lib/model-scope.js.map +1 -0
  402. package/dist/lib/one-time-code.d.ts +15 -0
  403. package/dist/lib/one-time-code.d.ts.map +1 -0
  404. package/dist/lib/one-time-code.js +41 -0
  405. package/dist/lib/one-time-code.js.map +1 -0
  406. package/dist/lib/output-cap.d.ts +24 -0
  407. package/dist/lib/output-cap.d.ts.map +1 -0
  408. package/dist/lib/output-cap.js +80 -0
  409. package/dist/lib/output-cap.js.map +1 -0
  410. package/dist/lib/password.d.ts +3 -0
  411. package/dist/lib/password.d.ts.map +1 -0
  412. package/dist/lib/password.js +27 -0
  413. package/dist/lib/password.js.map +1 -0
  414. package/dist/lib/process-safety-net.d.ts +32 -0
  415. package/dist/lib/process-safety-net.d.ts.map +1 -0
  416. package/dist/lib/process-safety-net.js +123 -0
  417. package/dist/lib/process-safety-net.js.map +1 -0
  418. package/dist/lib/provider-identity.d.ts +37 -0
  419. package/dist/lib/provider-identity.d.ts.map +1 -0
  420. package/dist/lib/provider-identity.js +110 -0
  421. package/dist/lib/provider-identity.js.map +1 -0
  422. package/dist/lib/provider-size-parser.d.ts +6 -0
  423. package/dist/lib/provider-size-parser.d.ts.map +1 -0
  424. package/dist/lib/provider-size-parser.js +72 -0
  425. package/dist/lib/provider-size-parser.js.map +1 -0
  426. package/dist/lib/provider-timeout.d.ts +17 -0
  427. package/dist/lib/provider-timeout.d.ts.map +1 -0
  428. package/dist/lib/provider-timeout.js +70 -0
  429. package/dist/lib/provider-timeout.js.map +1 -0
  430. package/dist/lib/proxy.d.ts +180 -0
  431. package/dist/lib/proxy.d.ts.map +1 -0
  432. package/dist/lib/proxy.js +1002 -0
  433. package/dist/lib/proxy.js.map +1 -0
  434. package/dist/lib/request-log.d.ts +4 -0
  435. package/dist/lib/request-log.d.ts.map +1 -0
  436. package/dist/lib/request-log.js +161 -0
  437. package/dist/lib/request-log.js.map +1 -0
  438. package/dist/lib/reset-code.d.ts +5 -0
  439. package/dist/lib/reset-code.d.ts.map +1 -0
  440. package/dist/lib/reset-code.js +39 -0
  441. package/dist/lib/reset-code.js.map +1 -0
  442. package/dist/lib/retry-hint.d.ts +14 -0
  443. package/dist/lib/retry-hint.d.ts.map +1 -0
  444. package/dist/lib/retry-hint.js +36 -0
  445. package/dist/lib/retry-hint.js.map +1 -0
  446. package/dist/lib/route-live.d.ts +29 -0
  447. package/dist/lib/route-live.d.ts.map +1 -0
  448. package/dist/lib/route-live.js +97 -0
  449. package/dist/lib/route-live.js.map +1 -0
  450. package/dist/lib/sampling-params.d.ts +183 -0
  451. package/dist/lib/sampling-params.d.ts.map +1 -0
  452. package/dist/lib/sampling-params.js +386 -0
  453. package/dist/lib/sampling-params.js.map +1 -0
  454. package/dist/lib/scheduler.d.ts +13 -0
  455. package/dist/lib/scheduler.d.ts.map +1 -0
  456. package/dist/lib/scheduler.js +11 -0
  457. package/dist/lib/scheduler.js.map +1 -0
  458. package/dist/lib/served-model.d.ts +16 -0
  459. package/dist/lib/served-model.d.ts.map +1 -0
  460. package/dist/lib/served-model.js +0 -0
  461. package/dist/lib/served-model.js.map +1 -0
  462. package/dist/lib/server-logs.d.ts +132 -0
  463. package/dist/lib/server-logs.d.ts.map +1 -0
  464. package/dist/lib/server-logs.js +473 -0
  465. package/dist/lib/server-logs.js.map +1 -0
  466. package/dist/lib/setup-code.d.ts +5 -0
  467. package/dist/lib/setup-code.d.ts.map +1 -0
  468. package/dist/lib/setup-code.js +35 -0
  469. package/dist/lib/setup-code.js.map +1 -0
  470. package/dist/lib/structured-output.d.ts +9 -0
  471. package/dist/lib/structured-output.d.ts.map +1 -0
  472. package/dist/lib/structured-output.js +72 -0
  473. package/dist/lib/structured-output.js.map +1 -0
  474. package/dist/lib/system-prompt.d.ts +30 -0
  475. package/dist/lib/system-prompt.d.ts.map +1 -0
  476. package/dist/lib/system-prompt.js +66 -0
  477. package/dist/lib/system-prompt.js.map +1 -0
  478. package/dist/lib/task-type.d.ts +18 -0
  479. package/dist/lib/task-type.d.ts.map +1 -0
  480. package/dist/lib/task-type.js +92 -0
  481. package/dist/lib/task-type.js.map +1 -0
  482. package/dist/lib/think-tags.d.ts +37 -0
  483. package/dist/lib/think-tags.d.ts.map +1 -0
  484. package/dist/lib/think-tags.js +203 -0
  485. package/dist/lib/think-tags.js.map +1 -0
  486. package/dist/lib/tool-args.d.ts +36 -0
  487. package/dist/lib/tool-args.d.ts.map +1 -0
  488. package/dist/lib/tool-args.js +190 -0
  489. package/dist/lib/tool-args.js.map +1 -0
  490. package/dist/lib/tool-call-rescue.d.ts +62 -0
  491. package/dist/lib/tool-call-rescue.d.ts.map +1 -0
  492. package/dist/lib/tool-call-rescue.js +258 -0
  493. package/dist/lib/tool-call-rescue.js.map +1 -0
  494. package/dist/lib/tool-capability.d.ts +13 -0
  495. package/dist/lib/tool-capability.d.ts.map +1 -0
  496. package/dist/lib/tool-capability.js +70 -0
  497. package/dist/lib/tool-capability.js.map +1 -0
  498. package/dist/lib/tool-validate.d.ts +44 -0
  499. package/dist/lib/tool-validate.d.ts.map +1 -0
  500. package/dist/lib/tool-validate.js +165 -0
  501. package/dist/lib/tool-validate.js.map +1 -0
  502. package/dist/lib/ttfb-budget.d.ts +13 -0
  503. package/dist/lib/ttfb-budget.d.ts.map +1 -0
  504. package/dist/lib/ttfb-budget.js +153 -0
  505. package/dist/lib/ttfb-budget.js.map +1 -0
  506. package/dist/lib/url-guard.d.ts +47 -0
  507. package/dist/lib/url-guard.d.ts.map +1 -0
  508. package/dist/lib/url-guard.js +229 -0
  509. package/dist/lib/url-guard.js.map +1 -0
  510. package/dist/lib/wake-detect.d.ts +12 -0
  511. package/dist/lib/wake-detect.d.ts.map +1 -0
  512. package/dist/lib/wake-detect.js +99 -0
  513. package/dist/lib/wake-detect.js.map +1 -0
  514. package/dist/middleware/errorHandler.d.ts +3 -0
  515. package/dist/middleware/errorHandler.d.ts.map +1 -0
  516. package/dist/middleware/errorHandler.js +45 -0
  517. package/dist/middleware/errorHandler.js.map +1 -0
  518. package/dist/middleware/rateLimit.d.ts +4 -0
  519. package/dist/middleware/rateLimit.d.ts.map +1 -0
  520. package/dist/middleware/rateLimit.js +125 -0
  521. package/dist/middleware/rateLimit.js.map +1 -0
  522. package/dist/middleware/requireAuth.d.ts +3 -0
  523. package/dist/middleware/requireAuth.d.ts.map +1 -0
  524. package/dist/middleware/requireAuth.js +17 -0
  525. package/dist/middleware/requireAuth.js.map +1 -0
  526. package/dist/providers/aclide.d.ts +18 -0
  527. package/dist/providers/aclide.d.ts.map +1 -0
  528. package/dist/providers/aclide.js +174 -0
  529. package/dist/providers/aclide.js.map +1 -0
  530. package/dist/providers/aihorde.d.ts +42 -0
  531. package/dist/providers/aihorde.d.ts.map +1 -0
  532. package/dist/providers/aihorde.js +192 -0
  533. package/dist/providers/aihorde.js.map +1 -0
  534. package/dist/providers/airforce.d.ts +11 -0
  535. package/dist/providers/airforce.d.ts.map +1 -0
  536. package/dist/providers/airforce.js +32 -0
  537. package/dist/providers/airforce.js.map +1 -0
  538. package/dist/providers/base.d.ts +177 -0
  539. package/dist/providers/base.d.ts.map +1 -0
  540. package/dist/providers/base.js +354 -0
  541. package/dist/providers/base.js.map +1 -0
  542. package/dist/providers/blaze.d.ts +11 -0
  543. package/dist/providers/blaze.d.ts.map +1 -0
  544. package/dist/providers/blaze.js +33 -0
  545. package/dist/providers/blaze.js.map +1 -0
  546. package/dist/providers/blockrun.d.ts +14 -0
  547. package/dist/providers/blockrun.d.ts.map +1 -0
  548. package/dist/providers/blockrun.js +36 -0
  549. package/dist/providers/blockrun.js.map +1 -0
  550. package/dist/providers/clod.d.ts +11 -0
  551. package/dist/providers/clod.d.ts.map +1 -0
  552. package/dist/providers/clod.js +39 -0
  553. package/dist/providers/clod.js.map +1 -0
  554. package/dist/providers/cloudflare.d.ts +15 -0
  555. package/dist/providers/cloudflare.d.ts.map +1 -0
  556. package/dist/providers/cloudflare.js +175 -0
  557. package/dist/providers/cloudflare.js.map +1 -0
  558. package/dist/providers/cohere.d.ts +11 -0
  559. package/dist/providers/cohere.d.ts.map +1 -0
  560. package/dist/providers/cohere.js +117 -0
  561. package/dist/providers/cohere.js.map +1 -0
  562. package/dist/providers/dreamprompting.d.ts +11 -0
  563. package/dist/providers/dreamprompting.d.ts.map +1 -0
  564. package/dist/providers/dreamprompting.js +41 -0
  565. package/dist/providers/dreamprompting.js.map +1 -0
  566. package/dist/providers/electronhub.d.ts +10 -0
  567. package/dist/providers/electronhub.d.ts.map +1 -0
  568. package/dist/providers/electronhub.js +56 -0
  569. package/dist/providers/electronhub.js.map +1 -0
  570. package/dist/providers/experiential.d.ts +10 -0
  571. package/dist/providers/experiential.d.ts.map +1 -0
  572. package/dist/providers/experiential.js +33 -0
  573. package/dist/providers/experiential.js.map +1 -0
  574. package/dist/providers/gizmo.d.ts +15 -0
  575. package/dist/providers/gizmo.d.ts.map +1 -0
  576. package/dist/providers/gizmo.js +54 -0
  577. package/dist/providers/gizmo.js.map +1 -0
  578. package/dist/providers/google.d.ts +30 -0
  579. package/dist/providers/google.d.ts.map +1 -0
  580. package/dist/providers/google.js +766 -0
  581. package/dist/providers/google.js.map +1 -0
  582. package/dist/providers/index.d.ts +13 -0
  583. package/dist/providers/index.d.ts.map +1 -0
  584. package/dist/providers/index.js +561 -0
  585. package/dist/providers/index.js.map +1 -0
  586. package/dist/providers/llmtr.d.ts +15 -0
  587. package/dist/providers/llmtr.d.ts.map +1 -0
  588. package/dist/providers/llmtr.js +51 -0
  589. package/dist/providers/llmtr.js.map +1 -0
  590. package/dist/providers/logfare.d.ts +11 -0
  591. package/dist/providers/logfare.d.ts.map +1 -0
  592. package/dist/providers/logfare.js +33 -0
  593. package/dist/providers/logfare.js.map +1 -0
  594. package/dist/providers/lucidity.d.ts +11 -0
  595. package/dist/providers/lucidity.d.ts.map +1 -0
  596. package/dist/providers/lucidity.js +37 -0
  597. package/dist/providers/lucidity.js.map +1 -0
  598. package/dist/providers/modelscope.d.ts +50 -0
  599. package/dist/providers/modelscope.d.ts.map +1 -0
  600. package/dist/providers/modelscope.js +143 -0
  601. package/dist/providers/modelscope.js.map +1 -0
  602. package/dist/providers/moondream.d.ts +17 -0
  603. package/dist/providers/moondream.d.ts.map +1 -0
  604. package/dist/providers/moondream.js +140 -0
  605. package/dist/providers/moondream.js.map +1 -0
  606. package/dist/providers/openai-compat.d.ts +140 -0
  607. package/dist/providers/openai-compat.d.ts.map +1 -0
  608. package/dist/providers/openai-compat.js +537 -0
  609. package/dist/providers/openai-compat.js.map +1 -0
  610. package/dist/providers/opencode-free.d.ts +24 -0
  611. package/dist/providers/opencode-free.d.ts.map +1 -0
  612. package/dist/providers/opencode-free.js +262 -0
  613. package/dist/providers/opencode-free.js.map +1 -0
  614. package/dist/providers/pollinations.d.ts +32 -0
  615. package/dist/providers/pollinations.d.ts.map +1 -0
  616. package/dist/providers/pollinations.js +65 -0
  617. package/dist/providers/pollinations.js.map +1 -0
  618. package/dist/providers/router9.d.ts +16 -0
  619. package/dist/providers/router9.d.ts.map +1 -0
  620. package/dist/providers/router9.js +42 -0
  621. package/dist/providers/router9.js.map +1 -0
  622. package/dist/providers/sail.d.ts +44 -0
  623. package/dist/providers/sail.d.ts.map +1 -0
  624. package/dist/providers/sail.js +302 -0
  625. package/dist/providers/sail.js.map +1 -0
  626. package/dist/providers/septor.d.ts +10 -0
  627. package/dist/providers/septor.d.ts.map +1 -0
  628. package/dist/providers/septor.js +36 -0
  629. package/dist/providers/septor.js.map +1 -0
  630. package/dist/providers/speechify.d.ts +14 -0
  631. package/dist/providers/speechify.d.ts.map +1 -0
  632. package/dist/providers/speechify.js +28 -0
  633. package/dist/providers/speechify.js.map +1 -0
  634. package/dist/providers/speka.d.ts +12 -0
  635. package/dist/providers/speka.d.ts.map +1 -0
  636. package/dist/providers/speka.js +37 -0
  637. package/dist/providers/speka.js.map +1 -0
  638. package/dist/providers/waterfall.d.ts +11 -0
  639. package/dist/providers/waterfall.d.ts.map +1 -0
  640. package/dist/providers/waterfall.js +33 -0
  641. package/dist/providers/waterfall.js.map +1 -0
  642. package/dist/providers/xkiro.d.ts +71 -0
  643. package/dist/providers/xkiro.d.ts.map +1 -0
  644. package/dist/providers/xkiro.js +119 -0
  645. package/dist/providers/xkiro.js.map +1 -0
  646. package/dist/providers/zhipu.d.ts +43 -0
  647. package/dist/providers/zhipu.d.ts.map +1 -0
  648. package/dist/providers/zhipu.js +95 -0
  649. package/dist/providers/zhipu.js.map +1 -0
  650. package/dist/routes/analytics.d.ts +2 -0
  651. package/dist/routes/analytics.d.ts.map +1 -0
  652. package/dist/routes/analytics.js +738 -0
  653. package/dist/routes/analytics.js.map +1 -0
  654. package/dist/routes/anthropic.d.ts +7 -0
  655. package/dist/routes/anthropic.d.ts.map +1 -0
  656. package/dist/routes/anthropic.js +1076 -0
  657. package/dist/routes/anthropic.js.map +1 -0
  658. package/dist/routes/auth.d.ts +2 -0
  659. package/dist/routes/auth.d.ts.map +1 -0
  660. package/dist/routes/auth.js +310 -0
  661. package/dist/routes/auth.js.map +1 -0
  662. package/dist/routes/backups.d.ts +2 -0
  663. package/dist/routes/backups.d.ts.map +1 -0
  664. package/dist/routes/backups.js +127 -0
  665. package/dist/routes/backups.js.map +1 -0
  666. package/dist/routes/cache.d.ts +2 -0
  667. package/dist/routes/cache.d.ts.map +1 -0
  668. package/dist/routes/cache.js +40 -0
  669. package/dist/routes/cache.js.map +1 -0
  670. package/dist/routes/client-profiles.d.ts +2 -0
  671. package/dist/routes/client-profiles.d.ts.map +1 -0
  672. package/dist/routes/client-profiles.js +128 -0
  673. package/dist/routes/client-profiles.js.map +1 -0
  674. package/dist/routes/compression.d.ts +2 -0
  675. package/dist/routes/compression.d.ts.map +1 -0
  676. package/dist/routes/compression.js +82 -0
  677. package/dist/routes/compression.js.map +1 -0
  678. package/dist/routes/conversations.d.ts +3 -0
  679. package/dist/routes/conversations.d.ts.map +1 -0
  680. package/dist/routes/conversations.js +215 -0
  681. package/dist/routes/conversations.js.map +1 -0
  682. package/dist/routes/docs.d.ts +2 -0
  683. package/dist/routes/docs.d.ts.map +1 -0
  684. package/dist/routes/docs.js +21 -0
  685. package/dist/routes/docs.js.map +1 -0
  686. package/dist/routes/embeddings.d.ts +2 -0
  687. package/dist/routes/embeddings.d.ts.map +1 -0
  688. package/dist/routes/embeddings.js +255 -0
  689. package/dist/routes/embeddings.js.map +1 -0
  690. package/dist/routes/fallback.d.ts +2 -0
  691. package/dist/routes/fallback.d.ts.map +1 -0
  692. package/dist/routes/fallback.js +631 -0
  693. package/dist/routes/fallback.js.map +1 -0
  694. package/dist/routes/free-tier.d.ts +22 -0
  695. package/dist/routes/free-tier.d.ts.map +1 -0
  696. package/dist/routes/free-tier.js +175 -0
  697. package/dist/routes/free-tier.js.map +1 -0
  698. package/dist/routes/gemini.d.ts +2 -0
  699. package/dist/routes/gemini.d.ts.map +1 -0
  700. package/dist/routes/gemini.js +218 -0
  701. package/dist/routes/gemini.js.map +1 -0
  702. package/dist/routes/health.d.ts +2 -0
  703. package/dist/routes/health.d.ts.map +1 -0
  704. package/dist/routes/health.js +72 -0
  705. package/dist/routes/health.js.map +1 -0
  706. package/dist/routes/keys.d.ts +17 -0
  707. package/dist/routes/keys.d.ts.map +1 -0
  708. package/dist/routes/keys.js +1787 -0
  709. package/dist/routes/keys.js.map +1 -0
  710. package/dist/routes/logs.d.ts +2 -0
  711. package/dist/routes/logs.d.ts.map +1 -0
  712. package/dist/routes/logs.js +88 -0
  713. package/dist/routes/logs.js.map +1 -0
  714. package/dist/routes/mcp.d.ts +4 -0
  715. package/dist/routes/mcp.d.ts.map +1 -0
  716. package/dist/routes/mcp.js +563 -0
  717. package/dist/routes/mcp.js.map +1 -0
  718. package/dist/routes/media.d.ts +2 -0
  719. package/dist/routes/media.d.ts.map +1 -0
  720. package/dist/routes/media.js +180 -0
  721. package/dist/routes/media.js.map +1 -0
  722. package/dist/routes/models.d.ts +2 -0
  723. package/dist/routes/models.d.ts.map +1 -0
  724. package/dist/routes/models.js +439 -0
  725. package/dist/routes/models.js.map +1 -0
  726. package/dist/routes/ollama.d.ts +7 -0
  727. package/dist/routes/ollama.d.ts.map +1 -0
  728. package/dist/routes/ollama.js +595 -0
  729. package/dist/routes/ollama.js.map +1 -0
  730. package/dist/routes/profiles.d.ts +3 -0
  731. package/dist/routes/profiles.d.ts.map +1 -0
  732. package/dist/routes/profiles.js +431 -0
  733. package/dist/routes/profiles.js.map +1 -0
  734. package/dist/routes/proxy.d.ts +32 -0
  735. package/dist/routes/proxy.d.ts.map +1 -0
  736. package/dist/routes/proxy.js +2793 -0
  737. package/dist/routes/proxy.js.map +1 -0
  738. package/dist/routes/responses.d.ts +1161 -0
  739. package/dist/routes/responses.d.ts.map +1 -0
  740. package/dist/routes/responses.js +1521 -0
  741. package/dist/routes/responses.js.map +1 -0
  742. package/dist/routes/settings.d.ts +2 -0
  743. package/dist/routes/settings.d.ts.map +1 -0
  744. package/dist/routes/settings.js +549 -0
  745. package/dist/routes/settings.js.map +1 -0
  746. package/dist/routes/status.d.ts +3 -0
  747. package/dist/routes/status.d.ts.map +1 -0
  748. package/dist/routes/status.js +214 -0
  749. package/dist/routes/status.js.map +1 -0
  750. package/dist/routes/update.d.ts +31 -0
  751. package/dist/routes/update.d.ts.map +1 -0
  752. package/dist/routes/update.js +536 -0
  753. package/dist/routes/update.js.map +1 -0
  754. package/dist/routes/url-tokens.d.ts +2 -0
  755. package/dist/routes/url-tokens.d.ts.map +1 -0
  756. package/dist/routes/url-tokens.js +31 -0
  757. package/dist/routes/url-tokens.js.map +1 -0
  758. package/dist/scripts/export-catalog.d.ts +2 -0
  759. package/dist/scripts/export-catalog.d.ts.map +1 -0
  760. package/dist/scripts/export-catalog.js +114 -0
  761. package/dist/scripts/export-catalog.js.map +1 -0
  762. package/dist/scripts/rotate-encryption-key.d.ts +81 -0
  763. package/dist/scripts/rotate-encryption-key.d.ts.map +1 -0
  764. package/dist/scripts/rotate-encryption-key.js +232 -0
  765. package/dist/scripts/rotate-encryption-key.js.map +1 -0
  766. package/dist/scripts/routing-sim.d.ts +2 -0
  767. package/dist/scripts/routing-sim.d.ts.map +1 -0
  768. package/dist/scripts/routing-sim.js +130 -0
  769. package/dist/scripts/routing-sim.js.map +1 -0
  770. package/dist/scripts/test-all-models.d.ts +2 -0
  771. package/dist/scripts/test-all-models.d.ts.map +1 -0
  772. package/dist/scripts/test-all-models.js +56 -0
  773. package/dist/scripts/test-all-models.js.map +1 -0
  774. package/dist/services/anthropic-map.d.ts +39 -0
  775. package/dist/services/anthropic-map.d.ts.map +1 -0
  776. package/dist/services/anthropic-map.js +138 -0
  777. package/dist/services/anthropic-map.js.map +1 -0
  778. package/dist/services/auth.d.ts +30 -0
  779. package/dist/services/auth.d.ts.map +1 -0
  780. package/dist/services/auth.js +124 -0
  781. package/dist/services/auth.js.map +1 -0
  782. package/dist/services/auto-discover.d.ts +77 -0
  783. package/dist/services/auto-discover.d.ts.map +1 -0
  784. package/dist/services/auto-discover.js +1047 -0
  785. package/dist/services/auto-discover.js.map +1 -0
  786. package/dist/services/backups.d.ts +67 -0
  787. package/dist/services/backups.d.ts.map +1 -0
  788. package/dist/services/backups.js +444 -0
  789. package/dist/services/backups.js.map +1 -0
  790. package/dist/services/builtin-model-discovery.d.ts +107 -0
  791. package/dist/services/builtin-model-discovery.d.ts.map +1 -0
  792. package/dist/services/builtin-model-discovery.js +296 -0
  793. package/dist/services/builtin-model-discovery.js.map +1 -0
  794. package/dist/services/cache.d.ts +190 -0
  795. package/dist/services/cache.d.ts.map +1 -0
  796. package/dist/services/cache.js +617 -0
  797. package/dist/services/cache.js.map +1 -0
  798. package/dist/services/catalog-sync.d.ts +257 -0
  799. package/dist/services/catalog-sync.d.ts.map +1 -0
  800. package/dist/services/catalog-sync.js +1041 -0
  801. package/dist/services/catalog-sync.js.map +1 -0
  802. package/dist/services/compression/config.d.ts +33 -0
  803. package/dist/services/compression/config.d.ts.map +1 -0
  804. package/dist/services/compression/config.js +158 -0
  805. package/dist/services/compression/config.js.map +1 -0
  806. package/dist/services/compression/engines/aging.d.ts +2 -0
  807. package/dist/services/compression/engines/aging.d.ts.map +1 -0
  808. package/dist/services/compression/engines/aging.js +53 -0
  809. package/dist/services/compression/engines/aging.js.map +1 -0
  810. package/dist/services/compression/engines/custom-filters.d.ts +4 -0
  811. package/dist/services/compression/engines/custom-filters.d.ts.map +1 -0
  812. package/dist/services/compression/engines/custom-filters.js +49 -0
  813. package/dist/services/compression/engines/custom-filters.js.map +1 -0
  814. package/dist/services/compression/engines/dedup.d.ts +2 -0
  815. package/dist/services/compression/engines/dedup.d.ts.map +1 -0
  816. package/dist/services/compression/engines/dedup.js +61 -0
  817. package/dist/services/compression/engines/dedup.js.map +1 -0
  818. package/dist/services/compression/engines/filter-definitions.d.ts +75 -0
  819. package/dist/services/compression/engines/filter-definitions.d.ts.map +1 -0
  820. package/dist/services/compression/engines/filter-definitions.js +45 -0
  821. package/dist/services/compression/engines/filter-definitions.js.map +1 -0
  822. package/dist/services/compression/engines/hard-budget.d.ts +2 -0
  823. package/dist/services/compression/engines/hard-budget.d.ts.map +1 -0
  824. package/dist/services/compression/engines/hard-budget.js +52 -0
  825. package/dist/services/compression/engines/hard-budget.js.map +1 -0
  826. package/dist/services/compression/engines/index.d.ts +9 -0
  827. package/dist/services/compression/engines/index.d.ts.map +1 -0
  828. package/dist/services/compression/engines/index.js +9 -0
  829. package/dist/services/compression/engines/index.js.map +1 -0
  830. package/dist/services/compression/engines/jsoncompact.d.ts +3 -0
  831. package/dist/services/compression/engines/jsoncompact.d.ts.map +1 -0
  832. package/dist/services/compression/engines/jsoncompact.js +145 -0
  833. package/dist/services/compression/engines/jsoncompact.js.map +1 -0
  834. package/dist/services/compression/engines/lite.d.ts +2 -0
  835. package/dist/services/compression/engines/lite.d.ts.map +1 -0
  836. package/dist/services/compression/engines/lite.js +34 -0
  837. package/dist/services/compression/engines/lite.js.map +1 -0
  838. package/dist/services/compression/engines/read-lifecycle.d.ts +2 -0
  839. package/dist/services/compression/engines/read-lifecycle.d.ts.map +1 -0
  840. package/dist/services/compression/engines/read-lifecycle.js +70 -0
  841. package/dist/services/compression/engines/read-lifecycle.js.map +1 -0
  842. package/dist/services/compression/engines/relevance.d.ts +2 -0
  843. package/dist/services/compression/engines/relevance.d.ts.map +1 -0
  844. package/dist/services/compression/engines/relevance.js +74 -0
  845. package/dist/services/compression/engines/relevance.js.map +1 -0
  846. package/dist/services/compression/engines/toolfilter.d.ts +2 -0
  847. package/dist/services/compression/engines/toolfilter.d.ts.map +1 -0
  848. package/dist/services/compression/engines/toolfilter.js +195 -0
  849. package/dist/services/compression/engines/toolfilter.js.map +1 -0
  850. package/dist/services/compression/fidelity-gate.d.ts +12 -0
  851. package/dist/services/compression/fidelity-gate.d.ts.map +1 -0
  852. package/dist/services/compression/fidelity-gate.js +90 -0
  853. package/dist/services/compression/fidelity-gate.js.map +1 -0
  854. package/dist/services/compression/helpers.d.ts +9 -0
  855. package/dist/services/compression/helpers.d.ts.map +1 -0
  856. package/dist/services/compression/helpers.js +52 -0
  857. package/dist/services/compression/helpers.js.map +1 -0
  858. package/dist/services/compression/pipeline.d.ts +7 -0
  859. package/dist/services/compression/pipeline.d.ts.map +1 -0
  860. package/dist/services/compression/pipeline.js +245 -0
  861. package/dist/services/compression/pipeline.js.map +1 -0
  862. package/dist/services/compression/preservation.d.ts +27 -0
  863. package/dist/services/compression/preservation.d.ts.map +1 -0
  864. package/dist/services/compression/preservation.js +109 -0
  865. package/dist/services/compression/preservation.js.map +1 -0
  866. package/dist/services/compression/registry.d.ts +6 -0
  867. package/dist/services/compression/registry.d.ts.map +1 -0
  868. package/dist/services/compression/registry.js +16 -0
  869. package/dist/services/compression/registry.js.map +1 -0
  870. package/dist/services/compression/stats.d.ts +29 -0
  871. package/dist/services/compression/stats.d.ts.map +1 -0
  872. package/dist/services/compression/stats.js +55 -0
  873. package/dist/services/compression/stats.js.map +1 -0
  874. package/dist/services/compression/types.d.ts +90 -0
  875. package/dist/services/compression/types.d.ts.map +1 -0
  876. package/dist/services/compression/types.js +2 -0
  877. package/dist/services/compression/types.js.map +1 -0
  878. package/dist/services/context-handoff.d.ts +22 -0
  879. package/dist/services/context-handoff.d.ts.map +1 -0
  880. package/dist/services/context-handoff.js +164 -0
  881. package/dist/services/context-handoff.js.map +1 -0
  882. package/dist/services/cooldown-probe.d.ts +24 -0
  883. package/dist/services/cooldown-probe.d.ts.map +1 -0
  884. package/dist/services/cooldown-probe.js +181 -0
  885. package/dist/services/cooldown-probe.js.map +1 -0
  886. package/dist/services/custom-endpoint.d.ts +58 -0
  887. package/dist/services/custom-endpoint.d.ts.map +1 -0
  888. package/dist/services/custom-endpoint.js +167 -0
  889. package/dist/services/custom-endpoint.js.map +1 -0
  890. package/dist/services/custom-media-register.d.ts +22 -0
  891. package/dist/services/custom-media-register.d.ts.map +1 -0
  892. package/dist/services/custom-media-register.js +45 -0
  893. package/dist/services/custom-media-register.js.map +1 -0
  894. package/dist/services/custom-model-register.d.ts +50 -0
  895. package/dist/services/custom-model-register.d.ts.map +1 -0
  896. package/dist/services/custom-model-register.js +103 -0
  897. package/dist/services/custom-model-register.js.map +1 -0
  898. package/dist/services/custom-model-seed.d.ts +18 -0
  899. package/dist/services/custom-model-seed.d.ts.map +1 -0
  900. package/dist/services/custom-model-seed.js +42 -0
  901. package/dist/services/custom-model-seed.js.map +1 -0
  902. package/dist/services/custom-model-sync.d.ts +37 -0
  903. package/dist/services/custom-model-sync.d.ts.map +1 -0
  904. package/dist/services/custom-model-sync.js +118 -0
  905. package/dist/services/custom-model-sync.js.map +1 -0
  906. package/dist/services/custom-model-tombstone.d.ts +5 -0
  907. package/dist/services/custom-model-tombstone.d.ts.map +1 -0
  908. package/dist/services/custom-model-tombstone.js +30 -0
  909. package/dist/services/custom-model-tombstone.js.map +1 -0
  910. package/dist/services/declarative-config.d.ts +356 -0
  911. package/dist/services/declarative-config.d.ts.map +1 -0
  912. package/dist/services/declarative-config.js +427 -0
  913. package/dist/services/declarative-config.js.map +1 -0
  914. package/dist/services/degradation.d.ts +40 -0
  915. package/dist/services/degradation.d.ts.map +1 -0
  916. package/dist/services/degradation.js +119 -0
  917. package/dist/services/degradation.js.map +1 -0
  918. package/dist/services/embeddings.d.ts +76 -0
  919. package/dist/services/embeddings.d.ts.map +1 -0
  920. package/dist/services/embeddings.js +319 -0
  921. package/dist/services/embeddings.js.map +1 -0
  922. package/dist/services/fusion.d.ts +162 -0
  923. package/dist/services/fusion.d.ts.map +1 -0
  924. package/dist/services/fusion.js +789 -0
  925. package/dist/services/fusion.js.map +1 -0
  926. package/dist/services/gemini-map.d.ts +14 -0
  927. package/dist/services/gemini-map.d.ts.map +1 -0
  928. package/dist/services/gemini-map.js +78 -0
  929. package/dist/services/gemini-map.js.map +1 -0
  930. package/dist/services/health.d.ts +52 -0
  931. package/dist/services/health.d.ts.map +1 -0
  932. package/dist/services/health.js +352 -0
  933. package/dist/services/health.js.map +1 -0
  934. package/dist/services/idempotency.d.ts +49 -0
  935. package/dist/services/idempotency.d.ts.map +1 -0
  936. package/dist/services/idempotency.js +140 -0
  937. package/dist/services/idempotency.js.map +1 -0
  938. package/dist/services/key-budget.d.ts +74 -0
  939. package/dist/services/key-budget.d.ts.map +1 -0
  940. package/dist/services/key-budget.js +200 -0
  941. package/dist/services/key-budget.js.map +1 -0
  942. package/dist/services/media.d.ts +123 -0
  943. package/dist/services/media.d.ts.map +1 -0
  944. package/dist/services/media.js +954 -0
  945. package/dist/services/media.js.map +1 -0
  946. package/dist/services/model-discovery.d.ts +126 -0
  947. package/dist/services/model-discovery.d.ts.map +1 -0
  948. package/dist/services/model-discovery.js +662 -0
  949. package/dist/services/model-discovery.js.map +1 -0
  950. package/dist/services/model-groups.d.ts +149 -0
  951. package/dist/services/model-groups.d.ts.map +1 -0
  952. package/dist/services/model-groups.js +314 -0
  953. package/dist/services/model-groups.js.map +1 -0
  954. package/dist/services/model-listing.d.ts +18 -0
  955. package/dist/services/model-listing.d.ts.map +1 -0
  956. package/dist/services/model-listing.js +94 -0
  957. package/dist/services/model-listing.js.map +1 -0
  958. package/dist/services/model-retirement.d.ts +29 -0
  959. package/dist/services/model-retirement.d.ts.map +1 -0
  960. package/dist/services/model-retirement.js +86 -0
  961. package/dist/services/model-retirement.js.map +1 -0
  962. package/dist/services/model-state.d.ts +90 -0
  963. package/dist/services/model-state.d.ts.map +1 -0
  964. package/dist/services/model-state.js +342 -0
  965. package/dist/services/model-state.js.map +1 -0
  966. package/dist/services/model-weight-overrides.d.ts +48 -0
  967. package/dist/services/model-weight-overrides.d.ts.map +1 -0
  968. package/dist/services/model-weight-overrides.js +127 -0
  969. package/dist/services/model-weight-overrides.js.map +1 -0
  970. package/dist/services/notification-emitter.d.ts +50 -0
  971. package/dist/services/notification-emitter.d.ts.map +1 -0
  972. package/dist/services/notification-emitter.js +177 -0
  973. package/dist/services/notification-emitter.js.map +1 -0
  974. package/dist/services/opencode-free-sync.d.ts +29 -0
  975. package/dist/services/opencode-free-sync.d.ts.map +1 -0
  976. package/dist/services/opencode-free-sync.js +206 -0
  977. package/dist/services/opencode-free-sync.js.map +1 -0
  978. package/dist/services/penalty-inspector.d.ts +56 -0
  979. package/dist/services/penalty-inspector.d.ts.map +1 -0
  980. package/dist/services/penalty-inspector.js +167 -0
  981. package/dist/services/penalty-inspector.js.map +1 -0
  982. package/dist/services/profile-models.d.ts +5 -0
  983. package/dist/services/profile-models.d.ts.map +1 -0
  984. package/dist/services/profile-models.js +62 -0
  985. package/dist/services/profile-models.js.map +1 -0
  986. package/dist/services/provider-credential.d.ts +19 -0
  987. package/dist/services/provider-credential.d.ts.map +1 -0
  988. package/dist/services/provider-credential.js +51 -0
  989. package/dist/services/provider-credential.js.map +1 -0
  990. package/dist/services/provider-quota.d.ts +64 -0
  991. package/dist/services/provider-quota.d.ts.map +1 -0
  992. package/dist/services/provider-quota.js +563 -0
  993. package/dist/services/provider-quota.js.map +1 -0
  994. package/dist/services/quirks.d.ts +0 -0
  995. package/dist/services/quirks.d.ts.map +1 -0
  996. package/dist/services/quirks.js +0 -0
  997. package/dist/services/quirks.js.map +1 -0
  998. package/dist/services/quota-forecast.d.ts +50 -0
  999. package/dist/services/quota-forecast.d.ts.map +1 -0
  1000. package/dist/services/quota-forecast.js +150 -0
  1001. package/dist/services/quota-forecast.js.map +1 -0
  1002. package/dist/services/quota-outlook.d.ts +4 -0
  1003. package/dist/services/quota-outlook.d.ts.map +1 -0
  1004. package/dist/services/quota-outlook.js +92 -0
  1005. package/dist/services/quota-outlook.js.map +1 -0
  1006. package/dist/services/ratelimit.d.ts +249 -0
  1007. package/dist/services/ratelimit.d.ts.map +1 -0
  1008. package/dist/services/ratelimit.js +1182 -0
  1009. package/dist/services/ratelimit.js.map +1 -0
  1010. package/dist/services/request-retention.d.ts +60 -0
  1011. package/dist/services/request-retention.d.ts.map +1 -0
  1012. package/dist/services/request-retention.js +229 -0
  1013. package/dist/services/request-retention.js.map +1 -0
  1014. package/dist/services/router.d.ts +384 -0
  1015. package/dist/services/router.d.ts.map +1 -0
  1016. package/dist/services/router.js +1986 -0
  1017. package/dist/services/router.js.map +1 -0
  1018. package/dist/services/scoring.d.ts +156 -0
  1019. package/dist/services/scoring.d.ts.map +1 -0
  1020. package/dist/services/scoring.js +435 -0
  1021. package/dist/services/scoring.js.map +1 -0
  1022. package/dist/services/url-tokens.d.ts +15 -0
  1023. package/dist/services/url-tokens.d.ts.map +1 -0
  1024. package/dist/services/url-tokens.js +55 -0
  1025. package/dist/services/url-tokens.js.map +1 -0
  1026. package/package.json +77 -4
  1027. package/README.md +0 -3
@@ -0,0 +1,1521 @@
1
+ import crypto from 'crypto';
2
+ import { Router } from 'express';
3
+ import { z } from 'zod';
4
+ import { routeRequest, hasEnabledVisionModel, hasEnabledToolsModel, resolveStickyPreference, routingReserveTokens, resolveModelGroupCandidates, resolveRoutingChain } from '../services/router.js';
5
+ import { getDb } from '../db/index.js';
6
+ import { resolveAuth, prependSystemPrompt } from '../lib/system-prompt.js';
7
+ import { isUnifyEnabled, getModelGroups, resolveRequestedIdForDispatch } from '../services/model-groups.js';
8
+ import { contentToString, estimateInputTokens, messageHasImage } from '../lib/content.js';
9
+ import { routeOutputBudget } from '../lib/output-cap.js';
10
+ import { resolveTaskType } from '../lib/task-type.js';
11
+ import { normalizeMessageImages } from '../lib/image-normalize.js';
12
+ import { repairToolArguments, toolSchemaMap } from '../lib/tool-args.js';
13
+ import { invalidToolArgumentsError, invalidToolCallReasons, isToolArgumentValidationEnabled } from '../lib/tool-validate.js';
14
+ import { rescueInlineToolCalls, startsWithDialectMarker, couldBecomeDialectMarker, containsDialectMarker } from '../lib/tool-call-rescue.js';
15
+ import { extractApiToken, getRequestGroupId, getStickyModel, setStickyModel, streamReasoningText, completionReasoningText, traceRouteEvent, logRequest, } from './proxy.js';
16
+ import { runFallbackLoop, newFallbackState, fallbackRoutingTokens, recordUpstreamSuccess, setFallbackHeaders, exhaustionErrorPayload, setExhaustionHeaders } from '../lib/fallback-loop.js';
17
+ import { routedViaValue, safeHeaderValue } from '../lib/header-value.js';
18
+ import { applyTokenBudget, tokenBudgetMessage } from '../lib/guardrails.js';
19
+ import { samplingParamSchemaFields, pickSamplingParams } from '../lib/sampling-params.js';
20
+ import { enforceJsonContent } from '../lib/structured-output.js';
21
+ import { sanitizeProviderErrorMessage } from '../lib/error-redaction.js';
22
+ import { isClientAbortError, newClientAbortError, newHedgeAbortError } from '../lib/error-classify.js';
23
+ import { inferQuotaPoolKey } from '../services/provider-quota.js';
24
+ import { compressRequest, formatCompressionHeader } from '../services/compression/pipeline.js';
25
+ import { FUSION_MODEL_ID, FusionError, fusionConfigSchema, isFusionModel, runFusion, } from '../services/fusion.js';
26
+ export const responsesRouter = Router();
27
+ const AUTO_MODEL_ID = 'auto';
28
+ function isAutoModel(modelId) {
29
+ if (!modelId)
30
+ return true;
31
+ const lower = modelId.toLowerCase();
32
+ return lower === AUTO_MODEL_ID || lower.startsWith(`${AUTO_MODEL_ID}:`);
33
+ }
34
+ // ─────────────────────────────────────────────────────────────────────────
35
+ // OpenAI Responses API shim (POST /v1/responses).
36
+ //
37
+ // Current Codex versions only speak the Responses API — `wire_api = "chat"`
38
+ // is rejected — so the existing /v1/chat/completions endpoint isn't reachable
39
+ // from Codex (see issue #96). This endpoint accepts a Responses-shaped request,
40
+ // translates it to the internal chat-message format, runs it through the SAME
41
+ // shared fallback loop as the proxy (lib/fallback-loop.ts), and translates the
42
+ // result back into the Responses object / SSE event stream Codex expects.
43
+ //
44
+ // A thin adapter: the cooldown/skip/penalty/exhaustion machinery is shared, and
45
+ // only the Responses request/stream translation lives here. This is what fixed
46
+ // the drift where /v1/responses ignored a provider Retry-After and under-benched
47
+ // a 403 (both are now handled identically to /chat/completions by the shared
48
+ // loop) and committed the SSE skeleton on the first raw chunk (the commit point
49
+ // is now held until the first meaningful content, so a pre-content failure fails
50
+ // over invisibly).
51
+ // ─────────────────────────────────────────────────────────────────────────
52
+ const MAX_RETRIES = 20;
53
+ function newId(prefix) {
54
+ return `${prefix}_${crypto.randomBytes(18).toString('hex')}`;
55
+ }
56
+ function nowUnix() {
57
+ return Math.floor(Date.now() / 1000);
58
+ }
59
+ // ── Request schema ──────────────────────────────────────────────────────
60
+ // Lenient on purpose: the Responses API surface is large and evolving, and we
61
+ // only consume the fields we can map. Unknown fields (store, reasoning,
62
+ // metadata, previous_response_id, …) are accepted and ignored.
63
+ const contentPartSchema = z.object({ type: z.string() }).passthrough();
64
+ const messageItemSchema = z.object({
65
+ type: z.literal('message').optional(),
66
+ role: z.enum(['system', 'developer', 'user', 'assistant']),
67
+ content: z.union([z.string(), z.array(contentPartSchema)]),
68
+ });
69
+ const functionCallItemSchema = z.object({
70
+ type: z.literal('function_call'),
71
+ call_id: z.string(),
72
+ name: z.string(),
73
+ arguments: z.string(),
74
+ id: z.string().optional(),
75
+ });
76
+ const functionCallOutputItemSchema = z.object({
77
+ type: z.literal('function_call_output'),
78
+ call_id: z.string(),
79
+ output: z.union([z.string(), z.array(contentPartSchema), z.record(z.string(), z.unknown())]),
80
+ });
81
+ // Remaining official ResponseInputItemParam kinds. Codex computer-use round-trips
82
+ // `computer_call` (the model's action request) and `computer_call_output` (the
83
+ // harness's result, incl. screenshots); multi-turn sessions also replay
84
+ // `reasoning` / `local_shell_call` items. We accept them all so validation never
85
+ // 400s on a standard payload; each is then either mapped (below) or dropped
86
+ // because chat-completions upstreams have no equivalent (computer/local_shell).
87
+ // Each schema is permissive — we only consume the fields that matter.
88
+ const computerCallItemSchema = z.object({
89
+ type: z.literal('computer_call'),
90
+ call_id: z.string(),
91
+ action: z.record(z.string(), z.unknown()).optional(),
92
+ id: z.string().optional(),
93
+ }).passthrough();
94
+ const computerCallOutputItemSchema = z.object({
95
+ type: z.literal('computer_call_output'),
96
+ call_id: z.string(),
97
+ output: z.union([
98
+ z.string(),
99
+ z.array(contentPartSchema),
100
+ z.record(z.string(), z.unknown()),
101
+ ]).optional(),
102
+ id: z.string().optional(),
103
+ }).passthrough();
104
+ const reasoningItemSchema = z.object({
105
+ type: z.literal('reasoning'),
106
+ summary: z.union([z.string(), z.array(contentPartSchema)]).optional(),
107
+ content: z.union([z.string(), z.array(contentPartSchema)]).optional(),
108
+ id: z.string().optional(),
109
+ }).passthrough();
110
+ const localShellCallItemSchema = z.object({
111
+ type: z.literal('local_shell_call'),
112
+ call_id: z.string().optional(),
113
+ action: z.record(z.string(), z.unknown()).optional(),
114
+ id: z.string().optional(),
115
+ }).passthrough();
116
+ // Codex sends its client-side tool inventory as an `additional_tools` input
117
+ // item on every Responses turn. It is metadata for the harness, not a chat
118
+ // message, so the translator deliberately drops it below. Keep the schema
119
+ // permissive because Codex may add fields (or tool shapes) as the inventory
120
+ // evolves; rejecting the whole request here turns an otherwise valid launch
121
+ // into a misleading `input: Invalid input` 400.
122
+ const additionalToolsItemSchema = z.object({
123
+ type: z.literal('additional_tools'),
124
+ id: z.string().optional(),
125
+ role: z.string().optional(),
126
+ tools: z.array(z.record(z.string(), z.unknown())).optional(),
127
+ }).passthrough();
128
+ // The rest of the official ResponseInputItemParam union: built-in tool calls
129
+ // (web_search, file_search, code interpreter, image generation), MCP items,
130
+ // and item references. None has a chat-completions equivalent — validated
131
+ // loosely so a standard replay never 400s, then skipped at conversion like
132
+ // the kinds above.
133
+ const otherKnownItemSchema = z.object({
134
+ type: z.enum([
135
+ 'web_search_call', 'file_search_call', 'code_interpreter_call',
136
+ 'image_generation_call', 'mcp_call', 'mcp_list_tools',
137
+ 'mcp_approval_request', 'mcp_approval_response', 'item_reference',
138
+ ]),
139
+ id: z.string().optional(),
140
+ }).passthrough();
141
+ const inputItemSchema = z.union([
142
+ functionCallItemSchema,
143
+ functionCallOutputItemSchema,
144
+ computerCallItemSchema,
145
+ computerCallOutputItemSchema,
146
+ reasoningItemSchema,
147
+ localShellCallItemSchema,
148
+ additionalToolsItemSchema,
149
+ otherKnownItemSchema,
150
+ messageItemSchema,
151
+ ]);
152
+ // Accept ANY tool type, not just 'function'. Codex (Responses API) sends
153
+ // built-in tools like `web_search` / `local_shell` alongside function tools;
154
+ // a strict z.literal('function') rejected the whole request. We validate
155
+ // loosely here and drop non-function tools at conversion (toChatTools), since
156
+ // chat-completions providers only accept type:'function'.
157
+ const responsesToolSchema = z.object({
158
+ type: z.string(),
159
+ name: z.string().optional(),
160
+ description: z.string().nullable().optional(),
161
+ parameters: z.record(z.string(), z.unknown()).nullable().optional(),
162
+ strict: z.boolean().nullable().optional(),
163
+ }).passthrough();
164
+ const responsesRequestSchema = z.object({
165
+ model: z.string().optional(),
166
+ instructions: z.string().nullable().optional(),
167
+ input: z.union([z.string(), z.array(inputItemSchema)]),
168
+ stream: z.boolean().optional(),
169
+ temperature: z.number().min(0).max(2).nullable().optional(),
170
+ top_p: z.number().min(0).max(1).nullable().optional(),
171
+ max_output_tokens: z.number().int().positive().nullable().optional(),
172
+ tools: z.array(responsesToolSchema).optional(),
173
+ // The virtual Fusion model fans the translated conversation out to a
174
+ // diverse panel, then optionally synthesizes the survivors. Keep the
175
+ // Responses surface in parity with /v1/chat/completions.
176
+ fusion: fusionConfigSchema.optional(),
177
+ tool_choice: z.union([
178
+ z.enum(['none', 'auto', 'required']),
179
+ z.object({ type: z.literal('function'), name: z.string() }).passthrough(),
180
+ ]).optional(),
181
+ parallel_tool_calls: z.boolean().nullable().optional(),
182
+ // Extended sampling params, validated the same way as /chat/completions.
183
+ // Responses clients express structured output as `text.format` rather than
184
+ // `response_format` — mapped where completionOpts is built.
185
+ ...samplingParamSchemaFields,
186
+ text: z.object({
187
+ format: z.object({
188
+ type: z.enum(['text', 'json_object', 'json_schema']),
189
+ name: z.string().optional(),
190
+ strict: z.boolean().nullable().optional(),
191
+ schema: z.record(z.string(), z.unknown()).optional(),
192
+ }).passthrough().optional(),
193
+ }).passthrough().nullable().optional(),
194
+ }).passthrough();
195
+ // Responses content parts → plain text. input_text / output_text both carry
196
+ // `text`; other part types (images, etc.) are dropped (parity with the proxy).
197
+ function partsToString(content) {
198
+ if (typeof content === 'string')
199
+ return content;
200
+ return content
201
+ .map((p) => (typeof p.text === 'string' ? p.text : ''))
202
+ .join('');
203
+ }
204
+ // Responses content parts → internal chat content. Text parts map to text
205
+ // blocks, image parts (Responses `input_image`, chat-style `image_url`/`image`,
206
+ // and computer-use `computer_screenshot`) map to `image_url` blocks so vision
207
+ // routing and the provider adapters see them (parity with /chat/completions).
208
+ // `refusal` parts (assistant history replay) fold into text so the turn isn't
209
+ // silently emptied. All-text content collapses back to a plain string (the
210
+ // shape upstream chat providers and compression expect); an array comes back
211
+ // only once a message actually carries an image.
212
+ function partsToChatContent(content) {
213
+ if (typeof content === 'string')
214
+ return content;
215
+ const blocks = [];
216
+ for (const p of content) {
217
+ const type = p.type;
218
+ const text = p.text;
219
+ if (type === 'text' || type === 'input_text' || type === 'output_text' || type === 'summary_text' || (type === undefined && typeof text === 'string')) {
220
+ if (typeof text === 'string')
221
+ blocks.push({ type: 'text', text });
222
+ continue;
223
+ }
224
+ if (type === 'refusal') {
225
+ // The model's refusal text on a replayed assistant turn; chat providers
226
+ // have no refusal concept, so carry it as ordinary text.
227
+ const refusal = p.refusal;
228
+ if (typeof refusal === 'string')
229
+ blocks.push({ type: 'text', text: refusal });
230
+ continue;
231
+ }
232
+ if (type === 'input_image' || type === 'computer_screenshot' || type === 'image_url' || type === 'image') {
233
+ // The image lives under `image_url`, as a bare data URL string
234
+ // (Responses `input_image` / `computer_screenshot`) or as
235
+ // `{ url }` (chat-style `image_url`). The Responses `detail` hint
236
+ // (low/high/auto/original) rides along; adapters without an equivalent
237
+ // (Gemini inlineData) ignore it. Unresolvable shapes (missing/empty
238
+ // url, file_id-only) are rejected up front by the route's pre-check,
239
+ // so dropping here never answers blind.
240
+ const url = extractPartImageUrl(p);
241
+ if (url) {
242
+ const detail = p.detail;
243
+ blocks.push({ type: 'image_url', image_url: { url, ...(typeof detail === 'string' ? { detail } : {}) } });
244
+ }
245
+ continue;
246
+ }
247
+ }
248
+ if (blocks.every((b) => b.type === 'text')) {
249
+ return blocks.map((b) => b.text).join('');
250
+ }
251
+ return blocks;
252
+ }
253
+ // Shared url extraction so the translation and the pre-check below always
254
+ // agree on what counts as a resolvable image.
255
+ function extractPartImageUrl(p) {
256
+ const raw = p.image_url;
257
+ const url = typeof raw === 'string' ? raw : raw?.url;
258
+ return typeof url === 'string' && url.length > 0 ? url : undefined;
259
+ }
260
+ // Responses `input_image` references an image by `image_url` OR a Files API
261
+ // `file_id`. This proxy has no OpenAI Files backend, and the schema is
262
+ // deliberately lenient, so an image part with no resolvable url (file_id-only,
263
+ // or no url at all) can't survive translation — reject up front instead of
264
+ // silently dropping it and answering blind to an image the client believes
265
+ // was sent. (`computer_screenshot` shares these forms but is intentionally
266
+ // not checked: computer use is rejected wholesale up front, today and until
267
+ // it's a supported feature.)
268
+ export function responsesInputHasFileIdImage(req) {
269
+ if (typeof req.input === 'string')
270
+ return false;
271
+ return req.input.some((item) => {
272
+ const content = item.content;
273
+ if (!Array.isArray(content))
274
+ return false;
275
+ return content.some((p) => {
276
+ const part = p;
277
+ if (part.type !== 'input_image')
278
+ return false;
279
+ return extractPartImageUrl(part) === undefined;
280
+ });
281
+ });
282
+ }
283
+ // Computer use (the Responses `computer` / `computer_use_preview` tool + its
284
+ // computer_call/computer_call_output items) has no chat-completions equivalent:
285
+ // the harness loop needs the model's computer_actions and screenshot context,
286
+ // neither of which survives translation. Fail clearly (mirroring the image 422)
287
+ // rather than silently dropping the calls and breaking the tool loop.
288
+ export function responsesInputRequestsComputerUse(req) {
289
+ if ((req.tools ?? []).some((t) => t.type === 'computer' || t.type === 'computer_use_preview'))
290
+ return true;
291
+ if (typeof req.input === 'string' || req.input == null)
292
+ return false;
293
+ return req.input.some((item) => {
294
+ const type = item?.type;
295
+ return type === 'computer_call' || type === 'computer_call_output';
296
+ });
297
+ }
298
+ // ── Translate a Responses request → internal chat messages + options ──────
299
+ export function toChatMessages(req) {
300
+ const systemMessages = [];
301
+ if (req.instructions) {
302
+ systemMessages.push({ role: 'system', content: req.instructions });
303
+ }
304
+ if (typeof req.input === 'string') {
305
+ const messages = [{ role: 'user', content: req.input }];
306
+ return [...systemMessages, ...messages];
307
+ }
308
+ const messages = [];
309
+ const items = req.input;
310
+ for (let i = 0; i < items.length; i++) {
311
+ const item = items[i];
312
+ if ('type' in item && item.type === 'function_call') {
313
+ // Parallel tool calls replay as consecutive standalone function_call
314
+ // items (no preceding assistant message item). Fold the run into one
315
+ // assistant turn, for the same reason as the message+function_call
316
+ // merge below: consecutive assistant turns 400 on strict upstreams.
317
+ const toolCalls = [];
318
+ let j = i;
319
+ while (j < items.length && items[j].type === 'function_call') {
320
+ const fc = items[j];
321
+ toolCalls.push({
322
+ id: fc.call_id,
323
+ type: 'function',
324
+ function: { name: fc.name, arguments: fc.arguments },
325
+ });
326
+ j++;
327
+ }
328
+ messages.push({ role: 'assistant', content: null, tool_calls: toolCalls });
329
+ i = j - 1;
330
+ continue;
331
+ }
332
+ if ('type' in item && item.type === 'function_call_output') {
333
+ const output = typeof item.output === 'string'
334
+ ? item.output
335
+ : Array.isArray(item.output)
336
+ ? partsToString(item.output)
337
+ : JSON.stringify(item.output);
338
+ messages.push({ role: 'tool', tool_call_id: item.call_id, content: output });
339
+ continue;
340
+ }
341
+ if ('type' in item && item.type !== 'message') {
342
+ // computer_call / computer_call_output / reasoning / local_shell_call:
343
+ // no chat-message equivalent (the route 422s computer use up front).
344
+ // Skip rather than mis-parse as a message item.
345
+ continue;
346
+ }
347
+ // message item
348
+ const m = item;
349
+ // 'developer' is the Responses-era system role.
350
+ const role = m.role === 'developer' ? 'system' : m.role;
351
+ const content = partsToChatContent(m.content);
352
+ if (role === 'system') {
353
+ // Hoist system/developer messages to the start of the conversation:
354
+ // chat providers (Gemini, Claude, Mistral) reject a system message that
355
+ // appears after a user turn. Codex history replay often emits developer
356
+ // items mid-conversation.
357
+ systemMessages.push({ role: 'system', content });
358
+ continue;
359
+ }
360
+ if (role === 'assistant') {
361
+ // A Responses assistant turn is a message item followed by its
362
+ // function_call items. Merge them into a single chat assistant message
363
+ // (content + tool_calls); emitting consecutive assistant turns makes
364
+ // Gemini map them to consecutive model turns and strict upstreams
365
+ // (Mistral/Cohere) 400. Drop empty assistant items — an empty turn means
366
+ // nothing to a chat provider. (#96)
367
+ const toolCalls = [];
368
+ let j = i + 1;
369
+ while (j < items.length && items[j].type === 'function_call') {
370
+ const fc = items[j];
371
+ toolCalls.push({
372
+ id: fc.call_id,
373
+ type: 'function',
374
+ function: { name: fc.name, arguments: fc.arguments },
375
+ });
376
+ j++;
377
+ }
378
+ if (toolCalls.length > 0) {
379
+ messages.push({
380
+ role: 'assistant',
381
+ content: content.length > 0 ? content : null,
382
+ tool_calls: toolCalls,
383
+ });
384
+ i = j - 1;
385
+ continue;
386
+ }
387
+ if (content.length === 0)
388
+ continue;
389
+ messages.push({ role: 'assistant', content });
390
+ continue;
391
+ }
392
+ messages.push({ role, content });
393
+ }
394
+ return [...systemMessages, ...messages];
395
+ }
396
+ export function toChatTools(tools) {
397
+ if (!tools?.length)
398
+ return undefined;
399
+ // Forward only function tools — chat-completions upstreams reject other
400
+ // Responses-API tool types (web_search, local_shell, etc.). Codex sends those
401
+ // extras alongside its function tools (shell/exec, apply_patch); dropping them
402
+ // keeps the request valid without losing the tools that actually do the work.
403
+ const fns = tools.filter((t) => t.type === 'function' && typeof t.name === 'string');
404
+ if (!fns.length)
405
+ return undefined;
406
+ return fns.map((t) => ({
407
+ type: 'function',
408
+ function: {
409
+ name: t.name,
410
+ ...(t.description ? { description: t.description } : {}),
411
+ ...(t.parameters ? { parameters: t.parameters } : {}),
412
+ ...(t.strict != null ? { strict: t.strict } : {}),
413
+ },
414
+ }));
415
+ }
416
+ export function toChatToolChoice(tc) {
417
+ if (!tc)
418
+ return undefined;
419
+ if (typeof tc === 'string')
420
+ return tc;
421
+ return { type: 'function', function: { name: tc.name } };
422
+ }
423
+ function requestDeclaresToolUse(req) {
424
+ return (req.tools?.length ?? 0) > 0 && req.tool_choice !== 'none';
425
+ }
426
+ // ── Build the final (non-stream) Responses object ─────────────────────────
427
+ export function buildResponseObject(opts) {
428
+ const output = [];
429
+ if (opts.text.length > 0) {
430
+ output.push({
431
+ type: 'message',
432
+ id: newId('msg'),
433
+ status: 'completed',
434
+ role: 'assistant',
435
+ content: [{ type: 'output_text', text: opts.text, annotations: [] }],
436
+ });
437
+ }
438
+ for (const tc of opts.toolCalls) {
439
+ output.push({
440
+ type: 'function_call',
441
+ id: newId('fc'),
442
+ call_id: tc.id,
443
+ name: tc.function.name,
444
+ arguments: tc.function.arguments,
445
+ status: 'completed',
446
+ });
447
+ }
448
+ return {
449
+ id: opts.id,
450
+ object: 'response',
451
+ created_at: nowUnix(),
452
+ status: 'completed',
453
+ model: opts.model,
454
+ output,
455
+ output_text: opts.text,
456
+ usage: {
457
+ input_tokens: opts.promptTokens,
458
+ input_tokens_details: { cached_tokens: 0 },
459
+ output_tokens: opts.completionTokens,
460
+ output_tokens_details: { reasoning_tokens: opts.reasoningTokens ?? 0 },
461
+ total_tokens: opts.promptTokens + opts.completionTokens,
462
+ },
463
+ ...(opts.metadata ? { metadata: opts.metadata } : {}),
464
+ };
465
+ }
466
+ /**
467
+ * Translate the chat-shaped result returned by the Fusion service into the
468
+ * Responses object clients expect. Chat Fusion uses additive `_fusion` and
469
+ * `x_fusion` fields, but those are not part of the Responses schema and strict
470
+ * SDKs (including Codex) reject them while decoding `response.completed`.
471
+ * Preserve the portable routing summary in the standard string-valued
472
+ * `metadata` map instead; the chat surface keeps its richer fields unchanged.
473
+ */
474
+ function buildFusionResponseObject(id, result) {
475
+ const fusionResponse = result.response;
476
+ const message = fusionResponse.choices?.[0]?.message;
477
+ const usage = fusionResponse.usage;
478
+ const response = buildResponseObject({
479
+ id,
480
+ model: FUSION_MODEL_ID,
481
+ text: contentToString(message?.content ?? ''),
482
+ toolCalls: message?.tool_calls ?? [],
483
+ promptTokens: usage?.prompt_tokens ?? 0,
484
+ completionTokens: usage?.completion_tokens ?? 0,
485
+ reasoningTokens: usage?.completion_tokens_details?.reasoning_tokens,
486
+ metadata: fusionResponseMetadata(fusionResponse),
487
+ });
488
+ return response;
489
+ }
490
+ function fusionResponseMetadata(result) {
491
+ const metadata = { fusion: 'true' };
492
+ const summary = result._fusion && typeof result._fusion === 'object' && !Array.isArray(result._fusion)
493
+ ? result._fusion
494
+ : undefined;
495
+ const details = result.x_fusion && typeof result.x_fusion === 'object' && !Array.isArray(result.x_fusion)
496
+ ? result.x_fusion
497
+ : undefined;
498
+ if (Array.isArray(summary?.panel)) {
499
+ const panel = summary.panel
500
+ .map((entry) => {
501
+ if (!entry || typeof entry !== 'object')
502
+ return null;
503
+ const item = entry;
504
+ return typeof item.platform === 'string' && typeof item.model === 'string'
505
+ ? `${item.platform}/${item.model}`
506
+ : null;
507
+ })
508
+ .filter((entry) => entry !== null);
509
+ if (panel.length > 0)
510
+ metadata.fusion_panel = panel.join(',');
511
+ }
512
+ if (summary && 'judge' in summary) {
513
+ const judge = summary.judge;
514
+ if (judge && typeof judge === 'object') {
515
+ const item = judge;
516
+ if (typeof item.platform === 'string' && typeof item.model === 'string') {
517
+ metadata.fusion_judge = `${item.platform}/${item.model}`;
518
+ }
519
+ }
520
+ else {
521
+ metadata.fusion_judge = 'none';
522
+ }
523
+ }
524
+ if (typeof summary?.synthesized === 'boolean')
525
+ metadata.fusion_synthesized = String(summary.synthesized);
526
+ if (typeof details?.strategy === 'string')
527
+ metadata.fusion_strategy = details.strategy;
528
+ return metadata;
529
+ }
530
+ /** Apply the same structured-output contract to Fusion as the chat route. */
531
+ function enforceFusionResponseFormat(result, responseFormat) {
532
+ if (!responseFormat)
533
+ return;
534
+ const message = result.response.choices?.[0]?.message;
535
+ if (!message || message.tool_calls?.length)
536
+ return;
537
+ const text = contentToString(message.content ?? '');
538
+ if (!text)
539
+ return;
540
+ const enforced = enforceJsonContent(text);
541
+ if (!enforced.ok) {
542
+ throw new Error(`fusion produced non-JSON output despite response_format=${responseFormat.type} — retry, or pin a structured-output-capable model instead of "fusion"`);
543
+ }
544
+ if (enforced.healed)
545
+ message.content = enforced.content;
546
+ }
547
+ function quotaContextForRoute(route, endpoint) {
548
+ return {
549
+ platform: route.platform,
550
+ keyId: route.keyId,
551
+ modelId: route.modelId,
552
+ quotaPoolKey: inferQuotaPoolKey(route.platform, route.modelId),
553
+ endpoint,
554
+ origin: 'responses',
555
+ };
556
+ }
557
+ responsesRouter.post('/responses', async (req, res) => {
558
+ const start = Date.now();
559
+ const requestGroupId = getRequestGroupId(req);
560
+ res.setHeader('X-Request-ID', requestGroupId);
561
+ // Same auth as the proxy (accepts Bearer or x-api-key): unified key, or a
562
+ // client-profile key carrying a server-enforced system prompt (#411).
563
+ const auth = resolveAuth(extractApiToken(req));
564
+ if (!auth) {
565
+ res.status(401).json({ error: { message: 'Invalid API key', type: 'authentication_error' } });
566
+ return;
567
+ }
568
+ const parsed = responsesRequestSchema.safeParse(req.body);
569
+ if (!parsed.success) {
570
+ res.status(400).json({
571
+ error: {
572
+ message: `Invalid request: ${parsed.error.errors.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ')}`,
573
+ type: 'invalid_request_error',
574
+ },
575
+ });
576
+ return;
577
+ }
578
+ const reqData = parsed.data;
579
+ // Computer use can't survive the chat-completions translation either (no
580
+ // computer tool, no screenshot context). Fail clearly instead of silently
581
+ // dropping the calls and breaking Codex's computer-use tool loop.
582
+ if (responsesInputRequestsComputerUse(reqData)) {
583
+ res.status(422).json({
584
+ error: {
585
+ message: 'Computer use is not yet supported on /v1/responses (the computer / computer_use_preview tool and computer_call items have no chat-completions equivalent).',
586
+ type: 'invalid_request_error',
587
+ code: 'no_computer_use_model',
588
+ },
589
+ });
590
+ return;
591
+ }
592
+ // Unresolvable image parts (file_id-only, or no usable url) can't survive
593
+ // translation — fail clearly rather than dropping the image and answering
594
+ // blind.
595
+ if (responsesInputHasFileIdImage(reqData)) {
596
+ res.status(422).json({
597
+ error: {
598
+ message: "This request contains an image part that can't be resolved: input_image needs an image_url (an https URL or a base64 data URL); Files API file_id references aren't supported by this proxy.",
599
+ type: 'invalid_request_error',
600
+ code: 'unsupported_image_input',
601
+ },
602
+ });
603
+ return;
604
+ }
605
+ const stream = reqData.stream ?? false;
606
+ let messages = toChatMessages(reqData);
607
+ const tools = toChatTools(reqData.tools);
608
+ // name → parameter schema, for repairing double-encoded tool arguments on
609
+ // the way back out (see lib/tool-args.ts).
610
+ const toolSchemas = toolSchemaMap(tools);
611
+ const tool_choice = tools?.length ? toChatToolChoice(reqData.tool_choice) : undefined;
612
+ // Responses-API structured output arrives as `text.format`; translate it to
613
+ // the internal response_format shape (an explicit response_format on the
614
+ // body, unusual for this surface but valid, wins).
615
+ const samplingParams = pickSamplingParams(reqData);
616
+ const textFormat = reqData.text?.format;
617
+ if (!samplingParams.response_format && textFormat && textFormat.type !== 'text') {
618
+ samplingParams.response_format = textFormat.type === 'json_schema'
619
+ ? { type: 'json_schema', json_schema: { name: textFormat.name, strict: textFormat.strict, schema: textFormat.schema } }
620
+ : { type: 'json_object' };
621
+ }
622
+ const completionOpts = {
623
+ temperature: reqData.temperature ?? undefined,
624
+ max_tokens: reqData.max_output_tokens ?? undefined,
625
+ top_p: reqData.top_p ?? undefined,
626
+ tools,
627
+ tool_choice,
628
+ parallel_tool_calls: reqData.parallel_tool_calls ?? undefined,
629
+ ...samplingParams,
630
+ };
631
+ const hasCacheControl = typeof reqData.input !== 'string' && reqData.input.some(item => {
632
+ const content = item.content;
633
+ return Array.isArray(content)
634
+ && content.some(block => block && typeof block === 'object' && 'cache_control' in block);
635
+ });
636
+ const compressionResult = compressRequest(messages, {
637
+ header: req.headers['x-freellm-compress'],
638
+ tools,
639
+ cacheControlPrefixLength: hasCacheControl ? messages.length : 0,
640
+ });
641
+ messages = compressionResult.messages;
642
+ res.setHeader('X-FreeLLM-Compress', formatCompressionHeader(compressionResult));
643
+ // Server-enforced system prompt (#411): after compression so it is never
644
+ // compressed away, first in the list so the caller's own instructions
645
+ // (`instructions` / system input items) follow it and cannot override it.
646
+ messages = prependSystemPrompt(messages, auth.systemPrompt);
647
+ // Downscale over-threshold inline images before estimation/routing so the
648
+ // token budget, payload limits, and upstream transfer all see the shrunk
649
+ // bytes (see lib/image-normalize.ts). Mutates the image blocks in place.
650
+ await normalizeMessageImages(messages);
651
+ const estimatedInputTokens = estimateInputTokens(messages, tools);
652
+ // Image requests must route to a vision-capable model (mirrors
653
+ // /chat/completions, proxy.ts). Reject up front with a clear message when
654
+ // none is enabled; when vision models are available, requireVision routing
655
+ // skips text-only models — including a pinned/sticky one — and falls back to
656
+ // a vision-capable peer (#118, #125). A rough per-image token cost keeps
657
+ // budget routing from being skewed by content the text heuristic can't see.
658
+ const hasImage = messageHasImage(messages);
659
+ if (hasImage && !hasEnabledVisionModel()) {
660
+ res.status(422).json({
661
+ error: {
662
+ message: 'This request includes an image, but no vision-capable model is enabled. Enable a vision model (e.g. Gemini 2.5 Flash, Llama 4 Scout) in the Fallback Chain.',
663
+ type: 'invalid_request_error',
664
+ code: 'no_vision_model',
665
+ },
666
+ });
667
+ return;
668
+ }
669
+ const IMAGE_TOKEN_ESTIMATE = 1000;
670
+ const imageCount = messages.reduce((n, m) => n + (Array.isArray(m.content) ? m.content.filter(b => b?.type === 'image_url' || b?.type === 'image').length : 0), 0);
671
+ // Capped output reserve so a large max_output_tokens can't falsely exclude the
672
+ // model pool (#470); input counts in full. Threaded to the router separately:
673
+ // it is exact and must not be inflated by the context-window safety margin
674
+ // (#956 review).
675
+ const outputReserve = routingReserveTokens(reqData.max_output_tokens);
676
+ const estimatedTotal = estimatedInputTokens + imageCount * IMAGE_TOKEN_ESTIMATE + outputReserve;
677
+ // Guardrail: per-request token budget (request_max_tokens_budget, default
678
+ // off). A request with no max_output_tokens gets its output capped to the
679
+ // budget remainder instead of a rejection.
680
+ const budgetCheck = applyTokenBudget(estimatedInputTokens + imageCount * IMAGE_TOKEN_ESTIMATE, completionOpts.max_tokens);
681
+ if (budgetCheck.rejection) {
682
+ res.status(413).json({
683
+ error: { message: tokenBudgetMessage(budgetCheck.rejection), type: 'invalid_request_error', code: 'request_token_budget' },
684
+ });
685
+ return;
686
+ }
687
+ completionOpts.max_tokens = budgetCheck.maxTokens;
688
+ // Optional client-managed session affinity (mirrors /chat/completions).
689
+ const rawSessionId = req.headers['x-codex-session-id']
690
+ ?? req.headers['session-id']
691
+ ?? req.headers['x-session-id'];
692
+ const sessionIdHeader = Array.isArray(rawSessionId) ? rawSessionId[0] : rawSessionId;
693
+ const requestedModelLabel = reqData.model ?? 'auto';
694
+ // Explicit `model` field pins routing. If the catalog has no enabled row
695
+ // matching the requested id, return 400 — silently auto-routing to a
696
+ // different model would be surprising to Responses API clients.
697
+ // Priority: explicit model > sticky session > auto routing.
698
+ let preferredModel;
699
+ let groupChain;
700
+ let resolvedChain;
701
+ // Sticky scope for a named auto:<profile> (or auto:smart) chain. Plain `auto`
702
+ // stays on the unscoped key so existing session affinity is unchanged.
703
+ let stickyStrategyKey;
704
+ if (isAutoModel(requestedModelLabel)) {
705
+ try {
706
+ resolvedChain = resolveRoutingChain(requestedModelLabel);
707
+ }
708
+ catch (err) {
709
+ if (err?.status === 400) {
710
+ res.status(400).json({
711
+ error: { message: err.message, type: 'invalid_request_error' },
712
+ });
713
+ return;
714
+ }
715
+ throw err;
716
+ }
717
+ stickyStrategyKey = resolvedChain.strategyKey === 'auto' ? undefined : resolvedChain.strategyKey;
718
+ preferredModel = resolveStickyPreference(getStickyModel(messages, sessionIdHeader, stickyStrategyKey), resolvedChain.chain);
719
+ }
720
+ else if (isFusionModel(requestedModelLabel)) {
721
+ // Fusion is a virtual model, not a row in the catalog. Its panel and
722
+ // judge each resolve through the normal router inside runFusion below.
723
+ }
724
+ else {
725
+ const db = getDb();
726
+ const resolved = isUnifyEnabled() ? resolveRequestedIdForDispatch(requestedModelLabel, getModelGroups()) : null;
727
+ const members = resolved?.memberDbIds ?? null;
728
+ if (members && members.length > 0) {
729
+ groupChain = resolveModelGroupCandidates(members, resolved.demotedDbIds);
730
+ if (groupChain.length === 0) {
731
+ const placeholders = members.map(() => '?').join(',');
732
+ const anyEnabled = db.prepare(`SELECT 1 FROM models WHERE id IN (${placeholders}) AND enabled = 1 LIMIT 1`).get(...members);
733
+ const reason = anyEnabled ? 'has no providers with an enabled key' : 'is disabled';
734
+ res.status(400).json({
735
+ error: {
736
+ message: `Model '${requestedModelLabel}' ${reason}. Use 'auto' (or omit the 'model' field) to auto-route, or call /v1/models for the available list.`,
737
+ type: 'invalid_request_error',
738
+ code: 'model_not_found',
739
+ },
740
+ });
741
+ return;
742
+ }
743
+ const sticky = getStickyModel(messages, sessionIdHeader, requestedModelLabel);
744
+ preferredModel = (sticky != null && groupChain.some(r => r.model_db_id === sticky)) ? sticky : undefined;
745
+ }
746
+ else {
747
+ const enabled = db.prepare('SELECT id FROM models WHERE model_id = ? AND enabled = 1').get(requestedModelLabel);
748
+ if (enabled) {
749
+ preferredModel = enabled.id;
750
+ }
751
+ else {
752
+ const disabled = db.prepare('SELECT id FROM models WHERE model_id = ?').get(requestedModelLabel);
753
+ const reason = disabled ? 'is disabled' : 'is not in the catalog';
754
+ res.status(400).json({
755
+ error: {
756
+ message: `Model '${requestedModelLabel}' ${reason}. Use 'auto' (or omit the 'model' field) to auto-route, or call /v1/models for the available list.`,
757
+ type: 'invalid_request_error',
758
+ code: 'model_not_found',
759
+ },
760
+ });
761
+ return;
762
+ }
763
+ }
764
+ }
765
+ // Tool-bearing requests (the normal case for Codex/agent clients on this
766
+ // endpoint) must stay on models that emit structured tool_calls. Make the
767
+ // routing decision from the original Responses payload, not the subset of
768
+ // function tools we can forward to chat providers, because Codex may include
769
+ // built-in tool descriptors alongside or instead of function descriptors.
770
+ const wantsTools = requestDeclaresToolUse(reqData);
771
+ if (wantsTools && !hasEnabledToolsModel()) {
772
+ res.status(422).json({
773
+ error: {
774
+ message: 'This request includes tools, but no tool-capable model is enabled. Enable a tool-calling model (e.g. GPT-OSS 120B, Gemini 3.5 Flash, GLM-4.7) in the Fallback Chain.',
775
+ type: 'invalid_request_error',
776
+ code: 'no_tools_model',
777
+ },
778
+ });
779
+ return;
780
+ }
781
+ const responseId = newId('resp');
782
+ const state = newFallbackState();
783
+ // Lets the failover loop learn which models reject tool calls (#1230).
784
+ state.wantsTools = wantsTools;
785
+ const attemptLog = [];
786
+ // Client-disconnect fan-out: the flag stops the loop before the NEXT
787
+ // attempt; the AbortController (threaded to the provider as
788
+ // CompletionOptions.signal) additionally cancels the IN-FLIGHT upstream
789
+ // fetch and any body/stream read, so tokens stop burning and the in-flight
790
+ // lease frees immediately. 'close' also fires on normal completion —
791
+ // writableEnded distinguishes a real disconnect.
792
+ let clientGone = false;
793
+ const clientAbort = new AbortController();
794
+ // Fallback-v2 hedging: the loop aborts this controller (via abortInFlight)
795
+ // when the wall-clock retry budget expires mid-attempt, canceling the
796
+ // in-flight upstream instead of waiting for a stalled attempt to time out.
797
+ const hedgeAbort = new AbortController();
798
+ res.on('close', () => {
799
+ if (!res.writableEnded) {
800
+ clientGone = true;
801
+ clientAbort.abort(newClientAbortError());
802
+ }
803
+ });
804
+ const dispatchOpts = { ...completionOpts, signal: AbortSignal.any([clientAbort.signal, hedgeAbort.signal]) };
805
+ // ── Fusion: Responses-API virtual model ────────────────────────────────
806
+ // `fusion` is intentionally not a catalog row. The chat route handles this
807
+ // virtual id before normal model dispatch; do the same here after the common
808
+ // Responses translation/guardrails so Codex and other Responses clients can
809
+ // opt into the exact same panel + judge behavior.
810
+ if (isFusionModel(requestedModelLabel)) {
811
+ const fusionConfig = reqData.fusion ?? {};
812
+ const fusionOptions = { ...dispatchOpts };
813
+ if (!stream) {
814
+ try {
815
+ const result = await runFusion({
816
+ messages,
817
+ config: fusionConfig,
818
+ options: fusionOptions,
819
+ estimatedTokens: estimatedTotal,
820
+ vision: hasImage,
821
+ });
822
+ enforceFusionResponseFormat(result, samplingParams.response_format);
823
+ res.setHeader('X-Routed-Via', safeHeaderValue(result.routedVia));
824
+ res.json(buildFusionResponseObject(responseId, result));
825
+ }
826
+ catch (err) {
827
+ if (err instanceof FusionError) {
828
+ res.status(err.status).json({
829
+ error: {
830
+ message: err.message,
831
+ type: err.status === 429 ? 'rate_limit_error' : err.status >= 500 ? 'server_error' : 'invalid_request_error',
832
+ },
833
+ });
834
+ }
835
+ else {
836
+ res.status(502).json({
837
+ error: {
838
+ message: `fusion error: ${sanitizeProviderErrorMessage(err?.message)}`,
839
+ type: 'server_error',
840
+ },
841
+ });
842
+ }
843
+ }
844
+ return;
845
+ }
846
+ // A streamed Fusion judge can emit deltas through runFusion's hook. Panel
847
+ // and best-of/single-survivor paths remain valid Responses streams too:
848
+ // they simply emit the completed answer once the fan-out settles.
849
+ res.setHeader('Content-Type', 'text/event-stream');
850
+ res.setHeader('Cache-Control', 'no-cache');
851
+ res.setHeader('Connection', 'keep-alive');
852
+ // The detailed panel is only known after runFusion settles, but the
853
+ // standard header can still identify this stream as Fusion before the
854
+ // response is committed. The final metadata carries panel/judge details.
855
+ res.setHeader('X-Routed-Via', FUSION_MODEL_ID);
856
+ let fusionSequence = 0;
857
+ const fusionSse = (event, payload) => {
858
+ if (res.writableEnded)
859
+ return;
860
+ try {
861
+ res.write(`event: ${event}\n`);
862
+ res.write(`data: ${JSON.stringify({ type: event, sequence_number: fusionSequence++, ...payload })}\n\n`);
863
+ }
864
+ catch {
865
+ // The close listener aborts the provider signal; a late write can still
866
+ // race the socket teardown and is intentionally ignored.
867
+ }
868
+ };
869
+ const skeleton = {
870
+ id: responseId,
871
+ object: 'response',
872
+ created_at: nowUnix(),
873
+ status: 'in_progress',
874
+ model: FUSION_MODEL_ID,
875
+ output: [],
876
+ output_text: '',
877
+ metadata: { fusion: 'true' },
878
+ };
879
+ fusionSse('response.created', { response: skeleton });
880
+ fusionSse('response.in_progress', { response: skeleton });
881
+ // Fusion fans out to several upstream models before the judge can emit a
882
+ // token. That gap can exceed the ~15s idle timeout used by Codex's
883
+ // Responses client even though the HTTP connection is healthy. A standard
884
+ // in-progress event keeps the client-side event watchdog alive; comments
885
+ // are ignored by Codex's watchdog even though they refresh curl's stream.
886
+ const fusionHeartbeat = setInterval(() => {
887
+ if (res.writableEnded || res.destroyed)
888
+ return;
889
+ try {
890
+ fusionSse('response.in_progress', { response: skeleton });
891
+ }
892
+ catch {
893
+ // The close listener aborts the in-flight fan-out; a late heartbeat
894
+ // racing socket teardown is harmless.
895
+ }
896
+ }, 5000);
897
+ fusionHeartbeat.unref?.();
898
+ let textItemId = null;
899
+ const textOutputIndex = 0;
900
+ let streamedText = '';
901
+ const openTextItem = (id = newId('msg')) => {
902
+ if (textItemId !== null)
903
+ return;
904
+ textItemId = id;
905
+ fusionSse('response.output_item.added', {
906
+ output_index: textOutputIndex,
907
+ item: { id, type: 'message', status: 'in_progress', role: 'assistant', content: [] },
908
+ });
909
+ fusionSse('response.content_part.added', {
910
+ item_id: id,
911
+ output_index: textOutputIndex,
912
+ content_index: 0,
913
+ part: { type: 'output_text', text: '', annotations: [] },
914
+ });
915
+ };
916
+ try {
917
+ const result = await runFusion({
918
+ messages,
919
+ config: fusionConfig,
920
+ options: fusionOptions,
921
+ estimatedTokens: estimatedTotal,
922
+ vision: hasImage,
923
+ hooks: {
924
+ onJudgeDelta: (delta) => {
925
+ if (!delta)
926
+ return;
927
+ openTextItem();
928
+ fusionSse('response.output_text.delta', {
929
+ item_id: textItemId,
930
+ output_index: textOutputIndex,
931
+ content_index: 0,
932
+ delta,
933
+ });
934
+ streamedText += delta;
935
+ },
936
+ },
937
+ });
938
+ // Like the chat route's streamed Fusion branch, do not run a structured
939
+ // output verdict after judge deltas have already reached the client: a
940
+ // failed verdict could not retract the partial answer. Non-streaming
941
+ // Fusion is checked above before the response is committed.
942
+ const responseObject = buildFusionResponseObject(responseId, result);
943
+ const outputs = Array.isArray(responseObject.output) ? responseObject.output : [];
944
+ const messageOutput = outputs.find((item) => item?.type === 'message');
945
+ const finalText = typeof responseObject.output_text === 'string' ? responseObject.output_text : '';
946
+ if (messageOutput) {
947
+ // Keep the final Response object's message id aligned with any judge
948
+ // deltas already emitted on the wire.
949
+ if (textItemId === null)
950
+ openTextItem(messageOutput.id);
951
+ else
952
+ messageOutput.id = textItemId;
953
+ if (finalText && !streamedText) {
954
+ fusionSse('response.output_text.delta', {
955
+ item_id: textItemId,
956
+ output_index: textOutputIndex,
957
+ content_index: 0,
958
+ delta: finalText,
959
+ });
960
+ streamedText = finalText;
961
+ }
962
+ else if (finalText.length > streamedText.length && finalText.startsWith(streamedText)) {
963
+ fusionSse('response.output_text.delta', {
964
+ item_id: textItemId,
965
+ output_index: textOutputIndex,
966
+ content_index: 0,
967
+ delta: finalText.slice(streamedText.length),
968
+ });
969
+ streamedText = finalText;
970
+ }
971
+ fusionSse('response.output_text.done', {
972
+ item_id: textItemId,
973
+ output_index: textOutputIndex,
974
+ content_index: 0,
975
+ text: finalText,
976
+ });
977
+ fusionSse('response.content_part.done', {
978
+ item_id: textItemId,
979
+ output_index: textOutputIndex,
980
+ content_index: 0,
981
+ part: { type: 'output_text', text: finalText, annotations: [] },
982
+ });
983
+ fusionSse('response.output_item.done', {
984
+ output_index: textOutputIndex,
985
+ item: messageOutput,
986
+ });
987
+ }
988
+ let outputIndex = messageOutput ? 1 : 0;
989
+ for (const output of outputs) {
990
+ if (output?.type !== 'function_call')
991
+ continue;
992
+ const item = { ...output, status: 'in_progress', arguments: '' };
993
+ fusionSse('response.output_item.added', { output_index: outputIndex, item });
994
+ if (output.arguments) {
995
+ fusionSse('response.function_call_arguments.delta', {
996
+ item_id: output.id,
997
+ output_index: outputIndex,
998
+ delta: output.arguments,
999
+ });
1000
+ }
1001
+ fusionSse('response.function_call_arguments.done', {
1002
+ item_id: output.id,
1003
+ output_index: outputIndex,
1004
+ arguments: output.arguments ?? '',
1005
+ });
1006
+ fusionSse('response.output_item.done', { output_index: outputIndex, item: output });
1007
+ outputIndex++;
1008
+ }
1009
+ fusionSse('response.completed', { response: responseObject });
1010
+ res.end();
1011
+ }
1012
+ catch (err) {
1013
+ const message = err instanceof FusionError
1014
+ ? err.message
1015
+ : `fusion error: ${sanitizeProviderErrorMessage(err?.message)}`;
1016
+ const type = err instanceof FusionError
1017
+ ? (err.status === 429 ? 'rate_limit_error' : err.status >= 500 ? 'server_error' : 'invalid_request_error')
1018
+ : 'server_error';
1019
+ fusionSse('response.failed', {
1020
+ response: {
1021
+ ...skeleton,
1022
+ status: 'failed',
1023
+ error: { message, type },
1024
+ },
1025
+ });
1026
+ res.end();
1027
+ }
1028
+ finally {
1029
+ clearInterval(fusionHeartbeat);
1030
+ }
1031
+ return;
1032
+ }
1033
+ // Stream bookkeeping (used only when stream === true). `streamStarted` is the
1034
+ // commit flag: true once the response.created/in_progress skeleton has left,
1035
+ // after which failover is no longer possible. seq/streamStarted span attempts
1036
+ // so the SSE sequence numbers stay monotonic and a committed stream can't be
1037
+ // re-committed by a later attempt.
1038
+ let seq = 0;
1039
+ let streamStarted = false;
1040
+ const sse = (event, payload) => {
1041
+ res.write(`event: ${event}\n`);
1042
+ res.write(`data: ${JSON.stringify({ type: event, sequence_number: seq++, ...payload })}\n\n`);
1043
+ };
1044
+ await runFallbackLoop({
1045
+ maxRetries: MAX_RETRIES,
1046
+ state,
1047
+ attemptLog,
1048
+ logIdentity: { surface: 'responses', requestId: requestGroupId, requestedModel: requestedModelLabel },
1049
+ clientGone: () => clientGone,
1050
+ abortInFlight: () => hedgeAbort.abort(newHedgeAbortError()),
1051
+ route: () => {
1052
+ // Task-type routing (#1127): same header/derivation as /chat/completions.
1053
+ const taskType = resolveTaskType(req, tools, messages);
1054
+ // #507: inflate the routing estimate from any provider-reported REQUESTED
1055
+ // size latched onto state so the existing size gates in router.ts skip
1056
+ // low-TPM / small-context models on retry.
1057
+ const routingTotal = fallbackRoutingTokens(state, estimatedTotal, outputReserve);
1058
+ return routeRequest(routingTotal, state.skipKeys.size > 0 ? state.skipKeys : undefined, preferredModel, hasImage, wantsTools, state.skipModels.size > 0 ? state.skipModels : undefined, groupChain ?? resolvedChain?.chain, completionOpts.response_format !== undefined, state.skipPlatforms.size > 0 ? state.skipPlatforms : undefined, outputReserve, taskType);
1059
+ },
1060
+ dispatch: async (route, attempt, ctx) => {
1061
+ const contextBudget = routeOutputBudget(route, estimatedInputTokens);
1062
+ const routeOpts = contextBudget != null ? { ...dispatchOpts, contextBudget } : dispatchOpts;
1063
+ traceRouteEvent('Responses', {
1064
+ event: attempt === 0 ? 'start' : 'next',
1065
+ requestId: requestGroupId,
1066
+ attempt,
1067
+ platform: route.platform,
1068
+ model: route.modelId,
1069
+ requestedModel: attempt === 0 ? requestedModelLabel : undefined,
1070
+ });
1071
+ if (stream) {
1072
+ // Every output item (message text + each function_call) claims the next
1073
+ // free index. OpenAI's streaming SDK indexes snapshot.output by
1074
+ // output_index, so indices MUST be dense & unique — reusing 0 for the
1075
+ // text item after a tool-call item had already taken it makes the SDK
1076
+ // crash on `snapshot.output[output_index]` (#96, Codex computer-use).
1077
+ let outputIndex = 0;
1078
+ let msgItemId = null;
1079
+ let msgText = '';
1080
+ // output_index of the open text item (valid while msgItemId !== null).
1081
+ let textOutputIndex = 0;
1082
+ // tool-call accumulator keyed by the provider's tool_call index
1083
+ const toolAcc = new Map();
1084
+ let totalOutputTokens = 0;
1085
+ // #764: thinking tokens are tracked separately so the final Response
1086
+ // object can report `output_tokens_details.reasoning_tokens` truthfully
1087
+ // instead of a hardcoded 0.
1088
+ let totalReasoningTokens = 0;
1089
+ // #764: ttfb = first token of ANY kind (content or reasoning), recorded
1090
+ // in the pump loop; commit() only backfills streams that never produced
1091
+ // one. This path previously logged no ttfb at all, so Analytics showed
1092
+ // a null speed for every /v1/responses turn.
1093
+ let ttfbMs = null;
1094
+ // Inline-dialect hold window (#231): first text is held until it
1095
+ // either matches a tool-call dialect marker (held to the end and
1096
+ // rescued into function_call items) or provably cannot (flushed and
1097
+ // streamed normally). Mirrors the /chat/completions stream loop.
1098
+ let dialectMode = 'undecided';
1099
+ let heldText = '';
1100
+ let upstreamFinish = null;
1101
+ // Commit point: headers + the response.created/in_progress skeleton go
1102
+ // out only when the first MEANINGFUL output item is about to be emitted
1103
+ // (converged with /chat/completions — responses previously committed on
1104
+ // the first raw chunk, even a role-only one). Until then a connect-time
1105
+ // error, an empty completion, or an unparseable dialect turn fails over
1106
+ // on the same connection with no bytes on the wire. Idempotent.
1107
+ const commit = () => {
1108
+ if (streamStarted)
1109
+ return;
1110
+ if (ttfbMs === null)
1111
+ ttfbMs = Date.now() - start;
1112
+ res.setHeader('Content-Type', 'text/event-stream');
1113
+ res.setHeader('Cache-Control', 'no-cache');
1114
+ res.setHeader('Connection', 'keep-alive');
1115
+ res.setHeader('X-Routed-Via', routedViaValue(route.platform, route.modelId));
1116
+ setFallbackHeaders(res, attempt, attemptLog);
1117
+ const skeleton = {
1118
+ id: responseId, object: 'response', created_at: nowUnix(),
1119
+ status: 'in_progress', model: route.modelId, output: [], output_text: '',
1120
+ };
1121
+ sse('response.created', { response: skeleton });
1122
+ sse('response.in_progress', { response: skeleton });
1123
+ streamStarted = true;
1124
+ // Committed: the answer is on its way, so the retry budget must no
1125
+ // longer cancel this attempt (it could not fail over now anyway).
1126
+ ctx.disarmHedge();
1127
+ };
1128
+ // Open the text output item and stream `text` as its first delta.
1129
+ // The text item takes the next free output index (it is NOT always 0 —
1130
+ // when the model emits tool_calls first, the function_call items own
1131
+ // the low indices). Every later text delta/done must reference this
1132
+ // same index or the SDK snapshot lookup misroutes the deltas.
1133
+ const openTextItem = (text) => {
1134
+ commit();
1135
+ msgItemId = newId('msg');
1136
+ textOutputIndex = outputIndex++;
1137
+ sse('response.output_item.added', {
1138
+ output_index: textOutputIndex,
1139
+ item: { id: msgItemId, type: 'message', status: 'in_progress', role: 'assistant', content: [] },
1140
+ });
1141
+ sse('response.content_part.added', {
1142
+ item_id: msgItemId, output_index: textOutputIndex, content_index: 0,
1143
+ part: { type: 'output_text', text: '', annotations: [] },
1144
+ });
1145
+ if (text) {
1146
+ sse('response.output_text.delta', { item_id: msgItemId, output_index: textOutputIndex, content_index: 0, delta: text });
1147
+ msgText += text;
1148
+ }
1149
+ };
1150
+ try {
1151
+ const gen = route.provider.streamChatCompletion(route.apiKey, messages, route.modelId, routeOpts, quotaContextForRoute(route, 'responses'));
1152
+ for await (const chunk of gen) {
1153
+ if (clientGone)
1154
+ break; // client hung up: stop pulling; reader.cancel() aborts upstream
1155
+ // In-band upstream error frame ({"error":...} inside a 200 SSE
1156
+ // stream — observed live from Groq). Throwing hands it to the catch
1157
+ // below: pre-commit it fails over, post-commit it surfaces
1158
+ // response.failed.
1159
+ const anyChunk = chunk;
1160
+ if (anyChunk.error && !anyChunk.choices) {
1161
+ throw new Error(`in-band provider error from ${route.displayName}: ${anyChunk.error.message ?? 'provider error'}`);
1162
+ }
1163
+ const choice0 = chunk.choices?.[0];
1164
+ if (choice0?.finish_reason)
1165
+ upstreamFinish = choice0.finish_reason;
1166
+ const delta = choice0?.delta;
1167
+ if (!delta)
1168
+ continue;
1169
+ // Text deltas → output_text events on a single message item, after
1170
+ // the dialect hold window has decided the text is real prose.
1171
+ const text = delta.content ?? '';
1172
+ // #764: reasoning models stream thinking before the first answer
1173
+ // token — count that first token as ttfb so Analytics speed
1174
+ // reflects the real head-of-stream, and count thinking tokens as
1175
+ // real output consumption.
1176
+ const reasoning = streamReasoningText(chunk);
1177
+ if (ttfbMs === null && (text.length > 0 || reasoning.length > 0)) {
1178
+ ttfbMs = Date.now() - start;
1179
+ }
1180
+ if (text) {
1181
+ totalOutputTokens += Math.ceil((text.length + reasoning.length) / 4);
1182
+ if (reasoning.length > 0)
1183
+ totalReasoningTokens += Math.ceil(reasoning.length / 4);
1184
+ if (dialectMode === 'passthrough') {
1185
+ if (msgItemId === null)
1186
+ openTextItem('');
1187
+ sse('response.output_text.delta', {
1188
+ item_id: msgItemId, output_index: textOutputIndex, content_index: 0, delta: text,
1189
+ });
1190
+ msgText += text;
1191
+ }
1192
+ else {
1193
+ heldText += text;
1194
+ if (dialectMode === 'undecided') {
1195
+ const probe = heldText.trimStart();
1196
+ if (startsWithDialectMarker(probe)) {
1197
+ dialectMode = 'dialect';
1198
+ }
1199
+ else if (!couldBecomeDialectMarker(probe) || heldText.length > 256) {
1200
+ dialectMode = 'passthrough';
1201
+ openTextItem(heldText);
1202
+ heldText = '';
1203
+ }
1204
+ }
1205
+ }
1206
+ }
1207
+ else if (reasoning.length > 0) {
1208
+ // #764: thinking-only chunk (no visible text yet) — count tokens.
1209
+ totalOutputTokens += Math.ceil(reasoning.length / 4);
1210
+ totalReasoningTokens += Math.ceil(reasoning.length / 4);
1211
+ }
1212
+ // Tool-call deltas → function_call item + argument deltas.
1213
+ for (const tc of delta.tool_calls ?? []) {
1214
+ const idx = tc.index ?? 0;
1215
+ let acc = toolAcc.get(idx);
1216
+ if (!acc) {
1217
+ // First time we see this tool call: open a new output item.
1218
+ commit();
1219
+ if (msgItemId !== null && msgText.length > 0) {
1220
+ // close the text item (at its own output index) before starting a function_call item
1221
+ sse('response.output_text.done', { item_id: msgItemId, output_index: textOutputIndex, content_index: 0, text: msgText });
1222
+ sse('response.content_part.done', { item_id: msgItemId, output_index: textOutputIndex, content_index: 0, part: { type: 'output_text', text: msgText, annotations: [] } });
1223
+ sse('response.output_item.done', { output_index: textOutputIndex, item: { id: msgItemId, type: 'message', status: 'completed', role: 'assistant', content: [{ type: 'output_text', text: msgText, annotations: [] }] } });
1224
+ msgItemId = null;
1225
+ }
1226
+ acc = { outputIndex: outputIndex++, itemId: newId('fc'), callId: tc.id || newId('call'), name: tc.function?.name ?? '', args: '' };
1227
+ toolAcc.set(idx, acc);
1228
+ sse('response.output_item.added', {
1229
+ output_index: acc.outputIndex,
1230
+ item: { id: acc.itemId, type: 'function_call', status: 'in_progress', call_id: acc.callId, name: acc.name, arguments: '' },
1231
+ });
1232
+ }
1233
+ const argFrag = tc.function?.arguments ?? '';
1234
+ if (tc.function?.name && !acc.name)
1235
+ acc.name = tc.function.name;
1236
+ if (argFrag) {
1237
+ acc.args += argFrag;
1238
+ sse('response.function_call_arguments.delta', { item_id: acc.itemId, output_index: acc.outputIndex, delta: argFrag });
1239
+ }
1240
+ }
1241
+ }
1242
+ // Resolve the dialect hold window now that the full text is known.
1243
+ // Held text was never emitted, so a dead dialect turn can still fail
1244
+ // over on the same SSE stream (nothing has been committed yet).
1245
+ if (heldText.length > 0) {
1246
+ const rescue = (wantsTools && (dialectMode === 'dialect' || containsDialectMarker(heldText)))
1247
+ ? rescueInlineToolCalls(heldText, new Set((tools ?? []).map(t => t.function.name)))
1248
+ : { detected: false, calls: null, cleanText: heldText };
1249
+ if (rescue.detected && !rescue.calls) {
1250
+ // Unparseable dialect turn: throw so the shared loop cooldowns this
1251
+ // model+key and fails over (streamStarted is still false).
1252
+ throw new Error(`unparseable inline tool-call dialect from ${route.displayName}: ${heldText.slice(0, 120)}`);
1253
+ }
1254
+ if (rescue.detected && rescue.calls) {
1255
+ // Rescued calls become function_call items, exactly as if the
1256
+ // provider had streamed them structurally.
1257
+ console.log(`[Responses] Rescued ${rescue.calls.length} inline tool call(s) from ${route.displayName}`);
1258
+ if (rescue.cleanText.length > 0 && msgItemId === null)
1259
+ openTextItem(rescue.cleanText);
1260
+ let rescuedIdx = 0;
1261
+ for (const c of rescue.calls) {
1262
+ const idx = 1000 + rescuedIdx++; // synthetic accumulator keys, past any provider index
1263
+ commit();
1264
+ const acc = {
1265
+ outputIndex: outputIndex++,
1266
+ itemId: newId('fc'), callId: newId('call'), name: c.name, args: c.arguments,
1267
+ };
1268
+ toolAcc.set(idx, acc);
1269
+ sse('response.output_item.added', {
1270
+ output_index: acc.outputIndex,
1271
+ item: { id: acc.itemId, type: 'function_call', status: 'in_progress', call_id: acc.callId, name: acc.name, arguments: '' },
1272
+ });
1273
+ }
1274
+ }
1275
+ else if (msgItemId === null) {
1276
+ // Plain short answer that never left the hold window (e.g. "Hi").
1277
+ openTextItem(heldText);
1278
+ }
1279
+ heldText = '';
1280
+ }
1281
+ // Empty completion — the provider returned 200 with no text AND no
1282
+ // tool calls. Seen in production from nemotron-3-super on ~65k-token
1283
+ // contexts: transport-level "success", zero usable output. Nothing has
1284
+ // been committed yet (the skeleton is lazy), so throwing lets the
1285
+ // shared loop fail over to the next model on the same SSE connection.
1286
+ if (msgText.length === 0 && toolAcc.size === 0) {
1287
+ // Disconnect before the commit point: the break above fired with
1288
+ // nothing accumulated — CLIENT behavior, not a provider failure.
1289
+ // Falling through to the empty-completion throw benched a healthy
1290
+ // model+key for 90s on every Ctrl-C during a reasoning model's
1291
+ // TTFB window.
1292
+ if (clientGone && !streamStarted) {
1293
+ console.log(`[Responses] client disconnected before first token from ${route.displayName} — dropping attempt without benching`);
1294
+ traceRouteEvent('Responses', {
1295
+ event: 'canceled',
1296
+ requestId: requestGroupId,
1297
+ attempt,
1298
+ platform: route.platform,
1299
+ model: route.modelId,
1300
+ });
1301
+ return 'committed';
1302
+ }
1303
+ // finish_reason 'length' = the model spent the whole output budget
1304
+ // on hidden reasoning before any visible text: fail over, but skip
1305
+ // the cooldown/penalty (not a provider-health signal).
1306
+ throw Object.assign(new Error(`empty completion from ${route.displayName}`), upstreamFinish === 'length' ? { skipBench: true } : {});
1307
+ }
1308
+ // Finalize any open text item.
1309
+ if (msgItemId !== null) {
1310
+ sse('response.output_text.done', { item_id: msgItemId, output_index: textOutputIndex, content_index: 0, text: msgText });
1311
+ sse('response.content_part.done', { item_id: msgItemId, output_index: textOutputIndex, content_index: 0, part: { type: 'output_text', text: msgText, annotations: [] } });
1312
+ sse('response.output_item.done', { output_index: textOutputIndex, item: { id: msgItemId, type: 'message', status: 'completed', role: 'assistant', content: [{ type: 'output_text', text: msgText, annotations: [] }] } });
1313
+ }
1314
+ // Finalize tool-call items. Arguments are repaired against the tool's
1315
+ // parameter schema at this point (after the full string accumulated):
1316
+ // models like GLM double-encode nested arrays/objects as strings, and
1317
+ // Codex hard-rejects the call ("invalid type: string, expected a
1318
+ // sequence"). Clients consume the *.done events / final response for
1319
+ // arguments, so repairing here covers the streamed path too.
1320
+ //
1321
+ // No schema verdict here, deliberately. This surface commits on the
1322
+ // FIRST tool-call delta (`commit()` above, where the function_call
1323
+ // item is opened) — the arguments are not complete until this point,
1324
+ // which is long after the skeleton and the item.added events have
1325
+ // left. A verdict here could only turn a delivered-but-invalid call
1326
+ // into a `response.failed` on a stream the client is already reading,
1327
+ // which is strictly worse. Wiring it would mean holding the
1328
+ // function_call item until its arguments finish, the way
1329
+ // /chat/completions buffers — a change to this route's commit point,
1330
+ // not to validation.
1331
+ const finalToolCalls = [];
1332
+ for (const acc of toolAcc.values()) {
1333
+ const repairedArgs = repairToolArguments(acc.args, toolSchemas.get(acc.name));
1334
+ sse('response.function_call_arguments.done', { item_id: acc.itemId, output_index: acc.outputIndex, arguments: repairedArgs });
1335
+ sse('response.output_item.done', { output_index: acc.outputIndex, item: { id: acc.itemId, type: 'function_call', status: 'completed', call_id: acc.callId, name: acc.name, arguments: repairedArgs } });
1336
+ finalToolCalls.push({ id: acc.callId, type: 'function', function: { name: acc.name, arguments: repairedArgs } });
1337
+ }
1338
+ const finalResponse = buildResponseObject({
1339
+ id: responseId, model: route.modelId, text: msgText,
1340
+ toolCalls: finalToolCalls, promptTokens: estimatedInputTokens, completionTokens: totalOutputTokens,
1341
+ reasoningTokens: totalReasoningTokens,
1342
+ });
1343
+ sse('response.completed', { response: finalResponse });
1344
+ res.end();
1345
+ recordUpstreamSuccess(route, estimatedInputTokens + totalOutputTokens, state);
1346
+ setStickyModel(messages, route.modelDbId, sessionIdHeader, stickyStrategyKey);
1347
+ traceRouteEvent('Responses', {
1348
+ event: 'ok',
1349
+ requestId: requestGroupId,
1350
+ attempt,
1351
+ platform: route.platform,
1352
+ model: route.modelId,
1353
+ latencyMs: Date.now() - start,
1354
+ inputTokens: estimatedInputTokens,
1355
+ outputTokens: totalOutputTokens,
1356
+ });
1357
+ logRequest(route.platform, route.modelId, route.keyId, 'success', estimatedInputTokens, totalOutputTokens, Date.now() - start, null, ttfbMs, null, null, 'http');
1358
+ return 'done';
1359
+ }
1360
+ catch (streamErr) {
1361
+ // Client abort mid-stream: the pump's own `if (clientGone) break`
1362
+ // can lose the race against the fetch-signal rejection, so the
1363
+ // abort may surface here instead. Rethrow — the shared loop's
1364
+ // client-abort branch stops the ladder without benching or an
1365
+ // error log row (the socket is gone; nothing to render).
1366
+ if (isClientAbortError(streamErr))
1367
+ throw streamErr;
1368
+ // A committed stream can't fail over (bytes already sent) — surface a
1369
+ // response.failed event honestly and stop. A pre-commit failure throws
1370
+ // through to the shared loop for cooldown + failover.
1371
+ if (streamStarted) {
1372
+ const safe = sanitizeProviderErrorMessage(streamErr.message);
1373
+ traceRouteEvent('Responses', {
1374
+ event: 'fail',
1375
+ requestId: requestGroupId,
1376
+ attempt,
1377
+ platform: route.platform,
1378
+ model: route.modelId,
1379
+ latencyMs: Date.now() - start,
1380
+ error: safe,
1381
+ });
1382
+ logRequest(route.platform, route.modelId, route.keyId, 'error', estimatedInputTokens, 0, Date.now() - start, safe, ttfbMs, null, null, 'http');
1383
+ sse('response.failed', { response: { id: responseId, object: 'response', status: 'failed', error: { message: `Provider error (${route.displayName}): stream interrupted`, type: 'stream_error' } } });
1384
+ res.end();
1385
+ return 'committed';
1386
+ }
1387
+ throw streamErr;
1388
+ }
1389
+ }
1390
+ const result = await route.provider.chatCompletion(route.apiKey, messages, route.modelId, routeOpts, quotaContextForRoute(route, 'responses'));
1391
+ const msg = result.choices[0]?.message;
1392
+ let text = contentToString(msg?.content ?? '');
1393
+ let toolCalls = (msg?.tool_calls ?? []).map((tc) => ({
1394
+ ...tc,
1395
+ function: { ...tc.function, arguments: repairToolArguments(tc.function.arguments, toolSchemas.get(tc.function.name)) },
1396
+ }));
1397
+ // Inline tool-call dialect rescue (#231) — see /chat/completions.
1398
+ if (wantsTools && toolCalls.length === 0 && text) {
1399
+ const rescue = rescueInlineToolCalls(text, new Set((tools ?? []).map(t => t.function.name)));
1400
+ if (rescue.detected) {
1401
+ if (!rescue.calls) {
1402
+ throw new Error(`unparseable inline tool-call dialect from ${route.displayName}: ${text.slice(0, 120)}`);
1403
+ }
1404
+ console.log(`[Responses] Rescued ${rescue.calls.length} inline tool call(s) from ${route.displayName}`);
1405
+ toolCalls = rescue.calls.map((c, i) => ({
1406
+ id: `call_rescued_${i + 1}`,
1407
+ type: 'function',
1408
+ function: { name: c.name, arguments: repairToolArguments(c.arguments, toolSchemas.get(c.name)) },
1409
+ }));
1410
+ text = rescue.cleanText;
1411
+ }
1412
+ }
1413
+ // Opt-in schema verdict on what the repair could not fix. Thrown before
1414
+ // anything is written, so the failover hop is invisible to the client.
1415
+ if (isToolArgumentValidationEnabled() && toolCalls.length > 0) {
1416
+ const invalid = invalidToolCallReasons(toolCalls, toolSchemas);
1417
+ if (invalid.length > 0)
1418
+ throw invalidToolArgumentsError(route.displayName, invalid);
1419
+ }
1420
+ const promptTokens = result.usage?.prompt_tokens ?? estimatedInputTokens;
1421
+ // #764: include reasoning tokens (message.reasoning_content / reasoning)
1422
+ // in the chars/4 estimate so thinking models aren't undercounted when the
1423
+ // provider omits `usage`.
1424
+ const completionTokens = result.usage?.completion_tokens
1425
+ ?? Math.ceil((text.length + completionReasoningText(result).length) / 4);
1426
+ // #764: report reasoning_tokens truthfully — the provider's own count
1427
+ // when advertised, else the same chars/4 estimate of the thinking text.
1428
+ const reasoningTokens = result.usage?.completion_tokens_details?.reasoning_tokens
1429
+ ?? Math.ceil(completionReasoningText(result).length / 4);
1430
+ // Empty completion → fail over via the shared loop (see the streaming
1431
+ // path); finish_reason 'length' skips the cooldown/penalty.
1432
+ if (!text && toolCalls.length === 0) {
1433
+ throw Object.assign(new Error(`empty completion from ${route.displayName}`), result.choices[0]?.finish_reason === 'length' ? { skipBench: true } : {});
1434
+ }
1435
+ // Structured-output enforcement — see /chat/completions. Heal fenced or
1436
+ // prose-wrapped JSON in place, fail over (skipBench + skipModelForRequest
1437
+ // — a sibling key would misbehave identically) when the model ignored the
1438
+ // requested format; finish_reason 'length' gets an honest "truncated"
1439
+ // class instead, since the JSON was cut off by max_tokens, not ignored.
1440
+ if (completionOpts.response_format && text && toolCalls.length === 0) {
1441
+ const enforced = enforceJsonContent(text);
1442
+ if (!enforced.ok) {
1443
+ const truncated = result.choices[0]?.finish_reason === 'length';
1444
+ throw Object.assign(new Error(truncated
1445
+ ? `truncated JSON from ${route.displayName} (finish_reason=length — raise max_tokens for this ${completionOpts.response_format.type} request)`
1446
+ : `${route.displayName} ignored response_format (returned non-JSON despite ${completionOpts.response_format.type})`), { skipBench: true, skipModelForRequest: true });
1447
+ }
1448
+ if (enforced.healed)
1449
+ text = enforced.content;
1450
+ }
1451
+ // Usage fallback: a missing provider `usage` block used to record 0
1452
+ // tokens against the rate-limit ledger; promptTokens/completionTokens
1453
+ // above already carry the chars/4 estimate.
1454
+ recordUpstreamSuccess(route, result.usage?.total_tokens ?? (promptTokens + completionTokens), state);
1455
+ setStickyModel(messages, route.modelDbId, sessionIdHeader, stickyStrategyKey);
1456
+ res.setHeader('X-Routed-Via', routedViaValue(route.platform, route.modelId));
1457
+ setFallbackHeaders(res, attempt, attemptLog);
1458
+ res.json(buildResponseObject({
1459
+ id: responseId, model: route.modelId, text, toolCalls,
1460
+ promptTokens, completionTokens, reasoningTokens,
1461
+ }));
1462
+ traceRouteEvent('Responses', {
1463
+ event: 'ok',
1464
+ requestId: requestGroupId,
1465
+ attempt,
1466
+ platform: route.platform,
1467
+ model: route.modelId,
1468
+ latencyMs: Date.now() - start,
1469
+ inputTokens: promptTokens,
1470
+ outputTokens: completionTokens,
1471
+ });
1472
+ logRequest(route.platform, route.modelId, route.keyId, 'success', promptTokens, completionTokens, Date.now() - start, null, null, null, null, 'http');
1473
+ return 'done';
1474
+ },
1475
+ logFailure: (route, err, attempt) => {
1476
+ const latency = Date.now() - start;
1477
+ const safeError = sanitizeProviderErrorMessage(err.message);
1478
+ traceRouteEvent('Responses', {
1479
+ event: 'fail',
1480
+ requestId: requestGroupId,
1481
+ attempt,
1482
+ platform: route.platform,
1483
+ model: route.modelId,
1484
+ latencyMs: latency,
1485
+ error: safeError,
1486
+ });
1487
+ logRequest(route.platform, route.modelId, route.keyId, 'error', estimatedInputTokens, 0, latency, safeError, null, null, null, 'http');
1488
+ },
1489
+ onFatal: (route, err, attempt) => {
1490
+ setFallbackHeaders(res, attempt, attemptLog);
1491
+ res.status(502).json({ error: { message: `Provider error (${route.displayName}): ${sanitizeProviderErrorMessage(err.message)}`, type: 'provider_error' } });
1492
+ },
1493
+ onRoutingExhausted: (lastError, routeErr, exhaustion, info) => {
1494
+ if (streamStarted) {
1495
+ // Headers are already on the wire — the honest status/Retry-After can
1496
+ // only travel in the failed-event payload.
1497
+ sse('response.failed', { response: { id: responseId, object: 'response', status: 'failed', error: exhaustionErrorPayload(exhaustion) } });
1498
+ res.end();
1499
+ }
1500
+ else {
1501
+ setFallbackHeaders(res, info.attempts.length, info.attempts);
1502
+ setExhaustionHeaders(res, exhaustion);
1503
+ res.status(exhaustion.status).json({ error: exhaustionErrorPayload(exhaustion) });
1504
+ }
1505
+ },
1506
+ onExhausted: (exhaustion, info) => {
1507
+ // The streaming skeleton may already be on the wire — close the SSE stream
1508
+ // with a failed event instead of writing JSON onto a committed response.
1509
+ if (streamStarted) {
1510
+ sse('response.failed', { response: { id: responseId, object: 'response', status: 'failed', error: exhaustionErrorPayload(exhaustion) } });
1511
+ res.end();
1512
+ }
1513
+ else {
1514
+ setFallbackHeaders(res, info.attempts.length, info.attempts);
1515
+ setExhaustionHeaders(res, exhaustion);
1516
+ res.status(exhaustion.status).json({ error: exhaustionErrorPayload(exhaustion) });
1517
+ }
1518
+ },
1519
+ });
1520
+ });
1521
+ //# sourceMappingURL=responses.js.map