cartethyia 2.7.3 → 2.7.5

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 (625) hide show
  1. package/.env.example +2 -0
  2. package/README.md +17 -5
  3. package/dist/dashboard/assets/{BannedPage-2TJD37Hb.js → BannedPage-Cj15GsMp.js} +1 -1
  4. package/dist/dashboard/assets/BannedPage-Cj15GsMp.js.gz +0 -0
  5. package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js +1 -0
  6. package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js.gz +0 -0
  7. package/dist/dashboard/assets/{CliToolDetailPage-xefJNRmJ.js → CliToolDetailPage-DZKmVuJN.js} +1 -1
  8. package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js.gz +0 -0
  9. package/dist/dashboard/assets/{CliToolsPage-Rj9iHQ3F.js → CliToolsPage-BIccZmeL.js} +1 -1
  10. package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js.gz +0 -0
  11. package/dist/dashboard/assets/CombosPage-DEEKkYD-.js +1 -0
  12. package/dist/dashboard/assets/CombosPage-DEEKkYD-.js.gz +0 -0
  13. package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js +1 -0
  14. package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js.gz +0 -0
  15. package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js +1 -0
  16. package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js.gz +0 -0
  17. package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js +656 -0
  18. package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js.gz +0 -0
  19. package/dist/dashboard/assets/{LoginPage-C4w7vea-.js → LoginPage-DkXb6dx-.js} +1 -1
  20. package/dist/dashboard/assets/LoginPage-DkXb6dx-.js.gz +0 -0
  21. package/dist/dashboard/assets/{ModelPicker-BtPcAfot.js → ModelPicker-DnQPSIA8.js} +1 -1
  22. package/dist/dashboard/assets/ModelPicker-DnQPSIA8.js.gz +0 -0
  23. package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js +1 -0
  24. package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js.gz +0 -0
  25. package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js +5 -0
  26. package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js.gz +0 -0
  27. package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js +1 -0
  28. package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js.gz +0 -0
  29. package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js +1 -0
  30. package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js.gz +0 -0
  31. package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js +9 -0
  32. package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js.gz +0 -0
  33. package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js +1 -0
  34. package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js.gz +0 -0
  35. package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js +1 -0
  36. package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js.gz +0 -0
  37. package/dist/dashboard/assets/{SetupPage-BQS0Rx_u.js → SetupPage-WTa0sktC.js} +1 -1
  38. package/dist/dashboard/assets/SetupPage-WTa0sktC.js.gz +0 -0
  39. package/dist/dashboard/assets/{StudioPage-TP7voAQ0.js → StudioPage-BfAb5-o_.js} +3 -3
  40. package/dist/dashboard/assets/StudioPage-BfAb5-o_.js.gz +0 -0
  41. package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js +56 -0
  42. package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js.gz +0 -0
  43. package/dist/dashboard/assets/api-keys-l29JROPv.js +1 -0
  44. package/dist/dashboard/assets/api-keys-l29JROPv.js.gz +0 -0
  45. package/dist/dashboard/assets/app--PJWq8_C.css +2 -0
  46. package/dist/dashboard/assets/app--PJWq8_C.css.gz +0 -0
  47. package/dist/dashboard/assets/{assert-C0YYuZJ4.js → assert-C0K-c4eo.js} +1 -1
  48. package/dist/dashboard/assets/assert-C0K-c4eo.js.gz +0 -0
  49. package/dist/dashboard/assets/{badge-B_DMpMJN.js → badge-DfbhQU4k.js} +1 -1
  50. package/dist/dashboard/assets/badge-DfbhQU4k.js.gz +0 -0
  51. package/dist/dashboard/assets/{banner-DMrMPmth.js → banner-BllT4LcF.js} +1 -1
  52. package/dist/dashboard/assets/banner-BllT4LcF.js.gz +0 -0
  53. package/dist/dashboard/assets/brand/login_register.jpg +0 -0
  54. package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
  55. package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
  56. package/dist/dashboard/assets/brand/og_image.jpg +0 -0
  57. package/dist/dashboard/assets/{card-Be5Uzamx.js → card-CtkX66Wd.js} +1 -1
  58. package/dist/dashboard/assets/card-CtkX66Wd.js.gz +0 -0
  59. package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js +1 -0
  60. package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js.gz +0 -0
  61. package/dist/dashboard/assets/{cli-tools-muFRphDu.js → cli-tools-CQXVMn4P.js} +1 -1
  62. package/dist/dashboard/assets/cli-tools-CQXVMn4P.js.gz +0 -0
  63. package/dist/dashboard/assets/{clipboard-button-DOfrVmsi.js → clipboard-button-BQqBQd2n.js} +1 -1
  64. package/dist/dashboard/assets/clipboard-button-BQqBQd2n.js.gz +0 -0
  65. package/dist/dashboard/assets/{dialog-C9omdjQt.js → dialog-BEK7KP0H.js} +1 -1
  66. package/dist/dashboard/assets/dialog-BEK7KP0H.js.gz +0 -0
  67. package/dist/dashboard/assets/{drawer-Bm8WKaGY.js → drawer-BrTEZ8zu.js} +1 -1
  68. package/dist/dashboard/assets/drawer-BrTEZ8zu.js.gz +0 -0
  69. package/dist/dashboard/assets/entry-CHWxbS56.js +1 -0
  70. package/dist/dashboard/assets/entry-CHWxbS56.js.gz +0 -0
  71. package/dist/dashboard/assets/{entry-q5HwZ8nN.js → entry-EBHJ3DrE.js} +1 -1
  72. package/dist/dashboard/assets/entry-EBHJ3DrE.js.gz +0 -0
  73. package/dist/dashboard/assets/entry-LCS85hiq.js +4 -0
  74. package/dist/dashboard/assets/entry-LCS85hiq.js.gz +0 -0
  75. package/dist/dashboard/assets/{formatters-BTScrQpe.js → formatters-BAACA727.js} +1 -1
  76. package/dist/dashboard/assets/formatters-BAACA727.js.gz +0 -0
  77. package/dist/dashboard/assets/{github-badge-Orjuzemr.js → github-badge-YSCE4UC2.js} +1 -1
  78. package/dist/dashboard/assets/github-badge-YSCE4UC2.js.gz +0 -0
  79. package/dist/dashboard/assets/{index-cPrX5vn6.js → index-FQlj0HUT.js} +2 -2
  80. package/dist/dashboard/assets/index-FQlj0HUT.js.gz +0 -0
  81. package/dist/dashboard/assets/{inline-Dvux8w-0.js → inline-C4gU5iOL.js} +1 -1
  82. package/dist/dashboard/assets/inline-C4gU5iOL.js.gz +0 -0
  83. package/dist/dashboard/assets/{input-CU1Rgf3p.js → input-BHHX_Tmm.js} +1 -1
  84. package/dist/dashboard/assets/input-BHHX_Tmm.js.gz +0 -0
  85. package/dist/dashboard/assets/{layout-C_7A6H55.js → layout-CajrYUnD.js} +1 -1
  86. package/dist/dashboard/assets/{layout-C_7A6H55.js.gz → layout-CajrYUnD.js.gz} +0 -0
  87. package/dist/dashboard/assets/{list-row-CzFvogbj.js → list-row-Bx2I7XUe.js} +1 -1
  88. package/dist/dashboard/assets/list-row-Bx2I7XUe.js.gz +0 -0
  89. package/dist/dashboard/assets/live-DNNqRbj2.js +1 -0
  90. package/dist/dashboard/assets/live-DNNqRbj2.js.gz +0 -0
  91. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js +1 -0
  92. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
  93. package/dist/dashboard/assets/{mutation-JJGQm8TD.js → mutation-Rr0TIvcj.js} +1 -1
  94. package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
  95. package/dist/dashboard/assets/network-C5UBACrJ.js +1 -0
  96. package/dist/dashboard/assets/network-C5UBACrJ.js.gz +0 -0
  97. package/dist/dashboard/assets/numbers-CUWAZK4J.js +2 -0
  98. package/dist/dashboard/assets/numbers-CUWAZK4J.js.gz +0 -0
  99. package/dist/dashboard/assets/pagination-Xsi9Qx04.js +1 -0
  100. package/dist/dashboard/assets/pagination-Xsi9Qx04.js.gz +0 -0
  101. package/dist/dashboard/assets/{query-keys-CLaNz8Xz.js → query-keys-BloKtaXJ.js} +1 -1
  102. package/dist/dashboard/assets/query-keys-BloKtaXJ.js.gz +0 -0
  103. package/dist/dashboard/assets/quota-DvQbD_sr.js +1 -0
  104. package/dist/dashboard/assets/quota-DvQbD_sr.js.gz +0 -0
  105. package/dist/dashboard/assets/{routing-BEH32QOC.js → routing-in0f_IfK.js} +1 -1
  106. package/dist/dashboard/assets/routing-in0f_IfK.js.gz +0 -0
  107. package/dist/dashboard/assets/{select-B3iTetHN.js → select-BLULNqTp.js} +2 -2
  108. package/dist/dashboard/assets/select-BLULNqTp.js.gz +0 -0
  109. package/dist/dashboard/assets/settings-CcGczj6I.js +1 -0
  110. package/dist/dashboard/assets/settings-CcGczj6I.js.gz +0 -0
  111. package/dist/dashboard/assets/shield-BMQNxORh.js +1 -0
  112. package/dist/dashboard/assets/shield-BMQNxORh.js.gz +0 -0
  113. package/dist/dashboard/assets/{stack-KrOrm7JZ.js → stack-D2lIR0tg.js} +1 -1
  114. package/dist/dashboard/assets/stack-D2lIR0tg.js.gz +0 -0
  115. package/dist/dashboard/assets/{state-DLS7IDMy.js → state-DTARjfiW.js} +1 -1
  116. package/dist/dashboard/assets/state-DTARjfiW.js.gz +0 -0
  117. package/dist/dashboard/assets/{switch-42ItvbAs.js → switch-c0oPSnqW.js} +1 -1
  118. package/dist/dashboard/assets/switch-c0oPSnqW.js.gz +0 -0
  119. package/dist/dashboard/assets/{useMutation-Dmnfh1Ue.js → useMutation-m8WGNPrz.js} +1 -1
  120. package/dist/dashboard/assets/useMutation-m8WGNPrz.js.gz +0 -0
  121. package/dist/dashboard/console-backgrounds.jpg +0 -0
  122. package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
  123. package/dist/dashboard/index.html +2 -2
  124. package/dist/dashboard/index.html.gz +0 -0
  125. package/dist/dashboard/precompressed-assets.json +59 -57
  126. package/migrations/full-postgres/0027_qoder_identity_genuine_seed.sql +9 -10
  127. package/migrations/full-postgres/0032_add_provider_pool_accounts.sql +53 -0
  128. package/migrations/full-postgres/0033_add_api_key_ban.sql +9 -0
  129. package/migrations/full-postgres/0034_add_api_key_ban_until.sql +7 -0
  130. package/migrations/full-postgres/0035_align_pool_account_status_enum.sql +21 -0
  131. package/migrations/full-postgres/0036_system_prompt_presets_refs.sql +10 -0
  132. package/migrations/full-postgres/0037_share_concurrency_scope.sql +3 -0
  133. package/migrations/full-postgres/0038_add_accounts_quota_snapshot.sql +7 -0
  134. package/migrations/full-postgres/0039_add_telemetry_provider_model_indexes.sql +13 -0
  135. package/migrations/full-postgres/0040_add_models_dialect.sql +3 -0
  136. package/migrations/full-postgres/0041_add_daily_checkin_ledger_account_fk.sql +7 -0
  137. package/migrations/lite-sqlite3/0027_qoder_identity_genuine_seed.sql +9 -10
  138. package/migrations/lite-sqlite3/0032_add_provider_pool_accounts.sql +44 -0
  139. package/migrations/lite-sqlite3/0033_add_api_key_ban.sql +8 -0
  140. package/migrations/lite-sqlite3/0034_add_api_key_ban_until.sql +5 -0
  141. package/migrations/lite-sqlite3/0035_align_pool_account_status_enum.sql +6 -0
  142. package/migrations/lite-sqlite3/0036_system_prompt_presets_refs.sql +14 -0
  143. package/migrations/lite-sqlite3/0037_share_concurrency_scope.sql +3 -0
  144. package/migrations/lite-sqlite3/0038_add_accounts_quota_snapshot.sql +4 -0
  145. package/migrations/lite-sqlite3/0039_add_telemetry_provider_model_indexes.sql +6 -0
  146. package/migrations/lite-sqlite3/0040_add_models_dialect.sql +3 -0
  147. package/migrations/lite-sqlite3/0041_add_daily_checkin_ledger_account_fk.sql +26 -0
  148. package/package.json +4 -5
  149. package/scripts/cli/update.ts +15 -41
  150. package/src/boot/app.ts +67 -10
  151. package/src/boot/dependencies.ts +786 -732
  152. package/src/boot/lifecycle.ts +11 -12
  153. package/src/config/cooldowns.ts +34 -55
  154. package/src/config/network.ts +8 -4
  155. package/src/config/package-paths.ts +1 -1
  156. package/src/config/providers.ts +13 -14
  157. package/src/config/resources.ts +0 -5
  158. package/src/config/runtime.ts +2 -5
  159. package/src/config/spec.ts +26 -6
  160. package/src/console/api-keys/ban-constants.ts +38 -0
  161. package/src/console/api-keys/contracts.ts +92 -31
  162. package/src/console/api-keys/routes.ts +131 -6
  163. package/src/console/audit/routes.ts +3 -3
  164. package/src/console/auth/access.ts +4 -4
  165. package/src/console/auth/account-reset.ts +5 -5
  166. package/src/console/auth/audit-recorder.ts +1 -1
  167. package/src/console/auth/lockout.ts +7 -3
  168. package/src/console/auth/row-coercion.ts +57 -12
  169. package/src/console/auth/session-resolver.ts +1 -1
  170. package/src/console/auth/session-service.ts +8 -2
  171. package/src/console/auth/session.ts +40 -34
  172. package/src/console/auth/setup-service.ts +4 -3
  173. package/src/console/auth/user-store.ts +1 -1
  174. package/src/console/backup/9router-map.ts +0 -3
  175. package/src/console/backup/contracts.ts +11 -9
  176. package/src/console/backup/export-endpoint.ts +11 -12
  177. package/src/console/backup/nine-router.ts +1 -7
  178. package/src/console/backup/routes.ts +32 -36
  179. package/src/console/backup/service.ts +468 -468
  180. package/src/console/backup/store.ts +47 -29
  181. package/src/console/backup/validate.ts +11 -1
  182. package/src/console/cli-tools/contracts.ts +7 -7
  183. package/src/console/cli-tools/injectors/cline.ts +110 -110
  184. package/src/console/cli-tools/injectors/codex.ts +181 -181
  185. package/src/console/cli-tools/injectors/copilot.ts +108 -108
  186. package/src/console/cli-tools/injectors/driver.ts +247 -247
  187. package/src/console/cli-tools/injectors/jcode.ts +115 -115
  188. package/src/console/cli-tools/injectors/openclaw.ts +161 -161
  189. package/src/console/cli-tools/injectors/opencode.ts +153 -153
  190. package/src/console/cli-tools/routes.ts +4 -4
  191. package/src/console/cli-tools/service.ts +1 -1
  192. package/src/console/cli-tools/store.ts +2 -2
  193. package/src/console/console-router-guards.ts +2 -2
  194. package/src/console/console-router.ts +36 -4
  195. package/src/console/dashboard-assets.ts +9 -9
  196. package/src/console/data-management/routes.ts +3 -3
  197. package/src/console/domain-registration.ts +18 -4
  198. package/src/console/inferhub/client.ts +0 -7
  199. package/src/console/inferhub/key-store.ts +1 -1
  200. package/src/console/inferhub/normalize.ts +2 -2
  201. package/src/console/inferhub/routes.ts +1 -1
  202. package/src/console/inferhub/types.ts +1 -1
  203. package/src/console/observability/contracts.ts +5 -1
  204. package/src/console/observability/live.ts +34 -21
  205. package/src/console/observability/logs.ts +4 -4
  206. package/src/console/observability/performance.ts +3 -3
  207. package/src/console/observability/routes.ts +3 -3
  208. package/src/console/observability/sse.ts +62 -8
  209. package/src/console/observability/store.ts +36 -20
  210. package/src/console/providers/catalog/catalog-projections.ts +1 -1
  211. package/src/console/providers/catalog/contracts.ts +82 -22
  212. package/src/console/providers/catalog/model-operations.ts +1 -1
  213. package/src/console/providers/catalog/provider-operations.ts +110 -17
  214. package/src/console/providers/catalog/public-model-store.ts +0 -0
  215. package/src/console/providers/catalog/routes.ts +30 -6
  216. package/src/console/providers/catalog/store-helpers.ts +349 -326
  217. package/src/console/providers/catalog/store.ts +1662 -1292
  218. package/src/console/providers/detail/routes.ts +4 -4
  219. package/src/console/providers/oauth/callback-listener.ts +100 -18
  220. package/src/console/providers/oauth/routes.ts +95 -16
  221. package/src/console/quota/account-quota-global.ts +41 -3
  222. package/src/console/quota/account-quota-shared.ts +10 -3
  223. package/src/console/quota/account-quota-tenant.ts +285 -67
  224. package/src/console/quota/account-test-egress.ts +4 -4
  225. package/src/console/quota/daily-growth-job.ts +308 -0
  226. package/src/console/quota/native-identity.ts +2 -2
  227. package/src/console/quota/quota-view.ts +1 -1
  228. package/src/console/remote-support/routes.ts +1 -1
  229. package/src/console/remote-support/service.ts +2 -2
  230. package/src/console/routing/model/contracts.ts +1 -1
  231. package/src/console/routing/model/routes.ts +3 -3
  232. package/src/console/routing/pools/contracts.ts +13 -10
  233. package/src/console/routing/pools/import-parse.ts +3 -3
  234. package/src/console/routing/pools/operations.ts +14 -7
  235. package/src/console/routing/pools/probe-verdict.ts +8 -3
  236. package/src/console/routing/pools/relay-deploy.ts +12 -12
  237. package/src/console/routing/pools/routes.ts +3 -3
  238. package/src/console/routing/pools/store.ts +21 -1
  239. package/src/console/settings/contracts.ts +29 -2
  240. package/src/console/settings/routes.ts +3 -3
  241. package/src/console/settings/store.ts +20 -0
  242. package/src/console/share/share-router.ts +112 -44
  243. package/src/console/share/share-stats.ts +109 -39
  244. package/src/console/share/share-usage.ts +1 -1
  245. package/src/console/shared/errors.ts +2 -2
  246. package/src/console/shared/object.ts +18 -0
  247. package/src/console/shared/read-through-cache.ts +42 -6
  248. package/src/console/shared/transaction.ts +14 -0
  249. package/src/console/system-prompt/contracts.ts +89 -12
  250. package/src/console/system-prompt/routes.ts +98 -45
  251. package/src/console/updates/apply-update.ts +244 -0
  252. package/src/console/updates/routes.ts +256 -98
  253. package/src/main.ts +11 -5
  254. package/src/network/http2-bidi.ts +1 -1
  255. package/src/network/ip-utils.ts +298 -0
  256. package/src/network/outbound-fetch.ts +76 -5
  257. package/src/network/pool/agent.ts +7 -6
  258. package/src/network/pool/byte-accounting.ts +1 -1
  259. package/src/network/pool/resolver.ts +55 -51
  260. package/src/network/pool/selector.ts +150 -136
  261. package/src/network/pool-cooldown-store.ts +71 -16
  262. package/src/network/pool-health-machine.ts +22 -2
  263. package/src/network/rate-limit-verdict.ts +68 -0
  264. package/src/network/retry.ts +1 -1
  265. package/src/network/ssrf.ts +24 -75
  266. package/src/observability/event-log.ts +2 -2
  267. package/src/observability/event-query.ts +286 -214
  268. package/src/observability/log-sanitize.ts +1 -1
  269. package/src/observability/metrics.ts +4 -4
  270. package/src/observability/payload-capture.ts +6 -5
  271. package/src/observability/payload-store.ts +1 -1
  272. package/src/observability/runtime-metrics.ts +1 -1
  273. package/src/observability/telemetry-buffer.ts +12 -3
  274. package/src/observability/telemetry-reconciliation.ts +3 -3
  275. package/src/observability/telemetry-status.ts +1 -1
  276. package/src/observability/token-speed.ts +1 -1
  277. package/src/persistence/connection-url.ts +1 -1
  278. package/src/persistence/db-handle.ts +1 -1
  279. package/src/persistence/db-pg.ts +2 -2
  280. package/src/persistence/db-sqlite.ts +10 -5
  281. package/src/persistence/dialect.ts +5 -1
  282. package/src/persistence/readiness.ts +19 -7
  283. package/src/persistence/redis.ts +1 -1
  284. package/src/persistence/schema.ts +177 -45
  285. package/src/persistence/shared-cache.ts +2 -2
  286. package/src/persistence/sort-order.ts +1 -1
  287. package/src/persistence/stores/api-key-store.ts +109 -4
  288. package/src/persistence/stores/share-store.ts +20 -8
  289. package/src/persistence/stores/telemetry-store.ts +1 -1
  290. package/src/persistence/system-prompt-store.ts +63 -18
  291. package/src/persistence/tenant-preferences.ts +1 -1
  292. package/src/persistence/ttl-cache.ts +3 -3
  293. package/src/persistence/usage-flush.ts +7 -2
  294. package/src/protocol/canonical-model.ts +1 -1
  295. package/src/protocol/codecs.ts +12 -16
  296. package/src/protocol/dsml.ts +3 -49
  297. package/src/protocol/gateway-error.ts +6 -3
  298. package/src/protocol/image-clamp.ts +2 -2
  299. package/src/protocol/image-generation.ts +2 -2
  300. package/src/protocol/reasoning-text.ts +1 -1
  301. package/src/protocol/reasoning.ts +7 -52
  302. package/src/protocol/registry.ts +50 -15
  303. package/src/protocol/request/chat.ts +46 -19
  304. package/src/protocol/request/codex.ts +1 -1
  305. package/src/protocol/request/messages.ts +46 -14
  306. package/src/protocol/request/responses.ts +19 -10
  307. package/src/protocol/response/chat.ts +27 -12
  308. package/src/protocol/response/codex.ts +2 -2
  309. package/src/protocol/response/gemini.ts +1 -1
  310. package/src/protocol/response/messages.ts +6 -6
  311. package/src/protocol/response/omp-tool-call-markup.ts +1 -1
  312. package/src/protocol/response/responses.ts +3 -3
  313. package/src/protocol/sse-decoder.ts +64 -4
  314. package/src/protocol/stop-reason.ts +2 -2
  315. package/src/protocol/think-split.ts +347 -0
  316. package/src/protocol/translation/attachment-integrity.ts +3 -3
  317. package/src/protocol/translation/cache-controls.ts +8 -20
  318. package/src/protocol/translation/capabilities.ts +8 -8
  319. package/src/protocol/translation/dialect/deepseek.ts +10 -0
  320. package/src/protocol/translation/dialect/inband.ts +39 -0
  321. package/src/protocol/translation/dialect/model-thinking.ts +89 -0
  322. package/src/protocol/translation/dialect/qwen.ts +114 -0
  323. package/src/protocol/translation/dialect/resolver.ts +83 -0
  324. package/src/protocol/translation/dialect/rules.ts +93 -0
  325. package/src/protocol/translation/dialect/thinking-ladder.ts +245 -0
  326. package/src/protocol/translation/dialect/types.ts +58 -0
  327. package/src/protocol/translation/quirks.ts +18 -4
  328. package/src/protocol/translation/thinking.ts +12 -252
  329. package/src/protocol/translation/tool-id.ts +3 -3
  330. package/src/protocol/translation/tool-repair.ts +1 -1
  331. package/src/protocol/upstream/post-json.ts +6 -1
  332. package/src/protocol/upstream/send-claude-messages.ts +3 -3
  333. package/src/providers/adapters/base-adapter.ts +1 -1
  334. package/src/providers/adapters/compatible-adapter.ts +44 -5
  335. package/src/providers/authentication/jwt-validator.ts +0 -5
  336. package/src/providers/authentication/oauth-client.ts +254 -253
  337. package/src/providers/authentication/oauth-flow-store.ts +42 -7
  338. package/src/providers/authentication/oauth-refresh-service.ts +2 -2
  339. package/src/providers/bundled-discovery-providers.ts +6 -30
  340. package/src/providers/configured-openai-providers.ts +82 -97
  341. package/src/providers/configured-provider.ts +1 -1
  342. package/src/providers/default-registry.ts +23 -35
  343. package/src/providers/discovery/catalog/catalog-format.ts +13 -2
  344. package/src/providers/discovery/discovery-types.ts +0 -1
  345. package/src/providers/discovery/openai-model-discovery.ts +2 -2
  346. package/src/providers/discovery/probe-phases.ts +50 -15
  347. package/src/providers/discovery/probing-service.ts +18 -20
  348. package/src/providers/integrations/agentrouter/agentrouter.ts +14 -5
  349. package/src/providers/integrations/alibabacp/alibabacp.ts +2 -2
  350. package/src/providers/integrations/anthropic.ts +4 -11
  351. package/src/providers/integrations/antigravity/antigravity-oauth.ts +0 -1
  352. package/src/providers/integrations/antigravity/antigravity-protocol.ts +0 -14
  353. package/src/providers/integrations/antigravity/antigravity.ts +15 -26
  354. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +2 -2
  355. package/src/providers/integrations/buddy/buddy-chat-shared.ts +20 -4
  356. package/src/providers/integrations/buddy/buddy-cli-headers.ts +19 -52
  357. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +37 -6
  358. package/src/providers/integrations/buddy/buddy-identity.ts +108 -1
  359. package/src/providers/integrations/buddy/buddy-media.ts +16 -16
  360. package/src/providers/integrations/buddy/buddy-oauth-shared.ts +1 -1
  361. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +1 -2
  362. package/src/providers/integrations/buddy/codebuddy/provider.ts +5 -16
  363. package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
  364. package/src/providers/integrations/buddy/workbuddy/provider.ts +6 -10
  365. package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
  366. package/src/providers/integrations/claude/claude-betas.ts +1 -1
  367. package/src/providers/integrations/claude/claude-cch.ts +148 -148
  368. package/src/providers/integrations/claude/claude-credentials.ts +1 -1
  369. package/src/providers/integrations/claude/claude-slow-lane-retry.ts +1 -1
  370. package/src/providers/integrations/claude/claude.ts +3 -10
  371. package/src/providers/integrations/claude/fallback-credit.ts +3 -7
  372. package/src/providers/integrations/cline/cline.ts +2 -2
  373. package/src/providers/integrations/cloudflareai/cloudflareai.ts +4 -7
  374. package/src/providers/integrations/codex/codex-device-code.ts +101 -101
  375. package/src/providers/integrations/codex/codex-errors.ts +1 -1
  376. package/src/providers/integrations/codex/codex-oauth.ts +0 -1
  377. package/src/providers/integrations/codex/codex.ts +14 -15
  378. package/src/providers/integrations/commandcode/commandcode.ts +76 -116
  379. package/src/providers/integrations/commandcode/quota.ts +132 -48
  380. package/src/providers/integrations/cursor/cursor-http2.ts +11 -14
  381. package/src/providers/integrations/cursor/cursor-quota.ts +2 -6
  382. package/src/providers/integrations/cursor/cursor-request.ts +0 -87
  383. package/src/providers/integrations/cursor/cursor-shared.ts +0 -4
  384. package/src/providers/integrations/cursor/cursor.ts +53 -20
  385. package/src/providers/integrations/cursor/protocol/framing.ts +0 -56
  386. package/src/providers/integrations/cursor/protocol/messages.ts +0 -96
  387. package/src/providers/integrations/factory/factory.ts +13 -13
  388. package/src/providers/integrations/gemini.ts +11 -11
  389. package/src/providers/integrations/github/github-discovery.ts +55 -10
  390. package/src/providers/integrations/github/github-oauth.ts +6 -5
  391. package/src/providers/integrations/github/github.ts +47 -20
  392. package/src/providers/integrations/grok/grok-oauth.ts +261 -261
  393. package/src/providers/integrations/grok/grok-quota-frame.ts +1 -1
  394. package/src/providers/integrations/grok/grok-quota.ts +16 -7
  395. package/src/providers/integrations/grok/grok-ratelimit-store.ts +0 -5
  396. package/src/providers/integrations/grok/grok-ratelimit.ts +3 -0
  397. package/src/providers/integrations/grok/grok.ts +88 -42
  398. package/src/providers/integrations/inferhub.ts +4 -11
  399. package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
  400. package/src/providers/integrations/kimi/kimi.ts +4 -11
  401. package/src/providers/integrations/kiro/kiro-cache.ts +31 -0
  402. package/src/providers/integrations/kiro/kiro-catalog.ts +31 -5
  403. package/src/providers/integrations/kiro/kiro-discovery.ts +47 -43
  404. package/src/providers/integrations/kiro/kiro-machine-id.ts +6 -3
  405. package/src/providers/integrations/kiro/kiro-oauth.ts +523 -255
  406. package/src/providers/integrations/kiro/kiro-quota.ts +35 -47
  407. package/src/providers/integrations/kiro/kiro-request.ts +8 -11
  408. package/src/providers/integrations/kiro/kiro-stream.ts +13 -0
  409. package/src/providers/integrations/kiro/kiro.ts +91 -78
  410. package/src/providers/integrations/muse/muse-oauth.ts +0 -1
  411. package/src/providers/integrations/muse/muse-quota.ts +0 -4
  412. package/src/providers/integrations/muse/muse.ts +6 -9
  413. package/src/providers/integrations/opencode/fingerprint.ts +7 -5
  414. package/src/providers/integrations/opencode/provider.ts +17 -24
  415. package/src/providers/integrations/perplexity.ts +2 -2
  416. package/src/providers/integrations/qoder/qoder-catalog.ts +132 -17
  417. package/src/providers/integrations/qoder/qoder-checkin.ts +26 -8
  418. package/src/providers/integrations/qoder/qoder-native-identity-store.ts +9 -8
  419. package/src/providers/integrations/qoder/qoder-oauth.ts +165 -25
  420. package/src/providers/integrations/qoder/qoder-quota.ts +48 -6
  421. package/src/providers/integrations/qoder/qoder-realm.ts +22 -28
  422. package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
  423. package/src/providers/integrations/qoder/qoder-umid.ts +33 -38
  424. package/src/providers/integrations/qoder/qoder-virtual.ts +14 -2
  425. package/src/providers/integrations/qoder/qoder.ts +521 -63
  426. package/src/providers/integrations/shared/connect.ts +0 -54
  427. package/src/providers/integrations/shared/hash.ts +15 -0
  428. package/src/providers/integrations/shared/session-cache.ts +136 -0
  429. package/src/providers/integrations/tokenharbor.ts +2 -2
  430. package/src/providers/integrations/trae/trae-cache.ts +7 -87
  431. package/src/providers/integrations/trae/trae-catalog.ts +1 -6
  432. package/src/providers/integrations/trae/trae-protocol.ts +17 -8
  433. package/src/providers/integrations/trae/trae-realm.ts +0 -7
  434. package/src/providers/integrations/trae/trae.ts +59 -19
  435. package/src/providers/integrations/xai/xai-oauth.ts +5 -5
  436. package/src/providers/integrations/zcode/oauth.ts +1 -1
  437. package/src/providers/integrations/zcode-start-plan/gifts.ts +265 -0
  438. package/src/providers/integrations/zcode-start-plan/oauth.ts +154 -0
  439. package/src/providers/integrations/zcode-start-plan/provider.ts +131 -0
  440. package/src/providers/integrations/zcode-start-plan/quota.ts +152 -0
  441. package/src/providers/integrations/zcode-start-plan/zcode_prompt.json +10 -0
  442. package/src/providers/operations/account-error-classification.ts +131 -116
  443. package/src/providers/operations/account-health-service.ts +31 -39
  444. package/src/providers/operations/account-reset-service.ts +58 -1
  445. package/src/providers/operations/cli-platform.ts +31 -5
  446. package/src/providers/operations/client-version-resolver.ts +4 -6
  447. package/src/providers/operations/client-versions.ts +10 -7
  448. package/src/providers/operations/provider-catalog-seeder.ts +2 -0
  449. package/src/providers/operations/provider-catalog-service.ts +22 -6
  450. package/src/providers/operations/provider-credential-service.ts +35 -6
  451. package/src/providers/operations/upstream-deadline.ts +3 -3
  452. package/src/providers/provider-contracts.ts +65 -10
  453. package/src/providers/provider-credential-resolver.ts +1 -1
  454. package/src/providers/provider-metadata.ts +17 -2
  455. package/src/providers/provider-model-definition.ts +22 -1
  456. package/src/providers/provider-probe.ts +4 -2
  457. package/src/providers/provider-registry.ts +4 -4
  458. package/src/providers/provider-usage.ts +81 -16
  459. package/src/providers/quota/cache.ts +29 -0
  460. package/src/providers/quota/quota-contracts.ts +28 -10
  461. package/src/providers/quota/refresh.ts +29 -6
  462. package/src/providers/quota/snapshot.ts +94 -0
  463. package/src/providers/search/search-provider.ts +6 -8
  464. package/src/providers/search/search-providers.ts +2 -1
  465. package/src/security/abuse.ts +8 -8
  466. package/src/security/admission/reasons.ts +17 -17
  467. package/src/security/admission/service.ts +2 -2
  468. package/src/security/admission/ttl.ts +61 -61
  469. package/src/security/api-key-auth.ts +20 -8
  470. package/src/security/client-router-fingerprint.ts +4 -4
  471. package/src/security/credential-codec.ts +3 -3
  472. package/src/security/csrf.ts +2 -2
  473. package/src/security/ip-boundary.ts +16 -189
  474. package/src/security/model-access-rule.ts +1 -1
  475. package/src/security/outbound-headers.ts +18 -0
  476. package/src/shared/coerce.ts +19 -0
  477. package/src/shared/invoke-guard.ts +47 -0
  478. package/src/transport/contracts.ts +13 -4
  479. package/src/transport/dispatch/attempt-finalize.ts +5 -4
  480. package/src/transport/dispatch/attempt-loop.ts +10 -7
  481. package/src/transport/dispatch/capture-policy.ts +34 -37
  482. package/src/transport/dispatch/empty-stream.ts +28 -25
  483. package/src/transport/dispatch/generation.ts +33 -53
  484. package/src/transport/dispatch/leases.ts +8 -8
  485. package/src/transport/dispatch/native-shared.ts +127 -0
  486. package/src/transport/dispatch/proxy-request.ts +10 -8
  487. package/src/transport/dispatch/responses-compact.ts +35 -56
  488. package/src/transport/dispatch/retry-policy.ts +4 -67
  489. package/src/transport/dispatch/stream-bridge.ts +1 -1
  490. package/src/transport/dispatch/streaming/encoder.ts +18 -72
  491. package/src/transport/dispatch/streaming/finalize.ts +11 -10
  492. package/src/transport/dispatch/streaming/stream.ts +7 -3
  493. package/src/transport/dispatch/streaming/thinking-loop.ts +1 -1
  494. package/src/transport/dispatch/systemone.ts +27 -55
  495. package/src/transport/dispatch/token-count.ts +49 -46
  496. package/src/transport/dispatch/websearch.ts +24 -50
  497. package/src/transport/drain-endpoint.ts +3 -3
  498. package/src/transport/failure-policy.ts +19 -6
  499. package/src/transport/guards/auth-guard.ts +6 -11
  500. package/src/transport/guards/body-policy.ts +7 -7
  501. package/src/transport/guards/error-lifecycle.ts +18 -3
  502. package/src/transport/guards/guard-errors.ts +7 -3
  503. package/src/transport/guards/headroom-guard.ts +2 -2
  504. package/src/transport/guards/pipeline.ts +7 -7
  505. package/src/transport/guards/readiness-guard.ts +16 -2
  506. package/src/transport/guards/request-context.ts +15 -20
  507. package/src/transport/request/preparer.ts +306 -27
  508. package/src/transport/request/rtk/compress-request.ts +3 -3
  509. package/src/transport/request/rtk/rtk-constants.ts +2 -2
  510. package/src/transport/request/rtk/rtk-filters.ts +8 -8
  511. package/src/transport/request/state.ts +5 -20
  512. package/src/transport/request/system-prompt-injection.ts +74 -11
  513. package/src/transport/request/tenant-preferences.ts +22 -0
  514. package/src/transport/routing/candidate-index.ts +2 -2
  515. package/src/transport/routing/round-robin.ts +1 -1
  516. package/src/transport/routing/route-catalog.ts +113 -19
  517. package/src/transport/routing/route-model.ts +131 -38
  518. package/src/transport/routing/router.ts +154 -39
  519. package/src/transport/shutdown-notice.ts +21 -9
  520. package/src/transport/surface/adapters.ts +3 -3
  521. package/src/transport/surface/chat/encode.ts +7 -7
  522. package/src/transport/surface/chat/parse.ts +7 -7
  523. package/src/transport/surface/completion.ts +3 -3
  524. package/src/transport/surface/content-parts.ts +1 -1
  525. package/src/transport/surface/dialects.ts +2 -2
  526. package/src/transport/surface/messages/encode.ts +1 -1
  527. package/src/transport/surface/messages/parse.ts +3 -3
  528. package/src/transport/surface/responses/adapter.ts +1 -1
  529. package/src/transport/surface/responses/errors.ts +0 -9
  530. package/src/transport/vision-handoff-cache.ts +58 -0
  531. package/src/transport/vision-handoff.ts +376 -0
  532. package/src/workers/account-identity-sweep.ts +2 -2
  533. package/src/workers/buddy-growth-tasks.ts +392 -346
  534. package/src/workers/daily-checkin.ts +102 -49
  535. package/src/workers/maintenance-lease.ts +2 -35
  536. package/src/workers/oauth-refresh-sweep.ts +8 -1
  537. package/src/workers/quota-refresh-sweep.ts +120 -82
  538. package/src/workers/scheduler.ts +67 -4
  539. package/src/workers/sweep-runner.ts +24 -5
  540. package/src/workers/zcode-gift-claim.ts +108 -0
  541. package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js +0 -1
  542. package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js.gz +0 -0
  543. package/dist/dashboard/assets/BannedPage-2TJD37Hb.js.gz +0 -0
  544. package/dist/dashboard/assets/CliToolDetailPage-xefJNRmJ.js.gz +0 -0
  545. package/dist/dashboard/assets/CliToolsPage-Rj9iHQ3F.js.gz +0 -0
  546. package/dist/dashboard/assets/CombosPage-JEbYJFxw.js +0 -1
  547. package/dist/dashboard/assets/CombosPage-JEbYJFxw.js.gz +0 -0
  548. package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js +0 -1
  549. package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js.gz +0 -0
  550. package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js +0 -1
  551. package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js.gz +0 -0
  552. package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js +0 -1
  553. package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js.gz +0 -0
  554. package/dist/dashboard/assets/LoginPage-C4w7vea-.js.gz +0 -0
  555. package/dist/dashboard/assets/ModelPicker-BtPcAfot.js.gz +0 -0
  556. package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js +0 -1
  557. package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js.gz +0 -0
  558. package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js +0 -4
  559. package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js.gz +0 -0
  560. package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +0 -1
  561. package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
  562. package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js +0 -1
  563. package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js.gz +0 -0
  564. package/dist/dashboard/assets/ProxyPage-DdPc8slb.js +0 -9
  565. package/dist/dashboard/assets/ProxyPage-DdPc8slb.js.gz +0 -0
  566. package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js +0 -1
  567. package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js.gz +0 -0
  568. package/dist/dashboard/assets/SettingsPage-CDZjTt87.js +0 -1
  569. package/dist/dashboard/assets/SettingsPage-CDZjTt87.js.gz +0 -0
  570. package/dist/dashboard/assets/SetupPage-BQS0Rx_u.js.gz +0 -0
  571. package/dist/dashboard/assets/SortableList-BAL5npBC.js +0 -1
  572. package/dist/dashboard/assets/SortableList-BAL5npBC.js.gz +0 -0
  573. package/dist/dashboard/assets/StudioPage-TP7voAQ0.js.gz +0 -0
  574. package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js +0 -56
  575. package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js.gz +0 -0
  576. package/dist/dashboard/assets/api-keys-MbI7e7rt.js +0 -1
  577. package/dist/dashboard/assets/api-keys-MbI7e7rt.js.gz +0 -0
  578. package/dist/dashboard/assets/app-B2vyPoII.css +0 -2
  579. package/dist/dashboard/assets/app-B2vyPoII.css.gz +0 -0
  580. package/dist/dashboard/assets/assert-C0YYuZJ4.js.gz +0 -0
  581. package/dist/dashboard/assets/badge-B_DMpMJN.js.gz +0 -0
  582. package/dist/dashboard/assets/banner-DMrMPmth.js.gz +0 -0
  583. package/dist/dashboard/assets/card-Be5Uzamx.js.gz +0 -0
  584. package/dist/dashboard/assets/cli-tools-muFRphDu.js.gz +0 -0
  585. package/dist/dashboard/assets/clipboard-button-DOfrVmsi.js.gz +0 -0
  586. package/dist/dashboard/assets/dialog-C9omdjQt.js.gz +0 -0
  587. package/dist/dashboard/assets/drawer-Bm8WKaGY.js.gz +0 -0
  588. package/dist/dashboard/assets/entry-DS90gUks.js +0 -1
  589. package/dist/dashboard/assets/entry-DS90gUks.js.gz +0 -0
  590. package/dist/dashboard/assets/entry-j40LJotO.js +0 -4
  591. package/dist/dashboard/assets/entry-j40LJotO.js.gz +0 -0
  592. package/dist/dashboard/assets/entry-q5HwZ8nN.js.gz +0 -0
  593. package/dist/dashboard/assets/formatters-BTScrQpe.js.gz +0 -0
  594. package/dist/dashboard/assets/github-badge-Orjuzemr.js.gz +0 -0
  595. package/dist/dashboard/assets/index-cPrX5vn6.js.gz +0 -0
  596. package/dist/dashboard/assets/inline-Dvux8w-0.js.gz +0 -0
  597. package/dist/dashboard/assets/input-CU1Rgf3p.js.gz +0 -0
  598. package/dist/dashboard/assets/list-row-CzFvogbj.js.gz +0 -0
  599. package/dist/dashboard/assets/live-KaG15HLQ.js +0 -1
  600. package/dist/dashboard/assets/live-KaG15HLQ.js.gz +0 -0
  601. package/dist/dashboard/assets/minimize-2-D-MN8W8a.js +0 -1
  602. package/dist/dashboard/assets/minimize-2-D-MN8W8a.js.gz +0 -0
  603. package/dist/dashboard/assets/mutation-JJGQm8TD.js.gz +0 -0
  604. package/dist/dashboard/assets/numbers-BNDZnsUE.js +0 -2
  605. package/dist/dashboard/assets/numbers-BNDZnsUE.js.gz +0 -0
  606. package/dist/dashboard/assets/query-keys-CLaNz8Xz.js.gz +0 -0
  607. package/dist/dashboard/assets/quota-DZGyp0N5.js +0 -1
  608. package/dist/dashboard/assets/quota-DZGyp0N5.js.gz +0 -0
  609. package/dist/dashboard/assets/routing-BEH32QOC.js.gz +0 -0
  610. package/dist/dashboard/assets/select-B3iTetHN.js.gz +0 -0
  611. package/dist/dashboard/assets/settings-Dt3Eqbnq.js +0 -1
  612. package/dist/dashboard/assets/settings-Dt3Eqbnq.js.gz +0 -0
  613. package/dist/dashboard/assets/stack-KrOrm7JZ.js.gz +0 -0
  614. package/dist/dashboard/assets/state-DLS7IDMy.js.gz +0 -0
  615. package/dist/dashboard/assets/stream-retry-5QnVuSSo.js +0 -1
  616. package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
  617. package/dist/dashboard/assets/switch-42ItvbAs.js.gz +0 -0
  618. package/dist/dashboard/assets/useMutation-Dmnfh1Ue.js.gz +0 -0
  619. package/src/network/response-headers.ts +0 -39
  620. package/src/protocol/response/dsml.ts +0 -38
  621. package/src/providers/integrations/xiaomi-mimo/think-stream.ts +0 -229
  622. /package/dist/dashboard/assets/{app-wvwCi4Eh.js → app-Bgd3zANQ.js} +0 -0
  623. /package/dist/dashboard/assets/{app-wvwCi4Eh.js.gz → app-Bgd3zANQ.js.gz} +0 -0
  624. /package/dist/dashboard/assets/brand/{og_bansos.webp → og_bansos_initial.webp} +0 -0
  625. /package/dist/dashboard/assets/brand/{og_image.webp → og_image_initial.webp} +0 -0
