cartethyia 2.7.4 → 2.7.6

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 (595) hide show
  1. package/.env.example +5 -0
  2. package/README.md +18 -6
  3. package/dist/dashboard/assets/BannedPage-B1N603cM.js +1 -0
  4. package/dist/dashboard/assets/BannedPage-B1N603cM.js.gz +0 -0
  5. package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js +3 -0
  6. package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js.gz +0 -0
  7. package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js +1 -0
  8. package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js.gz +0 -0
  9. package/dist/dashboard/assets/CombosPage-D762MIkp.js +1 -0
  10. package/dist/dashboard/assets/CombosPage-D762MIkp.js.gz +0 -0
  11. package/dist/dashboard/assets/ConsoleLogPage-KRYjHZsX.js +1 -0
  12. package/dist/dashboard/assets/ConsoleLogPage-KRYjHZsX.js.gz +0 -0
  13. package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js +1 -0
  14. package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js.gz +0 -0
  15. package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js +1 -0
  16. package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js.gz +0 -0
  17. package/dist/dashboard/assets/InjectSystemPromptPanel-d5v3K_Yc.js +899 -0
  18. package/dist/dashboard/assets/InjectSystemPromptPanel-d5v3K_Yc.js.gz +0 -0
  19. package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js +1 -0
  20. package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js.gz +0 -0
  21. package/dist/dashboard/assets/{ModelPicker-BMS2t8BA.js → ModelPicker-C6WlpU5E.js} +1 -1
  22. package/dist/dashboard/assets/ModelPicker-C6WlpU5E.js.gz +0 -0
  23. package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js +1 -0
  24. package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js.gz +0 -0
  25. package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js +5 -0
  26. package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js.gz +0 -0
  27. package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js +1 -0
  28. package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js.gz +0 -0
  29. package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js +1 -0
  30. package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js.gz +0 -0
  31. package/dist/dashboard/assets/{ProxyPage-lsrx7NYg.js → ProxyPage-DxjvUzSD.js} +1 -1
  32. package/dist/dashboard/assets/ProxyPage-DxjvUzSD.js.gz +0 -0
  33. package/dist/dashboard/assets/QuotaPage-OaHScjpX.js +1 -0
  34. package/dist/dashboard/assets/QuotaPage-OaHScjpX.js.gz +0 -0
  35. package/dist/dashboard/assets/SettingsPage-BW4AKj2H.js +1 -0
  36. package/dist/dashboard/assets/SettingsPage-BW4AKj2H.js.gz +0 -0
  37. package/dist/dashboard/assets/SetupPage-44BZrxFC.js +1 -0
  38. package/dist/dashboard/assets/SetupPage-44BZrxFC.js.gz +0 -0
  39. package/dist/dashboard/assets/{StudioPage-Cq5tW7hW.js → StudioPage-DRPavCdU.js} +4 -4
  40. package/dist/dashboard/assets/StudioPage-DRPavCdU.js.gz +0 -0
  41. package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js +1 -0
  42. package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js.gz +0 -0
  43. package/dist/dashboard/assets/UsagePage-B7M7LvMM.js +56 -0
  44. package/dist/dashboard/assets/UsagePage-B7M7LvMM.js.gz +0 -0
  45. package/dist/dashboard/assets/api-keys-KvX83mRe.js +1 -0
  46. package/dist/dashboard/assets/api-keys-KvX83mRe.js.gz +0 -0
  47. package/dist/dashboard/assets/app-DRnQLhsc.js +9 -0
  48. package/dist/dashboard/assets/app-DRnQLhsc.js.gz +0 -0
  49. package/dist/dashboard/assets/app-mweRcsKp.css +2 -0
  50. package/dist/dashboard/assets/app-mweRcsKp.css.gz +0 -0
  51. package/dist/dashboard/assets/assert-CidQVoNo.js +1 -0
  52. package/dist/dashboard/assets/assert-CidQVoNo.js.gz +0 -0
  53. package/dist/dashboard/assets/{badge-D9CS6dZk.js → badge-BaX2GTrn.js} +1 -1
  54. package/dist/dashboard/assets/badge-BaX2GTrn.js.gz +0 -0
  55. package/dist/dashboard/assets/{banner-DympBNq4.js → banner-DFH4Bkte.js} +1 -1
  56. package/dist/dashboard/assets/banner-DFH4Bkte.js.gz +0 -0
  57. package/dist/dashboard/assets/brand/login_register.jpg +0 -0
  58. package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
  59. package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
  60. package/dist/dashboard/assets/brand/og_image.jpg +0 -0
  61. package/dist/dashboard/assets/{card-ibRWU_uT.js → card-CxDw0lK6.js} +1 -1
  62. package/dist/dashboard/assets/card-CxDw0lK6.js.gz +0 -0
  63. package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js +1 -0
  64. package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js.gz +0 -0
  65. package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js +1 -0
  66. package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js.gz +0 -0
  67. package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js +1 -0
  68. package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js.gz +0 -0
  69. package/dist/dashboard/assets/{clipboard-button-DJm-9pz7.js → clipboard-button-DfS1ntiq.js} +1 -1
  70. package/dist/dashboard/assets/clipboard-button-DfS1ntiq.js.gz +0 -0
  71. package/dist/dashboard/assets/dialog-BK-NRr5u.js +1 -0
  72. package/dist/dashboard/assets/dialog-BK-NRr5u.js.gz +0 -0
  73. package/dist/dashboard/assets/{drawer-CqGYeOrd.js → drawer-QqPul6sC.js} +1 -1
  74. package/dist/dashboard/assets/drawer-QqPul6sC.js.gz +0 -0
  75. package/dist/dashboard/assets/entry-BMlYJVFj.js +1 -0
  76. package/dist/dashboard/assets/entry-BMlYJVFj.js.gz +0 -0
  77. package/dist/dashboard/assets/entry-D6WvojeD.js +4 -0
  78. package/dist/dashboard/assets/entry-D6WvojeD.js.gz +0 -0
  79. package/dist/dashboard/assets/entry-Dmx7gBiN.js +1 -0
  80. package/dist/dashboard/assets/entry-Dmx7gBiN.js.gz +0 -0
  81. package/dist/dashboard/assets/{formatters-D-MTJxMr.js → formatters-u7JPB5BX.js} +1 -1
  82. package/dist/dashboard/assets/formatters-u7JPB5BX.js.gz +0 -0
  83. package/dist/dashboard/assets/house-h9AZlJuT.js +1 -0
  84. package/dist/dashboard/assets/house-h9AZlJuT.js.gz +0 -0
  85. package/dist/dashboard/assets/{index-DBNPOfhV.js → index-Ko4dLRnV.js} +2 -2
  86. package/dist/dashboard/assets/index-Ko4dLRnV.js.gz +0 -0
  87. package/dist/dashboard/assets/{inline-BT0zntEr.js → inline-CHJQwxLa.js} +1 -1
  88. package/dist/dashboard/assets/inline-CHJQwxLa.js.gz +0 -0
  89. package/dist/dashboard/assets/{input-Da2ZViWg.js → input-BlmlizVV.js} +1 -1
  90. package/dist/dashboard/assets/input-BlmlizVV.js.gz +0 -0
  91. package/dist/dashboard/assets/{layout-BXNeLw6M.js → layout-Bz93I-yE.js} +1 -1
  92. package/dist/dashboard/assets/layout-Bz93I-yE.js.gz +0 -0
  93. package/dist/dashboard/assets/{list-row-BycOrrIV.js → list-row-Ml16KCfu.js} +1 -1
  94. package/dist/dashboard/assets/list-row-Ml16KCfu.js.gz +0 -0
  95. package/dist/dashboard/assets/live-7_QpnYGu.js +1 -0
  96. package/dist/dashboard/assets/live-7_QpnYGu.js.gz +0 -0
  97. package/dist/dashboard/assets/{mutation-Rr0TIvcj.js → mutation-GU7XdEjz.js} +1 -1
  98. package/dist/dashboard/assets/mutation-GU7XdEjz.js.gz +0 -0
  99. package/dist/dashboard/assets/network-CuMDaTzT.js +1 -0
  100. package/dist/dashboard/assets/network-CuMDaTzT.js.gz +0 -0
  101. package/dist/dashboard/assets/numbers-CoqpaDMx.js +2 -0
  102. package/dist/dashboard/assets/numbers-CoqpaDMx.js.gz +0 -0
  103. package/dist/dashboard/assets/pagination-B5aOoweZ.js +1 -0
  104. package/dist/dashboard/assets/pagination-B5aOoweZ.js.gz +0 -0
  105. package/dist/dashboard/assets/providers/zai.webp +0 -0
  106. package/dist/dashboard/assets/providers/zcode.webp +0 -0
  107. package/dist/dashboard/assets/{query-keys-BloKtaXJ.js → query-keys-YSoQOUh6.js} +1 -1
  108. package/dist/dashboard/assets/{query-keys-BloKtaXJ.js.gz → query-keys-YSoQOUh6.js.gz} +0 -0
  109. package/dist/dashboard/assets/quota-DJ1QlAOq.js +1 -0
  110. package/dist/dashboard/assets/quota-DJ1QlAOq.js.gz +0 -0
  111. package/dist/dashboard/assets/routing-fnCsxgNO.js +1 -0
  112. package/dist/dashboard/assets/routing-fnCsxgNO.js.gz +0 -0
  113. package/dist/dashboard/assets/{select-BIYIBkMU.js → select-BHqpd-bg.js} +2 -2
  114. package/dist/dashboard/assets/select-BHqpd-bg.js.gz +0 -0
  115. package/dist/dashboard/assets/settings-BxslsErn.js +1 -0
  116. package/dist/dashboard/assets/settings-BxslsErn.js.gz +0 -0
  117. package/dist/dashboard/assets/shield-BMQNxORh.js +1 -0
  118. package/dist/dashboard/assets/shield-BMQNxORh.js.gz +0 -0
  119. package/dist/dashboard/assets/{stack-DfgnFDWY.js → stack-j7cXjgYX.js} +1 -1
  120. package/dist/dashboard/assets/stack-j7cXjgYX.js.gz +0 -0
  121. package/dist/dashboard/assets/{state-CkfdYWel.js → state-DHmEnLa2.js} +1 -1
  122. package/dist/dashboard/assets/state-DHmEnLa2.js.gz +0 -0
  123. package/dist/dashboard/assets/{switch-SYm237HG.js → switch-DmZcyTIC.js} +1 -1
  124. package/dist/dashboard/assets/switch-DmZcyTIC.js.gz +0 -0
  125. package/dist/dashboard/assets/{useMutation-BQqCRNIi.js → useMutation-CbCP4w1o.js} +1 -1
  126. package/dist/dashboard/assets/useMutation-CbCP4w1o.js.gz +0 -0
  127. package/dist/dashboard/console-backgrounds.jpg +0 -0
  128. package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
  129. package/dist/dashboard/index.html +2 -2
  130. package/dist/dashboard/index.html.gz +0 -0
  131. package/dist/dashboard/precompressed-assets.json +60 -59
  132. package/migrations/full-postgres/0036_system_prompt_presets_refs.sql +10 -0
  133. package/migrations/full-postgres/0037_share_concurrency_scope.sql +3 -0
  134. package/migrations/full-postgres/0038_add_accounts_quota_snapshot.sql +7 -0
  135. package/migrations/full-postgres/0039_add_telemetry_provider_model_indexes.sql +13 -0
  136. package/migrations/full-postgres/0040_add_models_dialect.sql +3 -0
  137. package/migrations/full-postgres/0041_add_daily_checkin_ledger_account_fk.sql +7 -0
  138. package/migrations/full-postgres/0042_add_telemetry_degenerate_stop.sql +14 -0
  139. package/migrations/lite-sqlite3/0036_system_prompt_presets_refs.sql +14 -0
  140. package/migrations/lite-sqlite3/0037_share_concurrency_scope.sql +3 -0
  141. package/migrations/lite-sqlite3/0038_add_accounts_quota_snapshot.sql +4 -0
  142. package/migrations/lite-sqlite3/0039_add_telemetry_provider_model_indexes.sql +6 -0
  143. package/migrations/lite-sqlite3/0040_add_models_dialect.sql +3 -0
  144. package/migrations/lite-sqlite3/0041_add_daily_checkin_ledger_account_fk.sql +26 -0
  145. package/migrations/lite-sqlite3/0042_add_telemetry_degenerate_stop.sql +12 -0
  146. package/package.json +4 -6
  147. package/scripts/cli/update.ts +15 -41
  148. package/src/boot/app.ts +58 -2
  149. package/src/boot/dependencies.ts +794 -732
  150. package/src/boot/lifecycle.ts +11 -12
  151. package/src/config/cooldowns.ts +34 -55
  152. package/src/config/network.ts +1 -1
  153. package/src/config/package-paths.ts +1 -1
  154. package/src/config/providers.ts +19 -14
  155. package/src/config/resources.ts +0 -5
  156. package/src/config/runtime.ts +2 -5
  157. package/src/config/spec.ts +33 -6
  158. package/src/config/usage.ts +27 -0
  159. package/src/console/api-keys/ban-constants.ts +38 -0
  160. package/src/console/api-keys/contracts.ts +71 -62
  161. package/src/console/api-keys/routes.ts +56 -24
  162. package/src/console/auth/access.ts +4 -4
  163. package/src/console/auth/account-reset.ts +1 -1
  164. package/src/console/auth/audit-recorder.ts +1 -1
  165. package/src/console/auth/lockout.ts +7 -3
  166. package/src/console/auth/row-coercion.ts +57 -12
  167. package/src/console/auth/session-resolver.ts +1 -1
  168. package/src/console/auth/session-service.ts +8 -2
  169. package/src/console/auth/session.ts +37 -31
  170. package/src/console/auth/setup-service.ts +4 -3
  171. package/src/console/auth/user-store.ts +1 -1
  172. package/src/console/backup/9router-map.ts +0 -3
  173. package/src/console/backup/contracts.ts +9 -9
  174. package/src/console/backup/export-endpoint.ts +5 -6
  175. package/src/console/backup/nine-router.ts +1 -7
  176. package/src/console/backup/routes.ts +29 -33
  177. package/src/console/backup/service.ts +468 -468
  178. package/src/console/backup/store.ts +47 -29
  179. package/src/console/backup/validate.ts +11 -1
  180. package/src/console/cli-tools/contracts.ts +43 -299
  181. package/src/console/cli-tools/fs-ops.ts +29 -0
  182. package/src/console/cli-tools/injectors/claude.ts +3 -3
  183. package/src/console/cli-tools/injectors/codex.ts +181 -181
  184. package/src/console/cli-tools/injectors/driver.ts +150 -247
  185. package/src/console/cli-tools/injectors/kilo.ts +146 -21
  186. package/src/console/cli-tools/injectors/opencode.ts +176 -153
  187. package/src/console/cli-tools/routes.ts +7 -1
  188. package/src/console/cli-tools/service.ts +8 -7
  189. package/src/console/cli-tools/store.ts +2 -2
  190. package/src/console/console-router.ts +32 -0
  191. package/src/console/dashboard-assets.ts +9 -9
  192. package/src/console/domain-registration.ts +16 -2
  193. package/src/console/inferhub/client.ts +0 -7
  194. package/src/console/inferhub/key-store.ts +1 -1
  195. package/src/console/inferhub/normalize.ts +2 -2
  196. package/src/console/inferhub/routes.ts +1 -1
  197. package/src/console/inferhub/types.ts +1 -1
  198. package/src/console/observability/contracts.ts +7 -1
  199. package/src/console/observability/live.ts +31 -18
  200. package/src/console/observability/logs.ts +1 -1
  201. package/src/console/observability/sse.ts +62 -8
  202. package/src/console/observability/store.ts +23 -4
  203. package/src/console/providers/catalog/catalog-projections.ts +1 -1
  204. package/src/console/providers/catalog/contracts.ts +39 -0
  205. package/src/console/providers/catalog/model-operations.ts +1 -1
  206. package/src/console/providers/catalog/provider-operations.ts +95 -17
  207. package/src/console/providers/catalog/public-model-store.ts +0 -0
  208. package/src/console/providers/catalog/routes.ts +27 -3
  209. package/src/console/providers/catalog/store.ts +147 -8
  210. package/src/console/providers/detail/routes.ts +1 -1
  211. package/src/console/providers/oauth/callback-listener.ts +100 -18
  212. package/src/console/providers/oauth/routes.ts +92 -13
  213. package/src/console/quota/account-quota-global.ts +41 -3
  214. package/src/console/quota/account-quota-shared.ts +10 -3
  215. package/src/console/quota/account-quota-tenant.ts +259 -66
  216. package/src/console/quota/daily-growth-job.ts +308 -0
  217. package/src/console/quota/native-identity.ts +2 -2
  218. package/src/console/quota/quota-view.ts +1 -1
  219. package/src/console/remote-support/routes.ts +1 -1
  220. package/src/console/remote-support/service.ts +2 -2
  221. package/src/console/routing/model/contracts.ts +1 -1
  222. package/src/console/routing/pools/contracts.ts +2 -2
  223. package/src/console/routing/pools/import-parse.ts +3 -3
  224. package/src/console/routing/pools/relay-deploy.ts +3 -3
  225. package/src/console/settings/contracts.ts +29 -2
  226. package/src/console/settings/store.ts +20 -0
  227. package/src/console/share/share-router.ts +100 -32
  228. package/src/console/share/share-stats.ts +11 -13
  229. package/src/console/share/share-usage.ts +1 -1
  230. package/src/console/shared/errors.ts +2 -2
  231. package/src/console/shared/object.ts +18 -0
  232. package/src/console/shared/read-through-cache.ts +4 -4
  233. package/src/console/shared/transaction.ts +14 -0
  234. package/src/console/system-prompt/contracts.ts +89 -12
  235. package/src/console/system-prompt/routes.ts +98 -45
  236. package/src/console/updates/apply-update.ts +244 -0
  237. package/src/console/updates/routes.ts +252 -97
  238. package/src/main.ts +11 -5
  239. package/src/network/http2-bidi.ts +1 -1
  240. package/src/network/ip-utils.ts +298 -0
  241. package/src/network/outbound-fetch.ts +1 -1
  242. package/src/network/pool/agent.ts +7 -6
  243. package/src/network/pool/byte-accounting.ts +1 -1
  244. package/src/network/pool/resolver.ts +16 -12
  245. package/src/network/pool/selector.ts +150 -136
  246. package/src/network/pool-health-machine.ts +1 -1
  247. package/src/network/rate-limit-verdict.ts +1 -1
  248. package/src/network/retry.ts +1 -1
  249. package/src/network/ssrf.ts +20 -71
  250. package/src/observability/event-log.ts +9 -2
  251. package/src/observability/event-query.ts +61 -291
  252. package/src/observability/log-sanitize.ts +1 -1
  253. package/src/observability/metrics.ts +4 -4
  254. package/src/observability/payload-capture.ts +6 -5
  255. package/src/observability/payload-store.ts +1 -1
  256. package/src/observability/runtime-metrics.ts +1 -1
  257. package/src/observability/telemetry-buffer.ts +19 -3
  258. package/src/observability/telemetry-reconciliation.ts +3 -3
  259. package/src/observability/token-speed.ts +1 -1
  260. package/src/persistence/connection-url.ts +1 -1
  261. package/src/persistence/db-handle.ts +1 -1
  262. package/src/persistence/db-pg.ts +2 -2
  263. package/src/persistence/db-sqlite.ts +3 -3
  264. package/src/persistence/dialect.ts +5 -1
  265. package/src/persistence/readiness.ts +1 -1
  266. package/src/persistence/redis.ts +1 -1
  267. package/src/persistence/schema.ts +121 -40
  268. package/src/persistence/shared-cache.ts +2 -2
  269. package/src/persistence/sort-order.ts +1 -1
  270. package/src/persistence/stores/api-key-store.ts +29 -2
  271. package/src/persistence/stores/share-store.ts +20 -8
  272. package/src/persistence/system-prompt-store.ts +63 -18
  273. package/src/persistence/tenant-preferences.ts +1 -1
  274. package/src/persistence/ttl-cache.ts +3 -3
  275. package/src/persistence/usage-flush.ts +7 -2
  276. package/src/protocol/canonical-model.ts +1 -1
  277. package/src/protocol/codecs.ts +6 -10
  278. package/src/protocol/dsml.ts +8 -125
  279. package/src/protocol/image-clamp.ts +2 -2
  280. package/src/protocol/reasoning-text.ts +1 -1
  281. package/src/protocol/reasoning.ts +7 -52
  282. package/src/protocol/registry.ts +50 -15
  283. package/src/protocol/request/chat.ts +46 -19
  284. package/src/protocol/request/codex.ts +1 -1
  285. package/src/protocol/request/messages.ts +46 -14
  286. package/src/protocol/request/responses.ts +19 -10
  287. package/src/protocol/response/chat.ts +25 -10
  288. package/src/protocol/response/codex.ts +1 -1
  289. package/src/protocol/response/messages.ts +1 -1
  290. package/src/protocol/response/omp-tool-call-markup.ts +1 -1
  291. package/src/protocol/response/responses.ts +1 -1
  292. package/src/protocol/sse-decoder.ts +50 -1
  293. package/src/protocol/stop-reason.ts +2 -2
  294. package/src/protocol/think-split.ts +347 -0
  295. package/src/protocol/translation/attachment-integrity.ts +3 -3
  296. package/src/protocol/translation/cache-controls.ts +8 -20
  297. package/src/protocol/translation/capabilities.ts +6 -6
  298. package/src/protocol/translation/dialect/deepseek.ts +10 -0
  299. package/src/protocol/translation/dialect/inband.ts +39 -0
  300. package/src/protocol/translation/dialect/model-thinking.ts +89 -0
  301. package/src/protocol/translation/dialect/qwen.ts +114 -0
  302. package/src/protocol/translation/dialect/resolver.ts +83 -0
  303. package/src/protocol/translation/dialect/rules.ts +93 -0
  304. package/src/protocol/translation/dialect/thinking-ladder.ts +245 -0
  305. package/src/protocol/translation/dialect/types.ts +58 -0
  306. package/src/protocol/translation/quirks.ts +18 -4
  307. package/src/protocol/translation/thinking.ts +12 -252
  308. package/src/protocol/translation/tool-id.ts +3 -3
  309. package/src/protocol/translation/tool-repair.ts +1 -1
  310. package/src/protocol/upstream/post-json.ts +6 -1
  311. package/src/providers/adapters/compatible-adapter.ts +71 -4
  312. package/src/providers/authentication/jwt-validator.ts +0 -5
  313. package/src/providers/authentication/oauth-client.ts +254 -253
  314. package/src/providers/authentication/oauth-flow-store.ts +42 -7
  315. package/src/providers/bundled-discovery-providers.ts +6 -30
  316. package/src/providers/configured-openai-providers.ts +82 -97
  317. package/src/providers/default-registry.ts +35 -37
  318. package/src/providers/discovery/catalog/catalog-format.ts +13 -2
  319. package/src/providers/discovery/discovery-types.ts +0 -1
  320. package/src/providers/discovery/openai-model-discovery.ts +2 -2
  321. package/src/providers/discovery/probe-phases.ts +48 -13
  322. package/src/providers/discovery/probing-service.ts +6 -8
  323. package/src/providers/integrations/agentrouter/agentrouter.ts +12 -2
  324. package/src/providers/integrations/antigravity/antigravity-oauth.ts +0 -1
  325. package/src/providers/integrations/antigravity/antigravity-protocol.ts +88 -28
  326. package/src/providers/integrations/antigravity/antigravity-quota.ts +18 -6
  327. package/src/providers/integrations/antigravity/antigravity.ts +22 -13
  328. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -1
  329. package/src/providers/integrations/buddy/buddy-chat-shared.ts +20 -4
  330. package/src/providers/integrations/buddy/buddy-cli-headers.ts +19 -52
  331. package/src/providers/integrations/buddy/buddy-identity.ts +86 -0
  332. package/src/providers/integrations/buddy/buddy-oauth-shared.ts +1 -1
  333. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +1 -2
  334. package/src/providers/integrations/buddy/codebuddy/provider.ts +4 -15
  335. package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
  336. package/src/providers/integrations/buddy/workbuddy/provider.ts +5 -9
  337. package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
  338. package/src/providers/integrations/claude/claude-cch.ts +148 -148
  339. package/src/providers/integrations/claude/fallback-credit.ts +3 -7
  340. package/src/providers/integrations/cline/cline.ts +1 -1
  341. package/src/providers/integrations/codex/codex-device-code.ts +101 -101
  342. package/src/providers/integrations/codex/codex-oauth.ts +0 -1
  343. package/src/providers/integrations/commandcode/commandcode.ts +75 -115
  344. package/src/providers/integrations/commandcode/quota.ts +131 -51
  345. package/src/providers/integrations/cursor/cursor-http2.ts +0 -3
  346. package/src/providers/integrations/cursor/cursor-quota.ts +2 -6
  347. package/src/providers/integrations/cursor/cursor-request.ts +0 -87
  348. package/src/providers/integrations/cursor/cursor-shared.ts +0 -4
  349. package/src/providers/integrations/cursor/cursor.ts +35 -3
  350. package/src/providers/integrations/cursor/protocol/framing.ts +0 -56
  351. package/src/providers/integrations/cursor/protocol/messages.ts +0 -96
  352. package/src/providers/integrations/factory/factory.ts +9 -9
  353. package/src/providers/integrations/github/github-discovery.ts +70 -12
  354. package/src/providers/integrations/github/github-oauth.ts +184 -14
  355. package/src/providers/integrations/github/github.ts +134 -26
  356. package/src/providers/integrations/grok/grok-oauth.ts +261 -261
  357. package/src/providers/integrations/grok/grok-quota-frame.ts +1 -1
  358. package/src/providers/integrations/grok/grok-quota.ts +16 -7
  359. package/src/providers/integrations/grok/grok-ratelimit-store.ts +0 -5
  360. package/src/providers/integrations/grok/grok-ratelimit.ts +3 -0
  361. package/src/providers/integrations/grok/grok.ts +86 -36
  362. package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
  363. package/src/providers/integrations/kiro/kiro-cache.ts +31 -0
  364. package/src/providers/integrations/kiro/kiro-catalog.ts +31 -5
  365. package/src/providers/integrations/kiro/kiro-discovery.ts +47 -43
  366. package/src/providers/integrations/kiro/kiro-machine-id.ts +6 -3
  367. package/src/providers/integrations/kiro/kiro-oauth.ts +523 -255
  368. package/src/providers/integrations/kiro/kiro-quota.ts +35 -47
  369. package/src/providers/integrations/kiro/kiro-request.ts +8 -11
  370. package/src/providers/integrations/kiro/kiro-stream.ts +13 -0
  371. package/src/providers/integrations/kiro/kiro.ts +80 -68
  372. package/src/providers/integrations/muse/muse-oauth.ts +0 -1
  373. package/src/providers/integrations/muse/muse-quota.ts +0 -4
  374. package/src/providers/integrations/opencode/fingerprint.ts +7 -5
  375. package/src/providers/integrations/opencode/provider.ts +17 -24
  376. package/src/providers/integrations/qoder/qoder-catalog.ts +94 -11
  377. package/src/providers/integrations/qoder/qoder-oauth.ts +165 -25
  378. package/src/providers/integrations/qoder/qoder-quota.ts +48 -6
  379. package/src/providers/integrations/qoder/qoder-realm.ts +10 -31
  380. package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
  381. package/src/providers/integrations/qoder/qoder-umid.ts +21 -25
  382. package/src/providers/integrations/qoder/qoder-virtual.ts +12 -0
  383. package/src/providers/integrations/qoder/qoder.ts +422 -71
  384. package/src/providers/integrations/shared/connect.ts +0 -54
  385. package/src/providers/integrations/shared/hash.ts +15 -0
  386. package/src/providers/integrations/shared/session-cache.ts +136 -0
  387. package/src/providers/integrations/trae/trae-cache.ts +7 -87
  388. package/src/providers/integrations/trae/trae-catalog.ts +1 -6
  389. package/src/providers/integrations/trae/trae-protocol.ts +17 -8
  390. package/src/providers/integrations/trae/trae-realm.ts +0 -7
  391. package/src/providers/integrations/trae/trae.ts +51 -8
  392. package/src/providers/integrations/xai/xai-oauth.ts +5 -5
  393. package/src/providers/integrations/zcode-start-plan/gifts.ts +265 -0
  394. package/src/providers/integrations/zcode-start-plan/oauth.ts +154 -0
  395. package/src/providers/integrations/zcode-start-plan/provider.ts +131 -0
  396. package/src/providers/integrations/zcode-start-plan/quota.ts +152 -0
  397. package/src/providers/integrations/zcode-start-plan/zcode_prompt.json +10 -0
  398. package/src/providers/operations/account-error-classification.ts +198 -133
  399. package/src/providers/operations/account-health-service.ts +31 -39
  400. package/src/providers/operations/account-reset-service.ts +58 -1
  401. package/src/providers/operations/byok-wire-profile.ts +19 -15
  402. package/src/providers/operations/cli-platform.ts +31 -5
  403. package/src/providers/operations/client-version-resolver.ts +4 -6
  404. package/src/providers/operations/client-versions.ts +10 -7
  405. package/src/providers/operations/provider-catalog-seeder.ts +2 -0
  406. package/src/providers/operations/provider-catalog-service.ts +18 -2
  407. package/src/providers/operations/provider-credential-service.ts +19 -2
  408. package/src/providers/provider-contracts.ts +18 -7
  409. package/src/providers/provider-metadata.ts +17 -2
  410. package/src/providers/provider-model-definition.ts +22 -1
  411. package/src/providers/provider-probe.ts +4 -2
  412. package/src/providers/provider-usage.ts +50 -19
  413. package/src/providers/quota/cache.ts +29 -0
  414. package/src/providers/quota/quota-contracts.ts +4 -0
  415. package/src/providers/quota/refresh.ts +27 -4
  416. package/src/providers/quota/snapshot.ts +94 -0
  417. package/src/providers/search/search-provider.ts +1 -3
  418. package/src/providers/search/search-providers.ts +2 -1
  419. package/src/security/abuse.ts +4 -4
  420. package/src/security/admission/service.ts +13 -6
  421. package/src/security/admission/ttl.ts +61 -61
  422. package/src/security/api-key-auth.ts +17 -3
  423. package/src/security/client-router-fingerprint.ts +4 -4
  424. package/src/security/credential-codec.ts +3 -3
  425. package/src/security/csrf.ts +2 -2
  426. package/src/security/ip-boundary.ts +16 -189
  427. package/src/security/model-access-rule.ts +1 -1
  428. package/src/security/outbound-headers.ts +18 -0
  429. package/src/shared/invoke-guard.ts +47 -0
  430. package/src/transport/contracts.ts +13 -4
  431. package/src/transport/dispatch/attempt-finalize.ts +70 -22
  432. package/src/transport/dispatch/attempt-loop.ts +22 -0
  433. package/src/transport/dispatch/capture-policy.ts +34 -37
  434. package/src/transport/dispatch/empty-stream.ts +23 -20
  435. package/src/transport/dispatch/generation.ts +25 -45
  436. package/src/transport/dispatch/native-shared.ts +127 -0
  437. package/src/transport/dispatch/proxy-request.ts +3 -1
  438. package/src/transport/dispatch/responses-compact.ts +25 -46
  439. package/src/transport/dispatch/streaming/encoder.ts +18 -72
  440. package/src/transport/dispatch/streaming/finalize.ts +2 -2
  441. package/src/transport/dispatch/streaming/stream.ts +7 -3
  442. package/src/transport/dispatch/streaming/thinking-loop.ts +1 -1
  443. package/src/transport/dispatch/systemone.ts +20 -48
  444. package/src/transport/dispatch/token-count.ts +41 -38
  445. package/src/transport/dispatch/websearch.ts +19 -45
  446. package/src/transport/drain-endpoint.ts +1 -1
  447. package/src/transport/failure-policy.ts +21 -1
  448. package/src/transport/guards/auth-guard.ts +0 -6
  449. package/src/transport/guards/body-policy.ts +3 -3
  450. package/src/transport/guards/error-lifecycle.ts +4 -0
  451. package/src/transport/guards/headroom-guard.ts +1 -1
  452. package/src/transport/guards/pipeline.ts +1 -1
  453. package/src/transport/guards/request-context.ts +12 -17
  454. package/src/transport/request/preparer.ts +286 -7
  455. package/src/transport/request/rtk/compress-request.ts +3 -3
  456. package/src/transport/request/rtk/rtk-constants.ts +2 -2
  457. package/src/transport/request/rtk/rtk-filters.ts +8 -8
  458. package/src/transport/request/state.ts +7 -13
  459. package/src/transport/request/system-prompt-injection.ts +74 -11
  460. package/src/transport/request/tenant-preferences.ts +22 -0
  461. package/src/transport/routing/candidate-index.ts +2 -2
  462. package/src/transport/routing/round-robin.ts +1 -1
  463. package/src/transport/routing/route-catalog.ts +17 -18
  464. package/src/transport/routing/route-model.ts +126 -33
  465. package/src/transport/routing/router.ts +159 -37
  466. package/src/transport/shutdown-notice.ts +2 -2
  467. package/src/transport/surface/adapters.ts +3 -3
  468. package/src/transport/surface/chat/encode.ts +7 -7
  469. package/src/transport/surface/chat/parse.ts +7 -7
  470. package/src/transport/surface/completion.ts +2 -2
  471. package/src/transport/surface/content-parts.ts +1 -1
  472. package/src/transport/surface/dialects.ts +2 -2
  473. package/src/transport/surface/messages/parse.ts +3 -3
  474. package/src/transport/surface/reasoning-summary.ts +9 -14
  475. package/src/transport/surface/responses/adapter.ts +1 -1
  476. package/src/transport/surface/responses/errors.ts +0 -9
  477. package/src/transport/vision-handoff-cache.ts +58 -0
  478. package/src/transport/vision-handoff.ts +431 -0
  479. package/src/workers/account-identity-sweep.ts +2 -2
  480. package/src/workers/buddy-growth-tasks.ts +392 -346
  481. package/src/workers/daily-checkin.ts +102 -49
  482. package/src/workers/maintenance-lease.ts +2 -35
  483. package/src/workers/oauth-refresh-sweep.ts +8 -1
  484. package/src/workers/quota-refresh-sweep.ts +120 -82
  485. package/src/workers/scheduler.ts +67 -4
  486. package/src/workers/sweep-runner.ts +24 -5
  487. package/src/workers/zcode-gift-claim.ts +108 -0
  488. package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js +0 -1
  489. package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js.gz +0 -0
  490. package/dist/dashboard/assets/BannedPage-ChG5svFb.js +0 -1
  491. package/dist/dashboard/assets/BannedPage-ChG5svFb.js.gz +0 -0
  492. package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js +0 -1
  493. package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js.gz +0 -0
  494. package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js +0 -1
  495. package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js.gz +0 -0
  496. package/dist/dashboard/assets/CombosPage-DCgm5g6g.js +0 -1
  497. package/dist/dashboard/assets/CombosPage-DCgm5g6g.js.gz +0 -0
  498. package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js +0 -1
  499. package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js.gz +0 -0
  500. package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js +0 -1
  501. package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js.gz +0 -0
  502. package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js +0 -1
  503. package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js.gz +0 -0
  504. package/dist/dashboard/assets/LoginPage-U6AKaJcF.js +0 -1
  505. package/dist/dashboard/assets/LoginPage-U6AKaJcF.js.gz +0 -0
  506. package/dist/dashboard/assets/ModelPicker-BMS2t8BA.js.gz +0 -0
  507. package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js +0 -1
  508. package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js.gz +0 -0
  509. package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js +0 -5
  510. package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js.gz +0 -0
  511. package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +0 -1
  512. package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
  513. package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js +0 -1
  514. package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js.gz +0 -0
  515. package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js.gz +0 -0
  516. package/dist/dashboard/assets/QuotaPage-L0skzML8.js +0 -1
  517. package/dist/dashboard/assets/QuotaPage-L0skzML8.js.gz +0 -0
  518. package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js +0 -1
  519. package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js.gz +0 -0
  520. package/dist/dashboard/assets/SetupPage-f0DE1h0-.js +0 -1
  521. package/dist/dashboard/assets/SetupPage-f0DE1h0-.js.gz +0 -0
  522. package/dist/dashboard/assets/SortableList-ClzpwyET.js +0 -1
  523. package/dist/dashboard/assets/SortableList-ClzpwyET.js.gz +0 -0
  524. package/dist/dashboard/assets/StudioPage-Cq5tW7hW.js.gz +0 -0
  525. package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js +0 -1
  526. package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js.gz +0 -0
  527. package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js +0 -56
  528. package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js.gz +0 -0
  529. package/dist/dashboard/assets/api-keys-C38iXoO4.js +0 -1
  530. package/dist/dashboard/assets/api-keys-C38iXoO4.js.gz +0 -0
  531. package/dist/dashboard/assets/app-BayTTy01.js +0 -9
  532. package/dist/dashboard/assets/app-BayTTy01.js.gz +0 -0
  533. package/dist/dashboard/assets/app-a159k0ub.css +0 -2
  534. package/dist/dashboard/assets/app-a159k0ub.css.gz +0 -0
  535. package/dist/dashboard/assets/assert-DbRA8EG2.js +0 -15
  536. package/dist/dashboard/assets/assert-DbRA8EG2.js.gz +0 -0
  537. package/dist/dashboard/assets/badge-D9CS6dZk.js.gz +0 -0
  538. package/dist/dashboard/assets/banner-DympBNq4.js.gz +0 -0
  539. package/dist/dashboard/assets/card-ibRWU_uT.js.gz +0 -0
  540. package/dist/dashboard/assets/cli-tools-63qBGaRw.js +0 -1
  541. package/dist/dashboard/assets/cli-tools-63qBGaRw.js.gz +0 -0
  542. package/dist/dashboard/assets/clipboard-button-DJm-9pz7.js.gz +0 -0
  543. package/dist/dashboard/assets/dialog-8fiE6tec.js +0 -1
  544. package/dist/dashboard/assets/dialog-8fiE6tec.js.gz +0 -0
  545. package/dist/dashboard/assets/drawer-CqGYeOrd.js.gz +0 -0
  546. package/dist/dashboard/assets/entry-BSAsYsmV.js +0 -1
  547. package/dist/dashboard/assets/entry-BSAsYsmV.js.gz +0 -0
  548. package/dist/dashboard/assets/entry-CiIQULZD.js +0 -4
  549. package/dist/dashboard/assets/entry-CiIQULZD.js.gz +0 -0
  550. package/dist/dashboard/assets/entry-Daz0lJ-W.js +0 -1
  551. package/dist/dashboard/assets/entry-Daz0lJ-W.js.gz +0 -0
  552. package/dist/dashboard/assets/formatters-D-MTJxMr.js.gz +0 -0
  553. package/dist/dashboard/assets/github-badge-CPK47xQx.js +0 -1
  554. package/dist/dashboard/assets/github-badge-CPK47xQx.js.gz +0 -0
  555. package/dist/dashboard/assets/index-DBNPOfhV.js.gz +0 -0
  556. package/dist/dashboard/assets/inline-BT0zntEr.js.gz +0 -0
  557. package/dist/dashboard/assets/input-Da2ZViWg.js.gz +0 -0
  558. package/dist/dashboard/assets/layout-BXNeLw6M.js.gz +0 -0
  559. package/dist/dashboard/assets/list-row-BycOrrIV.js.gz +0 -0
  560. package/dist/dashboard/assets/live-DCQNZpF7.js +0 -1
  561. package/dist/dashboard/assets/live-DCQNZpF7.js.gz +0 -0
  562. package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
  563. package/dist/dashboard/assets/network-9Yj2ZyLo.js +0 -1
  564. package/dist/dashboard/assets/network-9Yj2ZyLo.js.gz +0 -0
  565. package/dist/dashboard/assets/numbers-CEagj6ZZ.js +0 -2
  566. package/dist/dashboard/assets/numbers-CEagj6ZZ.js.gz +0 -0
  567. package/dist/dashboard/assets/providers/zai-coding-plan.svg +0 -6
  568. package/dist/dashboard/assets/providers/zai-coding-plan.svg.gz +0 -0
  569. package/dist/dashboard/assets/quota-Caw7wqci.js +0 -1
  570. package/dist/dashboard/assets/quota-Caw7wqci.js.gz +0 -0
  571. package/dist/dashboard/assets/routing-CO9zCUus.js +0 -1
  572. package/dist/dashboard/assets/routing-CO9zCUus.js.gz +0 -0
  573. package/dist/dashboard/assets/select-BIYIBkMU.js.gz +0 -0
  574. package/dist/dashboard/assets/settings-jS3-PNSA.js +0 -1
  575. package/dist/dashboard/assets/settings-jS3-PNSA.js.gz +0 -0
  576. package/dist/dashboard/assets/stack-DfgnFDWY.js.gz +0 -0
  577. package/dist/dashboard/assets/state-CkfdYWel.js.gz +0 -0
  578. package/dist/dashboard/assets/stream-retry-5QnVuSSo.js +0 -1
  579. package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
  580. package/dist/dashboard/assets/switch-SYm237HG.js.gz +0 -0
  581. package/dist/dashboard/assets/useMutation-BQqCRNIi.js.gz +0 -0
  582. package/src/console/cli-tools/injectors/cline.ts +0 -110
  583. package/src/console/cli-tools/injectors/copilot.ts +0 -108
  584. package/src/console/cli-tools/injectors/cowork.ts +0 -64
  585. package/src/console/cli-tools/injectors/deepseek-tui.ts +0 -85
  586. package/src/console/cli-tools/injectors/droid.ts +0 -96
  587. package/src/console/cli-tools/injectors/grok-build.ts +0 -70
  588. package/src/console/cli-tools/injectors/hermes.ts +0 -101
  589. package/src/console/cli-tools/injectors/jcode.ts +0 -115
  590. package/src/console/cli-tools/injectors/openclaw.ts +0 -161
  591. package/src/network/response-headers.ts +0 -39
  592. package/src/protocol/response/dsml.ts +0 -38
  593. package/src/providers/integrations/xiaomi-mimo/think-stream.ts +0 -229
  594. /package/dist/dashboard/assets/brand/{og_bansos.webp → og_bansos_initial.webp} +0 -0
  595. /package/dist/dashboard/assets/brand/{og_image.webp → og_image_initial.webp} +0 -0
@@ -1,8 +1,35 @@
1
1
  // Tenant payload-capture policy resolved once and shared by dispatch attempts.
2
2
  import type { CartethyiaDatabase } from "../../persistence/db-handle";
3
- import { payloadRetentionMs, resolvePayloadCapture } from "../../observability/payload-capture";
3
+ import { resolvePayloadCapture } from "../../observability/payload-capture";
4
4
  import type { TelemetryPayloadDepth, TelemetryPayloadMode } from "../../console/settings/contracts";
5
5
 
6
+ /** The resolved payload-capture preference, memoized on the request state. */
7
+ export type PayloadCapturePreference =
8
+ | {
9
+ readonly ok: true;
10
+ readonly mode: TelemetryPayloadMode;
11
+ readonly depth: TelemetryPayloadDepth;
12
+ readonly retentionMs: number;
13
+ }
14
+ | { readonly ok: false };
15
+
16
+ /** The slice of request state that memoizes the capture preference. */
17
+ export type CapturePreferenceState = { payloadCapturePreference?: PayloadCapturePreference };
18
+
19
+ /** Resolves the tenant's capture preference once, memoized on the request state. */
20
+ async function resolveCapturePreference(
21
+ db: CartethyiaDatabase,
22
+ tenantId: string | null,
23
+ state: CapturePreferenceState,
24
+ ): Promise<PayloadCapturePreference> {
25
+ let preference = state.payloadCapturePreference;
26
+ if (preference === undefined) {
27
+ preference = await resolvePayloadCapture(db, tenantId);
28
+ state.payloadCapturePreference = preference;
29
+ }
30
+ return preference;
31
+ }
32
+
6
33
  /** Per-stage body cap for the provider request/response tee. */