@@ -0,0 +1,347 @@
1
+ /**
2
+ * Canonical inline-thinking split.
3
+ *
4
+ * Some upstream models write their reasoning into the *text* stream wrapped in
5
+ * `<think>…</think>` (or `<thinking>…</thinking>`) instead of a dedicated
6
+ * reasoning field. This module recovers that reasoning — statefully, so a tag
7
+ * split across two chunks is still recognized — and lives in the canonical
8
+ * protocol layer because it serves every wire: the chat decoder, provider
9
+ * decoders (CommandCode, Xiaomi MiMo Studio), and nothing else is per-provider
10
+ * about it.
11
+ *
12
+ * Two safety rules keep the classification honest (mirroring the DSML parser's
13
+ * fail-open behavior):
14
+ *
15
+ * - Only a *paired* span — an opening tag followed by its matching closing tag,
16
+ * outside a code span — is treated as reasoning. An opening tag that never
17
+ * closes at end-of-stream is almost certainly literal output (docs, a code
18
+ * sample, a model reciting the syntax), so it is released as plain text
19
+ * rather than swallowing the tail of the answer into the thinking pane.
20
+ * - A stray closing tag with no opener is likewise preserved verbatim.
21
+ *
22
+ * The splitter is stateful because a tag can straddle two SSE frames: the
23
+ * `<thi` of one chunk and the `nk>` of the next are one opening tag, and a
24
+ * stateless regex would emit the fragment as text. It therefore holds back a
25
+ * trailing run that could still become a tag until the next chunk settles it.
26
+ *
27
+ * Two further quirks are handled here rather than at each call site:
28
+ * - NUL bytes (`\u0000`) are injected by the MiMo Studio upstream between
29
+ * thought tokens; forwarded verbatim they break JSON encoders and terminal
30
+ * renderers.
31
+ * - Text before an opening tag is the answer (some models answer first and
32
+ * reason afterwards), so it stays content; only the enclosed span becomes
33
+ * reasoning.
34
+ */
35
+
36
+ /** Tags whose enclosed span is reasoning, longest-first so `</thinking>` is never split as `<t`. */
37
+ const OPEN_TAGS = ["<thinking>", "<think>"] as const;
38
+ const CLOSE_TAGS = ["</thinking>", "</think>"] as const;
39
+
40
+ /** An opening tag, with optional attributes between the name and the `>`. */
41
+ const THINK_OPEN_RE = /^<think(?:ing)?\b[^>]*>/;
42
+
43
+ /**
44
+ * Fail-open cap for an unclosed thinking span. Past it the span is treated as
45
+ * plain text instead of being buffered indefinitely — same rule as DSML's
46
+ * `maxBlockBytes` reference guard.
47
+ */
48
+ const MAX_THINK_BLOCK_BYTES = 64 * 1024;
49
+
50
+ /**
51
+ * Removes visible thinking tags, leaving any that sit inside a code span or
52
+ * fence alone — and leaving any tag that is not part of a complete pair alone
53
+ * too, since an unpaired tag is the subject of the answer rather than markup
54
+ * the upstream leaked.
55
+ *
56
+ * A tag inside backticks is the *subject* of the answer — documentation or a
57
+ * code sample showing the tag — not markup the upstream leaked, so stripping it
58
+ * would silently rewrite the answer. The same goes for an opening tag whose
59
+ * closing tag never arrived or a closing tag with no opener.
60
+ */
61
+ export function stripThinkingTags(value: string): string {
62
+ if (!value.includes("<")) return value;
63
+ let out = "";
64
+ let i = 0;
65
+ let ticks = 0;
66
+ let fenced = false;
67
+ while (i < value.length) {
68
+ const char = value[i];
69
+ if (char === "`") {
70
+ let run = 1;
71
+ while (value[i + run] === "`") run++;
72
+ if (ticks === 0) {
73
+ ticks = run;
74
+ fenced = run >= 3;
75
+ } else if (run === ticks) {
76
+ ticks = 0;
77
+ fenced = false;
78
+ }
79
+ out += value.slice(i, i + run);
80
+ i += run;
81
+ continue;
82
+ }
83
+ // A newline closes an inline span that never closed on its own line; a
84
+ // fence runs to a closing line instead.
85
+ if (char === "\n" && ticks > 0 && !fenced) ticks = 0;
86
+ if (char !== "<" || ticks > 0) {
87
+ out += char;
88
+ i += 1;
89
+ continue;
90
+ }
91
+ const opened = THINK_OPEN_RE.exec(value.slice(i))?.[0];
92
+ if (opened === undefined) {
93
+ out += char;
94
+ i += 1;
95
+ continue;
96
+ }
97
+ const innerStart = i + opened.length;
98
+ const closed = pairedClose(value, innerStart, ticks, fenced);
99
+ if (closed === undefined) {
100
+ // No matching close: the opening tag is literal output, keep it.
101
+ out += char;
102
+ i += 1;
103
+ continue;
104
+ }
105
+ // A complete paired span is reasoning markup: keep the inner text, drop
106
+ // the tag bytes.
107
+ out += value.slice(innerStart, closed.index);
108
+ i = closed.index + closed.length;
109
+ }
110
+ return out;
111
+ }
112
+
113
+ interface PairedClose {
114
+ readonly index: number;
115
+ readonly length: number;
116
+ }
117
+
118
+ /** The first closing tag at or after `from`, outside any code span/fence. */
119
+ function pairedClose(
120
+ value: string,
121
+ from: number,
122
+ ticksIn: number,
123
+ fencedIn: boolean,
124
+ ): PairedClose | undefined {
125
+ let ticks = ticksIn;
126
+ let fenced = fencedIn;
127
+ for (let i = from; i < value.length; i++) {
128
+ const char = value[i];
129
+ if (char === "`") {
130
+ let run = 1;
131
+ while (value[i + run] === "`") run++;
132
+ if (ticks === 0) {
133
+ ticks = run;
134
+ fenced = run >= 3;
135
+ } else if (run === ticks) {
136
+ ticks = 0;
137
+ fenced = false;
138
+ }
139
+ i += run - 1;
140
+ continue;
141
+ }
142
+ if (char === "\n" && ticks > 0 && !fenced) ticks = 0;
143
+ if (char !== "<" || ticks > 0) continue;
144
+ for (const tag of CLOSE_TAGS) {
145
+ if (value.startsWith(tag, i)) return { index: i, length: tag.length };
146
+ }
147
+ // `</think >` / `</thinking >`: whitespace before the closing `>` is
148
+ // accepted, same as the historical regex-backed strip.
149
+ const relaxed = /^<\/(?:think|thinking)\s*>/.exec(value.slice(i));
150
+ if (relaxed !== null) return { index: i, length: relaxed[0].length };
151
+ }
152
+ return undefined;
153
+ }
154
+
155
+ /** The longest suffix of `text` that is a proper prefix of any tag. */
156
+ function danglingTagPrefix(text: string): string {
157
+ const tags = [...OPEN_TAGS, ...CLOSE_TAGS];
158
+ const maxLength = Math.max(...tags.map((tag) => tag.length)) - 1;
159
+ const limit = Math.min(maxLength, text.length);
160
+ for (let length = limit; length > 0; length -= 1) {
161
+ const suffix = text.slice(-length);
162
+ if (tags.some((tag) => tag.startsWith(suffix))) return suffix;
163
+ }
164
+ return "";
165
+ }
166
+
167
+ /** Strips the NUL padding MiMo Studio injects into thought streams. */
168
+ function stripNul(text: string): string {
169
+ return text.includes("\u0000") ? text.replace(/\u0000/g, "") : text;
170
+ }
171
+
172
+ export interface ThinkSplit {
173
+ /** Answer text, with all reasoning spans removed. */
174
+ readonly text: string;
175
+ /** Reasoning text, tag-free. Empty when the chunk carried no thought. */
176
+ readonly reasoning: string;
177
+ /**
178
+ * Content that landed inside a span that is still open at the end of this
179
+ * chunk. It is not yet classified — the span has not closed — so callers
180
+ * that render must not emit it; a loop guard may still watch it.
181
+ */
182
+ readonly pendingReasoning: string;
183
+ }
184
+
185
+ /** A tag match plus the code state at the match, so the caller commits it. */
186
+ interface TagMatch {
187
+ readonly index: number;
188
+ readonly length: number;
189
+ readonly codeTicks: number;
190
+ readonly codeFenced: boolean;
191
+ }
192
+
193
+ /**
194
+ * Splits one upstream text chunk into answer text and reasoning text.
195
+ *
196
+ * Only a pair of matching tags resolves to reasoning: the text inside stays
197
+ * buffered until the closing tag arrives, so the split is never decided before
198
+ * the span is known to be complete. If the stream ends (or the buffer exceeds
199
+ * `MAX_THINK_BLOCK_BYTES`) with the span still open, the whole thing is
200
+ * released as plain text.
201
+ *
202
+ * Call `flush()` once the upstream stream ends to release any text the splitter
203
+ * held back while it waited for a possible tag to complete.
204
+ */
205
+ export class ThinkSplitter {
206
+ #buffer = "";
207
+ #inReasoning = false;
208
+ /** The opening tag of the span currently being scanned; restored on fail-open. */
209
+ #openMarker = "";
210
+ /** Backtick run that opened the code span/fence we are inside; 0 when not in
211
+ * code. A tag inside a code span is the *subject* of the answer — a doc or a
212
+ * code sample mentioning `<think>` — not a delimiter, so it must survive
213
+ * verbatim instead of splitting the stream. */
214
+ #codeTicks = 0;
215
+ /** Whether `#codeTicks` opened a fenced block (closes on its own line) rather
216
+ * than an inline span. */
217
+ #codeFenced = false;
218
+ /** Span characters already reported as `pendingReasoning`; only the fresh
219
+ * tail of the open span is reported per chunk. */
220
+ #reportedSpanLen = 0;
221
+
222
+ /** Feeds a chunk; returns the text/reasoning split for everything settled. */
223
+ push(chunk: string): ThinkSplit {
224
+ this.#buffer += stripNul(chunk);
225
+ let text = "";
226
+ let reasoning = "";
227
+
228
+ for (;;) {
229
+ if (!this.#inReasoning) {
230
+ const opened = this.#findOpen();
231
+ if (opened === undefined) break;
232
+ text += this.#consumeUpTo(opened);
233
+ const openTag = this.#buffer.slice(opened.index, opened.index + opened.length);
234
+ this.#buffer = this.#buffer.slice(opened.index + opened.length);
235
+ this.#inReasoning = true;
236
+ this.#openMarker = openTag;
237
+ continue;
238
+ }
239
+ const closed = this.#findClose();
240
+ if (closed === undefined) break;
241
+ reasoning += this.#consumeUpTo(closed);
242
+ this.#buffer = this.#buffer.slice(closed.index + closed.length);
243
+ this.#inReasoning = false;
244
+ this.#openMarker = "";
245
+ this.#reportedSpanLen = 0;
246
+ }
247
+
248
+ // Hold back a suffix that could still grow into a tag; the rest is settled.
249
+ const held = danglingTagPrefix(this.#buffer);
250
+ const settled = held.length > 0 ? this.#buffer.slice(0, -held.length) : this.#buffer;
251
+ let pendingReasoning = "";
252
+ if (this.#inReasoning) {
253
+ // The closing tag has not arrived: do not classify any of this as
254
+ // reasoning yet. Keep it buffered so a later close can resolve it, but
255
+ // fail open past the cap so a runaway cannot buffer forever.
256
+ if (settled.length > MAX_THINK_BLOCK_BYTES) {
257
+ text += this.#openMarker + settled;
258
+ this.#buffer = held;
259
+ this.#inReasoning = false;
260
+ this.#openMarker = "";
261
+ this.#reportedSpanLen = 0;
262
+ } else {
263
+ this.#buffer = settled + held;
264
+ // Fresh span content this chunk added — visible to a loop guard, never
265
+ // to a renderer (the span has not closed).
266
+ pendingReasoning = this.#buffer.slice(this.#reportedSpanLen);
267
+ this.#reportedSpanLen = this.#buffer.length;
268
+ return { text, reasoning, pendingReasoning };
269
+ }
270
+ } else {
271
+ text += stripThinkingTags(settled);
272
+ this.#buffer = held;
273
+ }
274
+ return { text, reasoning, pendingReasoning };
275
+ }
276
+
277
+ /** Releases text held back for a tag that never completed. */
278
+ flush(): ThinkSplit {
279
+ const rest = this.#buffer;
280
+ this.#buffer = "";
281
+ this.#codeTicks = 0;
282
+ this.#codeFenced = false;
283
+ this.#reportedSpanLen = 0;
284
+ if (this.#inReasoning) {
285
+ // An open tag that never closed is not evidence of reasoning — restore it
286
+ // byte-for-byte and release the whole tail as text.
287
+ this.#inReasoning = false;
288
+ const marker = this.#openMarker;
289
+ this.#openMarker = "";
290
+ return { text: marker + stripThinkingTags(rest), reasoning: "", pendingReasoning: "" };
291
+ }
292
+ return { text: stripThinkingTags(rest), reasoning: "", pendingReasoning: "" };
293
+ }
294
+
295
+ /** Returns the text before a tag and commits the code state at it. */
296
+ #consumeUpTo(match: TagMatch): string {
297
+ this.#codeTicks = match.codeTicks;
298
+ this.#codeFenced = match.codeFenced;
299
+ return this.#buffer.slice(0, match.index);
300
+ }
301
+
302
+ #findOpen(): TagMatch | undefined {
303
+ return this.#findTag(OPEN_TAGS);
304
+ }
305
+
306
+ #findClose(): TagMatch | undefined {
307
+ return this.#findTag(CLOSE_TAGS);
308
+ }
309
+
310
+ /**
311
+ * The earliest occurrence of any of `tags` that is *not* inside a code span
312
+ * or fence, scanning left to right so code state is tracked exactly once per
313
+ * position. Returns the match and the code state at it, so `push` can commit
314
+ * that state when it consumes the tag.
315
+ */
316
+ #findTag(tags: readonly string[]): TagMatch | undefined {
317
+ const buffer = this.#buffer;
318
+ let ticks = this.#codeTicks;
319
+ let fenced = this.#codeFenced;
320
+ for (let i = 0; i < buffer.length; i++) {
321
+ const char = buffer[i];
322
+ if (char === "`") {
323
+ let run = 1;
324
+ while (buffer[i + run] === "`") run++;
325
+ if (ticks === 0) {
326
+ ticks = run;
327
+ // Three or more backticks open a fence rather than an inline span.
328
+ fenced = run >= 3;
329
+ } else if (run === ticks) {
330
+ ticks = 0;
331
+ fenced = false;
332
+ }
333
+ i += run - 1;
334
+ continue;
335
+ }
336
+ // A newline closes an inline span that never closed on its own line; a
337
+ // fence runs to a closing line instead.
338
+ if (char === "\n" && ticks > 0 && !fenced) ticks = 0;
339
+ if (char !== "<" || ticks > 0) continue;
340
+ for (const tag of tags) {
341
+ if (!buffer.startsWith(tag, i)) continue;
342
+ return { index: i, length: tag.length, codeTicks: ticks, codeFenced: fenced };
343
+ }
344
+ }
345
+ return undefined;
346
+ }
347
+ }
@@ -9,7 +9,7 @@ import { log } from "../../observability/logger";
9
9
  * and in the placeholder text the model sees, so each one has to be readable