7
34
  export function providerCaptureMaxBytes(depth?: TelemetryPayloadDepth): number {
8
35
  if (depth === "moderate") return 2 * 1024 * 1024;
@@ -14,14 +41,9 @@ export function providerCaptureMaxBytes(depth?: TelemetryPayloadDepth): number {
14
41
  export async function shouldCaptureProviderBodyForRequest(
15
42
  db: CartethyiaDatabase,
16
43
  tenantId: string | null,
17
- state: { payloadCapturePreference?: unknown },
44
+ state: CapturePreferenceState,
18
45
  ): Promise<boolean> {
19
- if (state.payloadCapturePreference === undefined) {
20
- state.payloadCapturePreference = await resolvePayloadCapture(db, tenantId);
21
- }
22
- const resolved = state.payloadCapturePreference as
23
- | { readonly ok: true; readonly mode: string }
24
- | { readonly ok: false };
46
+ const resolved = await resolveCapturePreference(db, tenantId, state);
25
47
  return resolved.ok === true && resolved.mode === "full";
26
48
  }
27
49
 
@@ -29,14 +51,9 @@ export async function shouldCaptureProviderBodyForRequest(
29
51
  export async function resolveCaptureModeForRequest(
30
52
  db: CartethyiaDatabase,
31
53
  tenantId: string | null,
32
- state: { payloadCapturePreference?: unknown },
54
+ state: CapturePreferenceState,
33
55
  ): Promise<TelemetryPayloadMode> {
34
- if (state.payloadCapturePreference === undefined) {
35
- state.payloadCapturePreference = await resolvePayloadCapture(db, tenantId);
36
- }
37
- const resolved = state.payloadCapturePreference as
38
- | { readonly ok: true; readonly mode: TelemetryPayloadMode }
39
- | { readonly ok: false };
56
+ const resolved = await resolveCapturePreference(db, tenantId, state);
40
57
  return resolved.ok === true ? resolved.mode : "none";
41
58
  }
42
59
 
@@ -44,32 +61,12 @@ export async function resolveCaptureModeForRequest(
44
61
  export async function resolveCaptureDepthForRequest(
45
62
  db: CartethyiaDatabase,
46
63
  tenantId: string | null,
47
- state: { payloadCapturePreference?: unknown },
64
+ state: CapturePreferenceState,
48
65
  ): Promise<TelemetryPayloadDepth> {
49
- if (state.payloadCapturePreference === undefined) {
50
- state.payloadCapturePreference = await resolvePayloadCapture(db, tenantId);
51
- }
52
- const resolved = state.payloadCapturePreference as
53
- | { readonly ok: true; readonly depth: TelemetryPayloadDepth }
54
- | { readonly ok: false };
66
+ const resolved = await resolveCapturePreference(db, tenantId, state);
55
67
  return resolved.ok === true ? resolved.depth : "minimum";
56
68
  }
57
69
 
58
- /** Resolve the tenant's payload retention once per request. */
59
- export async function resolveCaptureRetentionForRequest(
60
- db: CartethyiaDatabase,
61
- tenantId: string | null,
62
- state: { payloadCapturePreference?: unknown },
63
- ): Promise<number> {
64
- if (state.payloadCapturePreference === undefined) {
65
- state.payloadCapturePreference = await resolvePayloadCapture(db, tenantId);
66
- }
67
- const resolved = state.payloadCapturePreference as
68
- | { readonly ok: true; readonly retentionMs: number }
69
- | { readonly ok: false };
70
- return resolved.ok === true ? resolved.retentionMs : payloadRetentionMs();
71
- }
72
-
73
70
  /** Combined body cap for the four payload panels at one configured depth. */
74
71
  export function captureDepthMaxBytes(depth: TelemetryPayloadDepth): number {
75
72
  if (depth === "moderate") return 8 * 1024 * 1024;
@@ -52,13 +52,13 @@ export type EmptyStreamFetch = (
52
52
  * unjittered, so a provider returning 200-with-no-events could drive an
53
53
  * unbounded wait, and concurrent clients could resynchronize their retries.
54
54
  */
55
- export interface EmptyStreamPolicy {
55
+ interface EmptyStreamPolicy {
56
56
  readonly maxEmptyReplays?: number;
57
57
  readonly firstEventTimeoutMs?: number;
58
58
  readonly sleep?: (ms: number, signal: AbortSignal) => Promise<void>;
59
59
  }
60
60
 
61
- export interface EmptyStreamResult {
61
+ interface EmptyStreamResult {
62
62
  readonly response: Response;
63
63
  readonly tally: StreamTally;
64
64
  readonly endpoint: string;
@@ -112,8 +112,7 @@ export async function fetchWithEmptyStreamRecovery(
112
112
 
113
113
  for (let endpointIndex = 0; endpointIndex < endpoints.length; endpointIndex++) {
114
114
  const endpoint = endpoints[endpointIndex]!;
115
- const isLastEndpoint = endpointIndex === endpoints.length - 1;
116
- const replaysAllowed = isLastEndpoint ? maxReplays : maxReplays;
115
+ const replaysAllowed = maxReplays;
117
116
 
118
117
  for (let replay = 0; replay <= replaysAllowed; replay++) {
119
118
  // Check abort before each fetch
@@ -167,12 +166,7 @@ export async function fetchWithEmptyStreamRecovery(
167
166
  // Empty — replay if we haven't exhausted this endpoint's budget
168
167
  if (replay < replaysAllowed) {
169
168
  const delay = fallbackRetryDelayMs(replay);
170
- try {
171
- await sleep(delay, deps.signal);
172
- } catch (error) {
173
- if (error instanceof GatewayError) throw error;
174
- throw error;
175
- }
169
+ await sleep(delay, deps.signal);
176
170
  }
177
171
  }
178
172
  }
@@ -196,9 +190,22 @@ async function fetchWithTimeout(
196
190
  signal: AbortSignal,
197
191
  timeoutMs: number,
198
192
  ): Promise<Response> {
199
- // Race the fetch against a first-event timeout
193
+ // Race the fetch against a first-event timeout. The timer and the abort
194
+ // listener are both cleaned up once the race settles, so a successful fetch
195
+ // leaves neither behind.
196
+ let timer: ReturnType<typeof setTimeout> | undefined;
197
+ const onAbort = (): void => {
198
+ if (timer !== undefined) clearTimeout(timer);
199
+ // A client cancellation is the caller's signal; the fetch already races the
200
+ // same abort signal, so rejecting here only surfaces the 499 envelope.
201
+ timeoutPromiseReject(
202
+ new GatewayError("transport_closed", 499, "The request was aborted (request {requestId}). Send it again whenever ready 🙃"),
203
+ );
204
+ };
205
+ let timeoutPromiseReject!: (reason: unknown) => void;
200
206
  const timeoutPromise = new Promise<never>((_, reject) => {
201
- const timer = setTimeout(() => {
207
+ timeoutPromiseReject = reject;
208
+ timer = setTimeout(() => {
202
209
  // A first-event timeout is the gateway waiting on a silent upstream (e.g.
203
210
  // a provider that holds the request in a queue), not the client leaving.
204
211
  // It must surface as a deadline failure, never as a `transport_closed`
@@ -206,19 +213,15 @@ async function fetchWithTimeout(
206
213
  // otherwise hide a real upstream wait behind "cancelled by client".
207
214
  reject(new GatewayError("deadline_exceeded", 504, "First event timeout (request {requestId}). Some things just take longer 🐢", {}, "upstream"));
208
215
  }, timeoutMs);
209
- signal.addEventListener("abort", () => {
210
- clearTimeout(timer);
211
- reject(new GatewayError("transport_closed", 499, "The request was aborted (request {requestId}). Send it again whenever ready 🙃"));
212
- }, { once: true });
213
216
  });
214
-
217
+ signal.addEventListener("abort", onAbort, { once: true });
215
218
  try {
216
219
  return await Promise.race([
217
220
  fetch(url, { headers: request.headers, body: request.body, signal }),
218
221
  timeoutPromise,
219
222
  ]);
220
- } catch (error) {
221
- if (error instanceof GatewayError) throw error;
222
- throw error;
223
+ } finally {
224
+ if (timer !== undefined) clearTimeout(timer);
225
+ signal.removeEventListener("abort", onAbort);
223
226
  }
224
227
  }
@@ -20,20 +20,26 @@
20
20
  */
21
21
  import type { ImageGenerationOutcome, ProviderAdapter, ProviderDispatchTarget } from "../../providers/provider-contracts";
22
22
  import { GatewayError } from "../../protocol/gateway-error";
23
- import { resolveCredentialForAccount } from "../../providers/operations/provider-credential-service";
24
23
  import type { ServiceKind } from "../../protocol/canonical-model";
25
24
  import { metrics } from "../../observability/metrics";
26
25
  import type { ProxyRequestState } from "../request/state";
27
26
  import { completeAttempt, estimatedUsage, nativeCompletionBase } from "./attempt-finalize";
28
27
  import { runAttemptLoop } from "./attempt-loop";
29
28
  import { captureProviderRateLimit } from "./ratelimit-capture";
29
+ import {
30
+ hasScope,
31
+ jsonResponse,
32
+ networkFetchContext,
33
+ requireRecordBody,
34
+ resolveAttemptCredential,
35
+ } from "./native-shared";
30
36
 
31
37
  import type { GatewayHandlerDeps } from "./handler-deps";
32
38
 
33
- export type GenerationHandlerDeps = GatewayHandlerDeps;
39
+ type GenerationHandlerDeps = GatewayHandlerDeps;
34
40
 
35
41
  /** Which surface a handler serves. Drives candidate filtering and error copy. */
36
- export type GenerationKind = Extract<ServiceKind, "imageGeneration" | "videoGeneration">;
42
+ type GenerationKind = Extract<ServiceKind, "imageGeneration" | "videoGeneration">;
37
43
 
38
44
  /**
39
45
  * Surfaces the gateway routes but does not model.
@@ -44,7 +50,7 @@ export type GenerationKind = Extract<ServiceKind, "imageGeneration" | "videoGene
44
50
  * is the whole feature: routing, admission, failover, and accounting still
45
51
  * apply, and the upstream's own bytes come back untouched.
46
52
  */
47
- export type PassthroughKind = Extract<ServiceKind, "embedding" | "audioTranscription" | "audioSpeech">;
53
+ type PassthroughKind = Extract<ServiceKind, "embedding" | "audioTranscription" | "audioSpeech">;
48
54
 
49
55
  /** Error-copy noun per surface, so a refusal names what the caller asked for. */
50
56
  const SURFACE_NOUNS: Record<GenerationKind | PassthroughKind, string> = {
@@ -74,13 +80,6 @@ function isMediaKind(kind: GenerationKind | PassthroughKind): kind is Generation
74
80
  */
75
81
  const MAX_ITEMS_PER_REQUEST = 10;
76
82
 
77
- function jsonResponse(payload: unknown, status: number, requestId: string): Response {
78
- return new Response(JSON.stringify(payload), {
79
- status,
80
- headers: { "content-type": "application/json", "x-request-id": requestId },
81
- });
82
- }
83
-
84
83
  /**
85
84
  * Forwards a streaming upstream response to the client and accounts for it.
86
85
  *
@@ -145,12 +144,12 @@ async function passThrough(
145
144
  }
146
145
 
147
146
  /** Builds the handler for one generation surface. */
148
- export function createGenerationHandler(kind: GenerationKind, deps: GenerationHandlerDeps) {
147
+ function createGenerationHandler(kind: GenerationKind, deps: GenerationHandlerDeps) {
149
148
  return createNativeSurfaceHandler(kind, deps);
150
149
  }
151
150
 
152
151
  /** Builds the handler for one routed surface, media or passthrough. */
153
- export function createNativeSurfaceHandler(
152
+ function createNativeSurfaceHandler(
154
153
  kind: GenerationKind | PassthroughKind,
155
154
  deps: GenerationHandlerDeps,
156
155
  ) {
@@ -158,19 +157,14 @@ export function createNativeSurfaceHandler(
158
157
  return async ({ request }: { request: Request }): Promise<Response> => {
159
158
  const state = deps.stateStore.require(request);
160
159
  const authorization = state.authorization;
161
- const body = state.ingressBody;
162
- if (!authorization || body === null || typeof body !== "object" || Array.isArray(body)) {
163
- throw new GatewayError(
164
- "invalid_request",
165
- 400,
166
- `The ${noun} body must be a JSON object (request {requestId}). Send one with at least a model and a prompt. Check the shape of what you sent — then resend 🪞`,
167
- );
168
- }
169
- const generation = body as Record<string, unknown>;
160
+ const generation = requireRecordBody(
161
+ state,
162
+ `The ${noun} body must be a JSON object (request {requestId}). Send one with at least a model and a prompt. Check the shape of what you sent — then resend 🪞`,
163
+ );
170
164
  if (typeof generation.model !== "string" || generation.model.trim().length === 0) {
171
165
  throw new GatewayError("invalid_request", 400, `A model is required (request {requestId}): it names the ${noun} provider to use. Check the shape of what you sent — then resend 🪞`);
172
166
  }
173
- if (!authorization.snapshot.scopes?.includes("routing:invoke" as never)) {
167
+ if (!hasScope(authorization?.snapshot.scopes, "routing:invoke")) {
174
168
  throw new GatewayError(
175
169
  "policy_rejected",
176
170
  403,
@@ -200,10 +194,15 @@ export function createNativeSurfaceHandler(
200
194
  const prepared = await deps.proxyPreparer.prepareNativeService({
201
195
  model: generation.model,
202
196
  serviceKind: kind,
203
- authorization,
197
+ // `requireRecordBody` already refused a missing authorization; this guard
198
+ // exists only to narrow the type for the code below.
199
+ authorization: authorization!,
204
200
  ...(state.abortController.signal ? { signal: state.abortController.signal } : {}),
205
201
  ...(state.clientUserAgent === undefined ? {} : { clientUserAgent: state.clientUserAgent }),
206
202
  });
203
+ if (!authorization) {
204
+ throw new GatewayError("invalid_request", 400, "authentication required (request {requestId})");
205
+ }
207
206
 
208
207
  return runAttemptLoop<Response, ProviderAdapter>({
209
208
  state,
@@ -221,19 +220,7 @@ export function createNativeSurfaceHandler(
221
220
  strictPoolSelection: false,
222
221
  exhaustedError: new GatewayError("route_unavailable", 503, `no eligible ${noun} generation route (request {requestId}). The bouncer stepped out — come back shortly 🚪`),
223
222
  prepare: async (candidate) => {
224
- const credential = candidate.provider_account_id
225
- ? await resolveCredentialForAccount(
226
- deps.db,
227
- candidate.provider_id,
228
- candidate.provider_account_id,
229
- deps.oauthRefreshService && deps.resolveOAuthRefresher
230
- ? { refreshService: deps.oauthRefreshService, resolveRefresher: deps.resolveOAuthRefresher }
231
- : undefined,
232
- )
233
- : {
234
- provider_id: candidate.provider_id as ProviderAdapter["provider_id"],
235
- credential_kind: "none" as const,
236
- };
223
+ const credential = await resolveAttemptCredential(deps, candidate);
237
224
  const adapter = await deps.resolveProviderAdapter?.(candidate.provider_id);
238
225
  if (!adapter) {
239
226
  throw new GatewayError(
@@ -309,14 +296,7 @@ export function createNativeSurfaceHandler(
309
296
  abort_signal: state.abortController.signal,
310
297
  request_headers: inboundHeaders,
311
298
  ...(candidate.user_agent === undefined ? {} : { user_agent: candidate.user_agent }),
312
- ...(deps.networkBindingFactory
313
- ? {
314
- outbound_fetch: deps.networkBindingFactory.fetch(
315
- leases.proxySlot?.poolId,
316
- authorization.tenantId,
317
- ),
318
- }
319
- : {}),
299
+ ...networkFetchContext(deps, leases.proxySlot?.poolId, authorization.tenantId),
320
300
  };
321
301
  // Prefer the provider's own method for this kind; otherwise post the
322
302
  // body to the candidate's endpoint through the adapter, which owns the
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Shared helpers for the native dispatch handlers (web search, token count,
3
+ * System One, responses-compact, generation). The handlers previously spelled
4
+ * the same credential-resolution, outbound-fetch, body-validation, thinking-
5
+ * suffix, response-serialization, and usage-clone blocks by hand, and the
6
+ * copies could drift (generation's `jsonResponse` lost the `cache-control`
7
+ * header). One home here keeps them in step.
8
+ */
9
+ import { GatewayError } from "../../protocol/gateway-error";
10
+ import type { ProviderId, ResolvedCredential, ValidatedOutboundFetch } from "../../providers/provider-contracts";
11
+ import { resolveCredentialForAccount } from "../../providers/operations/provider-credential-service";
12
+ import { repriceUsage, usageFromProvider } from "../../providers/provider-usage";
13
+ import type { UsageRecord } from "../../protocol/canonical-model";
14
+ import { parseThinkingSuffix } from "../../protocol/translation/thinking";
15
+ import type { GatewayHandlerDeps } from "./handler-deps";
16
+
17
+ /**
18
+ * Whether a scope list contains the named scope. The native handlers used to
19
+ * force `scopes?.includes(name as never)` — a type-loop hole that bypassed
20
+ * the `AccessScope` union only to compare runtime strings. The check is the
21
+ * same, with a real `readonly string[]` receiver.
22
+ */
23
+ export function hasScope(scopes: readonly string[] | undefined, name: string): boolean {
24
+ return scopes?.includes(name) === true;
25
+ }
26
+
27
+ /**
28
+ * Resolves the credential for one candidate: a real account credential when
29
+ * the candidate carries an account, otherwise the credential-less `"none"`
30
+ * credential. Shared by every native handler's `prepare`.
31
+ */
32
+ export async function resolveAttemptCredential(
33
+ deps: Pick<GatewayHandlerDeps, "db" | "oauthRefreshService" | "resolveOAuthRefresher">,
34
+ candidate: { readonly provider_id: string; readonly provider_account_id?: string },
35
+ ): Promise<ResolvedCredential> {
36
+ if (!candidate.provider_account_id) {
37
+ return {
38
+ provider_id: candidate.provider_id as ProviderId,
39
+ credential_kind: "none" as const,
40
+ };
41
+ }
42
+ return resolveCredentialForAccount(
43
+ deps.db,
44
+ candidate.provider_id,
45
+ candidate.provider_account_id,
46
+ deps.oauthRefreshService && deps.resolveOAuthRefresher
47
+ ? { refreshService: deps.oauthRefreshService, resolveRefresher: deps.resolveOAuthRefresher }
48
+ : undefined,
49
+ );
50
+ }
51
+
52
+ /** The outbound-fetch context fragment native handlers spread into the dispatch context. */
53
+ export function networkFetchContext(
54
+ deps: { readonly networkBindingFactory?: GatewayHandlerDeps["networkBindingFactory"] },
55
+ poolId: string | undefined,
56
+ tenantId: string | null,
57
+ ): { outbound_fetch?: ValidatedOutboundFetch } {
58
+ if (!deps.networkBindingFactory) return {};
59
+ return { outbound_fetch: deps.networkBindingFactory.fetch(poolId, tenantId ?? undefined) };
60
+ }
61
+
62
+ /** Serializes a JSON payload with the shared native-response header set. */
63
+ export function jsonResponse(payload: unknown, status: number, requestId: string): Response {
64
+ return new Response(JSON.stringify(payload), {
65
+ status,
66
+ headers: {
67
+ "content-type": "application/json",
68
+ "cache-control": "no-store",
69
+ "x-request-id": requestId,
70
+ },
71
+ });
72
+ }
73
+
74
+ /**
75
+ * Strips a thinking suffix (`model(high)`) from the model name and rebuilds
76
+ * the body with the bare model when it changed. Returns the bare model and
77
+ * the body to forward upstream.
78
+ */
79
+ export function stripThinkingSuffix(
80
+ model: string,
81
+ body: Record<string, unknown>,
82
+ ): { bareModel: string; body: Record<string, unknown> } {
83
+ const { model: bareModel } = parseThinkingSuffix(model);
84
+ return { bareModel, body: bareModel === model ? body : { ...body, model: bareModel } };
85
+ }
86
+
87
+ /**
88
+ * Validates the ingress body is a non-null object and returns it as a record.
89
+ * Throws the standard `invalid_request` with the caller's message (kept
90
+ * per-route so the wire text is unchanged).
91
+ */
92
+ export function requireRecordBody(
93
+ state: { readonly authorization?: unknown; readonly ingressBody?: unknown },
94
+ message: string,
95
+ ): Record<string, unknown> {
96
+ const { authorization, ingressBody } = state;
97
+ if (!authorization || ingressBody === null || typeof ingressBody !== "object" || Array.isArray(ingressBody))
98
+ throw new GatewayError("invalid_request", 400, message);
99
+ return ingressBody as Record<string, unknown>;
100
+ }
101
+
102
+ /**
103
+ * Reads usage from an upstream response clone, repricing the reported value (or
104
+ * the caller's estimate) for the routed provider/model. Shared by the native
105
+ * handlers that proxy an upstream JSON body verbatim.
106
+ */
107
+ export async function usageFromUpstreamClone(
108
+ response: Response,
109
+ estimate: UsageRecord,
110
+ providerId: string,
111
+ modelId: string,
112
+ ): Promise<UsageRecord> {
113
+ let reportedUsage: ReturnType<typeof usageFromProvider>;
114
+ try {
115
+ const parsed: unknown = await response.clone().json();
116
+ reportedUsage = usageFromProvider(
117
+ typeof parsed === "object" && parsed !== null
118
+ ? (parsed as Record<string, unknown>)["usage"]
119
+ : undefined,
120
+ );
121
+ } catch {
122
+ reportedUsage = undefined;
123
+ }
124
+ return repriceUsage(reportedUsage ?? estimate, providerId, modelId);
125
+ }
126
+
127
+
@@ -41,7 +41,7 @@ import type { PreparedProxyRequest } from "../request/preparer";
41
41
 
42
42
  import type { GatewayHandlerDeps } from "./handler-deps";
43
43
 
44
- export type ProviderProxyHandlerDeps = GatewayHandlerDeps & {
44
+ type ProviderProxyHandlerDeps = GatewayHandlerDeps & {
45
45
  /** Live lookup of a provider's SSRF-validated upstream host. */
46
46
  readonly byokUpstreamHosts?: { readonly get: (providerId: string) => ByokUpstreamHost | undefined };
47
47
  };
@@ -135,6 +135,7 @@ export async function handleProviderProxyRequest(
135
135
  {
136
136
  providerId: candidateForPrompt.provider_id,
137
137
  modelId: candidateForPrompt.model_id,
138
+ requestedModel: prepared.plan?.requested_model ?? candidateForPrompt.model_id,
138
139
  },
139
140
  )?.text
140
141
  : undefined;
@@ -239,6 +240,7 @@ export async function handleProviderProxyRequest(
239
240
  ...(candidate.output_limit == null ? {} : { output_limit: candidate.output_limit }),
240
241
  capabilities: candidate.capability_profile,
241
242
  user_agent: candidate.user_agent,
243
+ ...(candidate.dialect === undefined ? {} : { dialect: candidate.dialect }),
242
244
  };
243
245
  const candidateRequest =
244
246
  candidate.model_id === dispatchRequest.model
@@ -1,11 +1,15 @@
1
1
  import { GatewayError } from "../../protocol/gateway-error";
2
- import { resolveCredentialForAccount } from "../../providers/operations/provider-credential-service";
3
2
  import { metrics } from "../../observability/metrics";
4
- import { parseThinkingSuffix } from "../../protocol/translation/thinking";
5
3
  import type { CodexCompactAdapter } from "../../providers/integrations/codex/codex";
6
4
  import { completeAttempt, estimatedUsage, nativeCompletionBase } from "./attempt-finalize";
7
- import { repriceUsage, usageFromProvider } from "../../providers/provider-usage";
8
5
  import { runAttemptLoop } from "./attempt-loop";
6
+ import {
7
+ networkFetchContext,
8
+ requireRecordBody,
9
+ resolveAttemptCredential,
10
+ stripThinkingSuffix,
11
+ usageFromUpstreamClone,
12
+ } from "./native-shared";
9
13
 
10
14
  /**
11
15
  * The native Codex compaction route (`POST /v1/responses/compact`) and the
@@ -13,7 +17,7 @@ import { runAttemptLoop } from "./attempt-loop";
13
17
  */
14
18
  import type { GatewayHandlerDeps } from "./handler-deps";
15
19
 
16
- export type ResponsesCompactHandlerDeps = GatewayHandlerDeps;
20
+ type ResponsesCompactHandlerDeps = GatewayHandlerDeps;
17
21
 
18
22
  /**
19
23
  * Handles `POST /v1/responses/compact`: the native Codex compaction proxy. The
@@ -25,10 +29,10 @@ export function createResponsesCompactHandler(deps: ResponsesCompactHandlerDeps)
25
29
  return async ({ request }: { request: Request }): Promise<Response> => {
26
30
  const state = deps.stateStore.require(request);
27
31
  const authorization = state.authorization;
28
- const body = state.ingressBody;
29
- if (!authorization || body === null || typeof body !== "object" || Array.isArray(body))
30
- throw new GatewayError("invalid_request", 400, "compact request body must be a JSON object (request {requestId}). Check the shape of what you sent — then resend 🪞");
31
- const compact = body as Record<string, unknown>;
32
+ const compact = requireRecordBody(
33
+ state,
34
+ "compact request body must be a JSON object (request {requestId}). Check the shape of what you sent — then resend 🪞",
35
+ );
32
36
  if (typeof compact.model !== "string" || compact.model.trim().length === 0)
33
37
  throw new GatewayError("invalid_request", 400, "model is required (request {requestId}). Check the shape of what you sent — then resend 🪞");
34
38
  // A thinking suffix (`model(high)`) is stripped here for the same reason the
@@ -39,9 +43,7 @@ export function createResponsesCompactHandler(deps: ResponsesCompactHandlerDeps)
39
43
  // reasoning effort to shape, and the body is passed upstream verbatim.
40
44
  // Stripping keeps a client that appends the suffix to every request working
41
45
  // here instead of failing with a misleading model-not-found.
42
- const { model: compactModel } = parseThinkingSuffix(compact.model);
43
- const compactBody: Record<string, unknown> =
44
- compactModel === compact.model ? compact : { ...compact, model: compactModel };
46
+ const { bareModel: compactModel, body: compactBody } = stripThinkingSuffix(compact.model, compact);
45
47
  if (!(typeof compact.input === "string" || Array.isArray(compact.input)))
46
48
  throw new GatewayError("invalid_request", 400, "input must be a string or array (request {requestId}). Check the shape of what you sent — then resend 🪞");
47
49
  if ("instructions" in compact && typeof compact.instructions !== "string")
@@ -57,7 +59,7 @@ export function createResponsesCompactHandler(deps: ResponsesCompactHandlerDeps)
57
59
  throw new GatewayError("admission_unavailable", 503, "Codex compact transport unavailable (request {requestId}). The bouncer stepped out — come back shortly 🚪");
58
60
  const prepared = await deps.proxyPreparer.prepareNativeCompact({
59
61
  model: compactModel,
60
- authorization,
62
+ authorization: authorization!,
61
63
  signal: state.abortController.signal,
62
64
  ...(state.clientUserAgent === undefined ? {} : { clientUserAgent: state.clientUserAgent }),
63
65
  });
@@ -88,14 +90,10 @@ export function createResponsesCompactHandler(deps: ResponsesCompactHandlerDeps)
88
90
  503,
89
91
  "Codex compact requires an OAuth account (request {requestId}). Wrong key, right door — check the credential 🔑",
90
92
  );
91
- const credential = await resolveCredentialForAccount(
92
- deps.db,
93
- "codex",
94
- candidate.provider_account_id,
95
- deps.oauthRefreshService && deps.resolveOAuthRefresher
96
- ? { refreshService: deps.oauthRefreshService, resolveRefresher: deps.resolveOAuthRefresher }
97
- : undefined,
98
- );
93
+ const credential = await resolveAttemptCredential(deps, {
94
+ ...candidate,
95
+ provider_id: "codex",
96
+ });
99
97
  if (credential.credential_kind !== "oauth")
100
98
  throw new GatewayError(
101
99
  "capability_unsupported",
@@ -110,36 +108,17 @@ export function createResponsesCompactHandler(deps: ResponsesCompactHandlerDeps)
110
108
  credential,
111
109
  deadline: state.deadlineMs,
112
110
  abort_signal: state.abortController.signal,
113
- ...(deps.networkBindingFactory
114
- ? {
115
- outbound_fetch: deps.networkBindingFactory.fetch(
116
- leases.proxySlot?.poolId,
117
- authorization.tenantId,
118
- ),
119
- }
120
- : {}),
111
+ ...networkFetchContext(deps, leases.proxySlot?.poolId, authorization!.tenantId),
121
112
  });
122
113
  // Prefer the usage the compaction endpoint actually reported. The
123
114
  // estimate is a fixed 1024+1024 reserve, and compaction is by
124
115
  // construction a large-input operation, so charging it unconditionally
125
- // let real spend run far past the counter. `usageFromProvider` returns
126
- // undefined when the body reported nothing, which keeps the estimate as
127
- // the fallback. `response.clone()` leaves the body readable for the
128
- // client.
129
- let reportedUsage: ReturnType<typeof usageFromProvider>;
130
- try {
131
- const parsed: unknown = await response.clone().json();
132
- reportedUsage = usageFromProvider(
133
- typeof parsed === "object" && parsed !== null
134
- ? (parsed as Record<string, unknown>)["usage"]
135
- : undefined,
136
- );
137
- } catch {
138
- reportedUsage = undefined;
139
- }
140
- const usage = repriceUsage(
141
- reportedUsage ??
142
- estimatedUsage(prepared.estimatedInputTokens, prepared.estimatedOutputTokens),
116
+ // let real spend run far past the counter. `usageFromUpstreamClone`
117
+ // falls back to the estimate when the body reported nothing, and
118
+ // `response.clone()` leaves the body readable for the client.
119
+ const usage = await usageFromUpstreamClone(
120
+ response,
121
+ estimatedUsage(prepared.estimatedInputTokens, prepared.estimatedOutputTokens),
143
122
  candidate.provider_id,
144
123
  candidate.model_id,
145
124
  );