10
10
  * on its own.
11
11
  */
12
- export type AttachmentDefect =
12
+ type AttachmentDefect =
13
13
  /** The base64 payload is not a whole number of 4-char groups. */
14
14
  | "truncated_base64"
15
15
  /** Base64 is well-formed but the bytes behind it are not a decodable image. */
@@ -26,13 +26,13 @@ export type AttachmentDefect =
26
26
  | "unrecognized_signature";
27
27
 
28
28
  /** One attachment dropped from a request, and why. */
29
- export interface DroppedAttachment {
29
+ interface DroppedAttachment {
30
30
  readonly defect: AttachmentDefect;
31
31
  /** Index of the message the part lived in, for the log line. */
32
32
  readonly messageIndex: number;
33
33
  }
34
34
 
35
- export interface AttachmentIntegrityResult {
35
+ interface AttachmentIntegrityResult {
36
36
  /** The request with every defective inline attachment replaced by a note. */
37
37
  readonly request: CanonicalRequest;
38
38
  /** Non-empty only when something was actually dropped. */
@@ -74,13 +74,13 @@ export function markLatestBreakpoint(
74
74
  // Anthropic Cache Control (Messages API)
75
75
  // ============================================================================
76
76
 
77
- export interface AnthropicCacheControl {
77
+ interface AnthropicCacheControl {
78
78
  type: "ephemeral";
79
79
  /** Real Anthropic Messages wire field — NOT `budget_tokens` (that belongs only to `thinking`). */
80
80
  ttl?: AnthropicCacheTtl;
81
81
  }
82
82
 
83
- export type AnthropicCacheTtl = "5m" | "1h";
83
+ type AnthropicCacheTtl = "5m" | "1h";
84
84
 
85
85
  interface AnthropicCacheControlOptions {
86
86
  ttl?: AnthropicCacheTtl;
@@ -93,27 +93,15 @@ interface AnthropicCacheControlOptions {
93
93
  * No invented minimum prefix length.
94
94
  */
95
95
  export function anthropicCacheControl(
96
- hint: CacheHint | undefined,
96
+ _hint: CacheHint | undefined,
97
97
  options: AnthropicCacheControlOptions = {},
98
- ): Record<string, unknown> {
98
+ ): { cache_control: AnthropicCacheControl } {
99
99
  const control: AnthropicCacheControl = { type: "ephemeral" };
100
100
  if (options.ttl) control.ttl = options.ttl;
101
101
 
102
- if (!hint || hint === "stable_prefix") {
103
- // Stable prefix: emit automatic top-level cache_control at last cacheable block
104
- return { cache_control: control };
105
- }
106
-
107
- if (hint.kind === "breakpoint") {
108
- // Per-content-block cache control for explicitly marked breakpoints
109
- // The breakpoint list indicates positions where cache boundaries should be
110
- // applied (in top-level request builder when flattening to Messages format)
111
- return {
112
- // Caller-marked breakpoints handled by surface adapter
113
- // per_block_cache: hint.list, // not exposed to API, managed by adapter
114
- cache_control: control,
115
- };
116
- }
117
-
102
+ // Every hint shape maps to the same top-level control: stable_prefix emits
103
+ // it at the last cacheable block, breakpoint hints are handled per-block by
104
+ // the surface adapter, and no hint still gets Anthropic's automatic cache
105
+ // control. The (unused) hint parameter is kept so call sites document intent.
118
106
  return { cache_control: control };
119
107
  }
@@ -9,7 +9,7 @@ import {
9
9
  } from "../canonical-model";
10
10
 
11
11
  /** Capabilities declared by one resolved provider/model/route candidate. */
12
- export interface RouteCapabilities {
12
+ interface RouteCapabilities {
13
13
  readonly text: true;
14
14
  readonly image: boolean;
15
15
  /** Accepts non-image document payloads (PDF, plain files). */
@@ -45,7 +45,7 @@ export interface RouteCapabilities {
45
45
  * A bespoke adapter is not in this matrix at all — see
46
46
  * `BESPOKE_GENERATION_CONTROLS` for why it receives every control.
47
47
  */
48
- export const GENERATION_CONTROL_MATRIX: Readonly<
48
+ const GENERATION_CONTROL_MATRIX: Readonly<
49
49
  Record<WireFamily, ReadonlySet<keyof GenerationControls>>
50
50
  > = {
51
51
  chat: new Set([
@@ -98,7 +98,7 @@ const BESPOKE_GENERATION_CONTROLS: ReadonlySet<keyof GenerationControls> = new S
98
98
  * and `web_search_tool_result` are Anthropic Messages blocks, so only the
99
99
  * Messages wire re-encodes them (`protocol/request/messages.ts`).
100
100
  */
101
- export const EXTENSION_MATRIX: Readonly<Record<WireFamily, ReadonlySet<string>>> = {
101
+ const EXTENSION_MATRIX: Readonly<Record<WireFamily, ReadonlySet<string>>> = {
102
102
  chat: new Set(),
103
103
  responses: new Set(),
104
104
  messages: new Set(["server_tool_use", "search_result", "web_search_tool_result"]),
@@ -155,7 +155,7 @@ export function pickWireSupportedControls(
155
155
  * both the router (`routing/router.ts`) and the planner share one predicate
156
156
  * without a layering cycle.
157
157
  */
158
- export interface CapabilityProfileHolder {
158
+ interface CapabilityProfileHolder {
159
159
  readonly capability_profile: Readonly<Record<string, boolean>>;
160
160
  readonly wire_family: WireFamily;
161
161
  }
@@ -259,7 +259,7 @@ export function ensureMaxTokensForThinking(
259
259
  throw new GatewayError(
260
260
  "invalid_request",
261
261
  400,
262
- `Anthropic thinking budget requires max_tokens greater than ${buffer}; got ${currentMax} with budget ${budget}`,
262
+ `Anthropic thinking budget requires max_tokens greater than ${buffer}; got ${currentMax} with budget ${budget} (request {requestId}). Check the shape of what you sent — then resend 🪞`,
263
263
  { budget_tokens: budget, max_tokens: currentMax },
264
264
  );
265
265
  }
@@ -288,7 +288,7 @@ export type RequiredCapability =
288
288
  * No field is silently removed: every unsupported semantic requirement has
289
289
  * already produced a typed rejection before this result exists.
290
290
  */
291
- export interface ProjectedRequest extends CanonicalRequest {
291
+ interface ProjectedRequest extends CanonicalRequest {
292
292
  readonly requiredCapabilities: readonly RequiredCapability[];
293
293
  }
294
294
 
@@ -347,7 +347,7 @@ export function deriveRequiredCapabilities(
347
347
  }
348
348
 
349
349
  /** Anthropic and OpenAI 5.6+ both cap explicit cache breakpoints per request. */
350
- export const MAX_CACHE_BREAKPOINTS = 4;
350
+ const MAX_CACHE_BREAKPOINTS = 4;
351
351
 
352
352
  /**
353
353
  * Validates the cache-breakpoint count at the preflight boundary — before
@@ -364,7 +364,7 @@ function validateCacheBreakpoints(hint: CacheHint | undefined): void {
364
364
  throw new GatewayError(
365
365
  "invalid_request",
366
366
  400,
367
- `cache_hint declares ${hint.list.length} breakpoints; at most ${MAX_CACHE_BREAKPOINTS} are supported`,
367
+ `cache_hint declares ${hint.list.length} breakpoints; at most ${MAX_CACHE_BREAKPOINTS} are supported (request {requestId}). Check the shape of what you sent — then resend 🪞`,
368
368
  { breakpoint_count: hint.list.length, max_breakpoints: MAX_CACHE_BREAKPOINTS },
369
369
  );
370
370
  }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * DeepSeek wire dialect: DSML in-band tool calls.
3
+ *
4
+ * The engine (`DsmlStreamParser`) lives in `src/protocol/dsml.ts` and is shared
5
+ * with the CodeBuddy/Qoder families. This module owns the DeepSeek dialect
6
+ * facade used by the in-band scanner factory.
7
+ */
8
+ import { DsmlStreamParser } from "../../dsml";
9
+
10
+ export { DsmlStreamParser };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * In-band tool-call scanner interface + factory.
3
+ *
4
+ * Some model families emit tool calls as markup inside the text stream instead
5
+ * of structured `tool_calls`: DeepSeek DSML, Qwen `<tool_call>` JSON, OMP
6
+ * `<tool_calls:8hex>`. Each is a pluggable codec; the codecs select one by
7
+ * dialect id.
8
+ */
9
+ import { DsmlStreamParser } from "./deepseek";
10
+ import { QwenToolCallScanner } from "./qwen";
11
+ import type { DialectId } from "./types";
12
+
13
+ interface InbandToolCall {
14
+ readonly id: string;
15
+ readonly name: string;
16
+ readonly arguments: string;
17
+ }
18
+
19
+ export interface InbandScanResult {
20
+ readonly text: string;
21
+ readonly calls: readonly InbandToolCall[];
22
+ }
23
+
24
+ export interface InbandScanner {
25
+ push(chunk: string): InbandScanResult;
26
+ flush(): InbandScanResult;
27
+ }
28
+
29
+ /** Creates the scanner for a dialect, or undefined for structured-only dialects. */
30
+ export function createInbandScanner(
31
+ dialect: DialectId | null | undefined,
32
+ allowedNames: ReadonlySet<string>,
33
+ ): InbandScanner | undefined {
34
+ if (dialect === "qwen") return new QwenToolCallScanner(allowedNames);
35
+ if (dialect === "deepseek") return new DsmlStreamParser({ allowedNames });
36
+ return undefined;
37
+ }
38
+
39
+ export type { DsmlToolCall } from "../../dsml";
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Canonical thinking clamp + output-floor helpers, shared by every wire codec.
3
+ *
4
+ * `clampThinkingLevelForModel` is the single clamp: it steps a requested
5
+ * effort down to the highest supported level ≤ the request, or up to the
6
+ * model's floor when the request is below it. It replaces the old
7
+ * `clampReasoningEffort` with the same semantics, driven by the data-driven
8
+ * `ThinkingConfig` from `thinking-ladder.ts`.
9
+ *
10
+ * `outputFloorFor` is the clamped-effort output floor (previously
11
+ * `clampedEffortOutputFloor` in `protocol/reasoning.ts`): when the applied
12
+ * effort differs from the requested one, the caller sized `max_tokens` for the
13
+ * effort it asked for, and the applied level spends its budget on reasoning
14
+ * before writing the answer — so raise the output budget to the model's
15
+ * catalog ceiling rather than let the answer truncate.
16
+ */
17
+ import {
18
+ REASONING_EFFORT_LADDER,
19
+ type ReasoningEffortLevel,
20
+ } from "../thinking";
21
+ import type { ThinkingConfig } from "./thinking-ladder";
22
+
23
+ /**
24
+ * Clamps a requested reasoning effort against a model/wire's resolved
25
+ * `ThinkingConfig`. Same semantics as the removed `clampReasoningEffort`:
26
+ * synonyms normalize (`ultra` → `max`, `off` → `none`), unknown strings drop
27
+ * (no invented fallback), and a request below the floor uses the floor.
28
+ */
29
+ export function clampThinkingLevelForModel(
30
+ thinking: ThinkingConfig | undefined,
31
+ requested: string | undefined,
32
+ ): ReasoningEffortLevel | undefined {
33
+ if (requested === undefined || typeof requested !== "string") return undefined;
34
+ const raw = requested.toLowerCase().trim();
35
+ const normalized = raw === "ultra" ? "max" : raw === "off" ? "none" : raw;
36
+ if (normalized === "none") return undefined;
37
+ if (thinking === undefined) return normalized as ReasoningEffortLevel;
38
+
39
+ const supported = thinking.efforts;
40
+ if (supported.includes(normalized as ReasoningEffortLevel)) {
41
+ return normalized as ReasoningEffortLevel;
42
+ }
43
+ const requestedIndex = REASONING_EFFORT_LADDER.indexOf(normalized as ReasoningEffortLevel);
44
+ if (requestedIndex === -1) return undefined;
45
+
46
+ let clamped: ReasoningEffortLevel | undefined;
47
+ for (const level of supported) {
48
+ if (REASONING_EFFORT_LADDER.indexOf(level) > requestedIndex) break;
49
+ clamped = level;
50
+ }
51
+ // No smaller supported level exists (caller asked below floor): use floor.
52
+ return clamped ?? supported[0];
53
+ }
54
+
55
+ const CLAMPED_EFFORT_MIN_OUTPUT_TOKENS = 16_384;
56
+
57
+ const EFFORT_LEVEL_RANK: Readonly<Record<string, number>> = {
58
+ off: 0,
59
+ none: 0,
60
+ instant: 1,
61
+ minimal: 2,
62
+ low: 3,
63
+ medium: 4,
64
+ high: 5,
65
+ xhigh: 6,
66
+ max: 7,
67
+ };
68
+
69
+ /**
70
+ * Minimum output budget for a caller whose requested effort the ladder could
71
+ * not serve as asked. See the module doc for why both clamp directions need it.
72
+ * Only ever raises, and only to the model's own catalog limit.
73
+ */
74
+ export function outputFloorFor(
75
+ requested: string | undefined,
76
+ applied: string | undefined,
77
+ modelOutputLimit: number | undefined,
78
+ ): number | undefined {
79
+ if (requested === undefined || applied === undefined) return undefined;
80
+ const from = EFFORT_LEVEL_RANK[requested.trim().toLowerCase()];
81
+ const to = EFFORT_LEVEL_RANK[applied.trim().toLowerCase()];
82
+ if (from === undefined || to === undefined || from === to) return undefined;
83
+ if (modelOutputLimit === undefined || !Number.isFinite(modelOutputLimit) || modelOutputLimit <= 0)
84
+ return undefined;
85
+ // Leave headroom instead of claiming the whole catalog limit: the caller's
86
+ // number was a real preference about answer size, and the goal is to stop
87
+ // truncating, not to spend the model's ceiling on every turn.
88
+ return Math.max(CLAMPED_EFFORT_MIN_OUTPUT_TOKENS, Math.floor(modelOutputLimit / 2));
89
+ }