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
@@ -27,6 +27,7 @@ import type {
27
27
  } from "../../../protocol/canonical-model";
28
28
  import { resolveImageSource, splitDataUrl } from "../../../protocol/codecs";
29
29
  import { IMAGE_CLAMP_MAX_BYTES } from "../../../protocol/image-clamp";
30
+ import { applyCacheMode } from "../../provider-usage";
30
31
  import type { ProviderAdapter, ProviderDispatchContext, ProviderDispatchTarget } from "../../provider-contracts";
31
32
  import { readCredentialSecret } from "../../provider-contracts";
32
33
  import type { Http2BidiEndReason, ValidatedHttp2BidiStream } from "../../../network/http2-bidi";
@@ -62,6 +63,7 @@ import { defaultParameterValues, fetchCursorModels } from "./cursor-catalog";
62
63
  import type { CursorModelInfo } from "./cursor-catalog";
63
64
  import { decodeValueToJson, decodeStructEntriesToJson } from "./protocol/struct";
64
65
  import { cursorVariantForExecArgs } from "./protocol/tool-canonical";
66
+ import { fnv1a32 } from "../shared/hash";
65
67
 
66
68
  /**
67
69
  * Client-heartbeat cadence for every Cursor Run.
@@ -81,8 +83,38 @@ const CURSOR_HEARTBEAT_INTERVAL_MS = 5_000;
81
83
  * every dispatch.
82
84
  */
83
85
  const CURSOR_ROSTER_TTL_MS = 300_000;
84
- /** credential → { roster, fetchedAt } for the parameter lookup. */
86
+ /**
87
+ * credential → { roster, fetchedAt } for the parameter lookup.
88
+ *
89
+ * Keyed by an FNV-1a digest of the credential rather than the raw token so a
90
+ * long-lived map does not hold live credentials as keys. Bounded by
91
+ * `ROSTER_CACHE_MAX` with oldest-first eviction: the TTL is only checked on
92
+ * read, so without a cap rotated tokens would grow the map without bound.
93
+ */
94
+ const ROSTER_CACHE_MAX = 1024;
85
95
  const rosterCache = new Map<string, { roster: readonly CursorModelInfo[]; fetchedAt: number }>();
96
+
97
+ function rosterCacheSet(credential: string, roster: readonly CursorModelInfo[]): void {
98
+ const key = String(fnv1a32(credential));
99
+ rosterCache.delete(key); // re-set moves the entry to newest position
100
+ rosterCache.set(key, { roster, fetchedAt: Date.now() });
101
+ while (rosterCache.size > ROSTER_CACHE_MAX) {
102
+ const oldest = rosterCache.keys().next().value;
103
+ if (oldest === undefined) break;
104
+ rosterCache.delete(oldest);
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Test-only access to the roster cache (cap/eviction/TTL behavior is covered
110
+ * by `test/providers/cursor-roster-cache.test.ts` without a full dispatch).
111
+ */
112
+ export const rosterCacheForTests = {
113
+ size: (): number => rosterCache.size,
114
+ set: rosterCacheSet,
115
+ get: (credential: string): { roster: readonly CursorModelInfo[]; fetchedAt: number } | undefined =>
116
+ rosterCache.get(String(fnv1a32(credential))),
117
+ };
86
118
  /** Client-heartbeat cadence the Cursor edge expects (donor `heartbeatMs`). */
87
119
  const CURSOR_HEARTBEAT_INTERVAL_OVERRIDE = (() => {
88
120
  const raw = process.env.CARTETHYIA_CURSOR_HEARTBEAT_MS?.trim();
@@ -152,14 +184,14 @@ function finishImage(bytes: Uint8Array): CursorImageInput {
152
184
  throw new GatewayError(
153
185
  "invalid_request",
154
186
  400,
155
- "Cursor accepts PNG, JPEG, WebP and GIF image input only",
187
+ "Cursor accepts PNG, JPEG, WebP and GIF image input only (request {requestId}). Check the shape of what you sent — then resend 🪞",
156
188
  );
157
189
  }
158
190
  if (bytes.byteLength > MAX_IMAGE_BYTES) {
159
191
  throw new GatewayError(
160
192
  "invalid_request",
161
193
  400,
162
- `Cursor image input exceeds the ${MAX_IMAGE_BYTES}-byte limit`,
194
+ `Cursor image input exceeds the ${MAX_IMAGE_BYTES}-byte limit (request {requestId}). We love long reads, but not that long 📚`,
163
195
  );
164
196
  }
165
197
  return { data: bytes, mimeType };
@@ -183,7 +215,7 @@ async function resolveImageInput(
183
215
  if (dataUrl !== undefined) {
184
216
  const bytes = base64ToBytes(dataUrl.data);
185
217
  if (bytes === undefined) {
186
- throw new GatewayError("invalid_request", 400, "Cursor image attachment is not valid base64");
218
+ throw new GatewayError("invalid_request", 400, "Cursor image attachment is not valid base64 (request {requestId}). Check the shape of what you sent — then resend 🪞");
187
219
  }
188
220
  return finishImage(bytes);
189
221
  }
@@ -192,7 +224,7 @@ async function resolveImageInput(
192
224
  throw new GatewayError(
193
225
  "invalid_request",
194
226
  400,
195
- "Cursor image URLs must be HTTPS; private and non-TLS sources are not fetched",
227
+ "Cursor image URLs must be HTTPS; private and non-TLS sources are not fetched (request {requestId}). Check the shape of what you sent — then resend 🪞",
196
228
  );
197
229
  }
198
230
  const fetcher = context.outbound_attachment_fetch;
@@ -200,7 +232,7 @@ async function resolveImageInput(
200
232
  throw new GatewayError(
201
233
  "invalid_request",
202
234
  400,
203
- "Cursor has no validated egress to fetch this image URL",
235
+ "Cursor has no validated egress to fetch this image URL (request {requestId}). All roads blocked — time to find a new route 🗺️",
204
236
  );
205
237
  }
206
238
  // Redirects are not followed: reject the response rather than letting a
@@ -222,7 +254,7 @@ async function resolveImageInput(
222
254
  throw new GatewayError(
223
255
  "invalid_request",
224
256
  400,
225
- `Cursor image input exceeds the ${MAX_IMAGE_BYTES}-byte limit`,
257
+ `Cursor image input exceeds the ${MAX_IMAGE_BYTES}-byte limit (request {requestId}). We love long reads, but not that long 📚`,
226
258
  );
227
259
  }
228
260
  return finishImage(body);
@@ -249,7 +281,7 @@ function usageFromTurnEnded(turnEnded: Record<string, unknown>): UsageRecord {
249
281
  const input = count(turnEnded["input_tokens"]);
250
282
  const output = count(turnEnded["output_tokens"]);
251
283
  const cacheRead = count(turnEnded["cache_read"]);
252
- return {
284
+ return applyCacheMode({
253
285
  input_tokens: input,
254
286
  cached_input_tokens: cacheRead,
255
287
  cache_write_tokens: count(turnEnded["cache_write"]),
@@ -258,7 +290,7 @@ function usageFromTurnEnded(turnEnded: Record<string, unknown>): UsageRecord {
258
290
  reasoning_tokens: count(turnEnded["reasoning_tokens"]),
259
291
  estimated_cost: null,
260
292
  total_tokens: input + output,
261
- };
293
+ });
262
294
  }
263
295
 
264
296
  function nonEmptyStringOf(value: unknown): string | undefined {
@@ -503,7 +535,7 @@ class CursorAdapter implements ProviderAdapter {
503
535
  throw new GatewayError(
504
536
  "invalid_request",
505
537
  400,
506
- "This tool result cannot be matched to a live Cursor Run; resend the complete conversation.",
538
+ "This tool result cannot be matched to a live Cursor Run (request {requestId}); resend the complete conversation. The session went cold — reconnect or resend 🔌",
507
539
  );
508
540
  }
509
541
  const resolvedAll = pending.map((item) => cursorRunSessions.resolveCall(item.callId, scope));
@@ -530,7 +562,7 @@ class CursorAdapter implements ProviderAdapter {
530
562
  throw new GatewayError(
531
563
  "invalid_request",
532
564
  400,
533
- "This tool result refers to a Cursor tool call that is no longer pending. Resend the complete conversation.",
565
+ "This tool result refers to a Cursor tool call that is no longer pending (request {requestId}). Resend the complete conversation. The session went cold — reconnect or resend 🔌",
534
566
  { callIds: pending.map((item) => item.callId) },
535
567
  );
536
568
  }
@@ -538,12 +570,12 @@ class CursorAdapter implements ProviderAdapter {
538
570
  throw new GatewayError(
539
571
  "invalid_request",
540
572
  400,
541
- "These tool results belong to different Cursor Runs; resend them one Run at a time.",
573
+ "These tool results belong to different Cursor Runs (request {requestId}); resend them one Run at a time. The session went cold — reconnect or resend 🔌",
542
574
  );
543
575
  }
544
576
  const session = cursorRunSessions.get(runId);
545
577
  if (session === undefined) {
546
- throw new GatewayError("invalid_request", 400, "Cursor Run is no longer live");
578
+ throw new GatewayError("invalid_request", 400, "Cursor Run is no longer live (request {requestId}). The session went cold — reconnect or resend 🔌");
547
579
  }
548
580
  const handle = this.#handles.get(runId);
549
581
  if (handle === undefined) {
@@ -605,7 +637,7 @@ class CursorAdapter implements ProviderAdapter {
605
637
  throw new GatewayError(
606
638
  "admission_unavailable",
607
639
  503,
608
- "Cursor requires validated HTTP/2 egress; none is configured for this route",
640
+ "Cursor requires validated HTTP/2 egress (request {requestId}); none is configured for this route. No bridge to cross — configure a pool first 🏗️",
609
641
  { providerId: CURSOR_PROVIDER_ID },
610
642
  );
611
643
  }
@@ -784,7 +816,7 @@ class CursorAdapter implements ProviderAdapter {
784
816
  : new GatewayError(
785
817
  "platform_unavailable",
786
818
  502,
787
- `Cursor Run failed: ${error instanceof Error ? error.message : String(error)}`,
819
+ `Cursor Run failed (request {requestId}): ${error instanceof Error ? error.message : String(error)}. The upstream came back empty-handed — retry 🌙`,
788
820
  { providerId: CURSOR_PROVIDER_ID },
789
821
  "upstream",
790
822
  );
@@ -828,7 +860,7 @@ class CursorAdapter implements ProviderAdapter {
828
860
  throw new GatewayError(
829
861
  "platform_unavailable",
830
862
  502,
831
- `Cursor Run origin could not be resolved: ${error instanceof Error ? error.message : String(error)}`,
863
+ `Cursor Run origin could not be resolved (request {requestId}): ${error instanceof Error ? error.message : String(error)}. The connection ghosted us — come back in a moment 👻`,
832
864
  { providerId: CURSOR_PROVIDER_ID },
833
865
  "upstream",
834
866
  );
@@ -846,7 +878,8 @@ class CursorAdapter implements ProviderAdapter {
846
878
  credential: string,
847
879
  context: ProviderDispatchContext,
848
880
  ): Promise<readonly CursorModelInfo[]> {
849
- const cached = rosterCache.get(credential);
881
+ const key = String(fnv1a32(credential));
882
+ const cached = rosterCache.get(key);
850
883
  if (cached !== undefined && Date.now() - cached.fetchedAt < CURSOR_ROSTER_TTL_MS) {
851
884
  return cached.roster;
852
885
  }
@@ -858,7 +891,7 @@ class CursorAdapter implements ProviderAdapter {
858
891
  : { fetcher: context.outbound_fetch as unknown as typeof fetch }),
859
892
  signal: context.abort_signal,
860
893
  });
861
- rosterCache.set(credential, { roster, fetchedAt: Date.now() });
894
+ rosterCacheSet(credential, roster);
862
895
  return roster;
863
896
  } catch {
864
897
  return cached?.roster ?? [];
@@ -890,7 +923,7 @@ class CursorAdapter implements ProviderAdapter {
890
923
  }
891
924
  const message = item;
892
925
  if (context.abort_signal.aborted) {
893
- failure = new GatewayError("transport_closed", 499, "request was cancelled");
926
+ failure = new GatewayError("transport_closed", 499, "The request was cancelled (request {requestId}). Send it again whenever ready 🙃");
894
927
  break;
895
928
  }
896
929
  const execServer = recordOf(message["exec_server_message"]);
@@ -994,7 +1027,7 @@ class CursorAdapter implements ProviderAdapter {
994
1027
  : new GatewayError(
995
1028
  "platform_unavailable",
996
1029
  502,
997
- `Cursor Run failed: ${error instanceof Error ? error.message : String(error)}`,
1030
+ `Cursor Run failed (request {requestId}): ${error instanceof Error ? error.message : String(error)}. The upstream came back empty-handed — retry 🌙`,
998
1031
  { providerId: CURSOR_PROVIDER_ID },
999
1032
  "upstream",
1000
1033
  );
@@ -71,59 +71,3 @@ export function* streamFrames(buffer: Uint8Array): Generator<Frame, void, void>
71
71
  offset = start + len
72
72
  }
73
73
  }
74
-
75
- export async function* asyncStreamFrames(
76
- stream: ReadableStream<Uint8Array>,
77
- ): AsyncGenerator<Frame, void, void> {
78
- const reader = stream.getReader()
79
- const chunks: Uint8Array[] = []
80
- let pendingLength = 0
81
-
82
- try {
83
- while (true) {
84
- const { done, value } = await reader.read()
85
- if (done && chunks.length === 0) return
86
- if (value) {
87
- chunks.push(value)
88
- pendingLength += value.length
89
- }
90
-
91
- // Try to parse frames from accumulated data
92
- const merged = mergeChunks(chunks)
93
- const frames = Array.from(streamFrames(merged))
94
- const consumed = frames.reduce((sum, f) => sum + 5 + f.payload.length, 0)
95
-
96
- if (frames.length > 0) {
97
- for (const frame of frames) {
98
- yield frame
99
- }
100
- // Keep any trailing bytes for next iteration
101
- const remaining = merged.subarray(consumed)
102
- chunks.length = 0
103
- if (remaining.length > 0) {
104
- chunks.push(remaining)
105
- pendingLength = remaining.length
106
- } else {
107
- pendingLength = 0
108
- }
109
- }
110
-
111
- if (done) return
112
- }
113
- } finally {
114
- reader.releaseLock()
115
- }
116
- }
117
-
118
- function mergeChunks(chunks: Uint8Array[]): Uint8Array {
119
- const first = chunks[0]
120
- if (chunks.length === 1 && first !== undefined) return first
121
- const total = chunks.reduce((s, c) => s + c.length, 0)
122
- const merged = new Uint8Array(total)
123
- let offset = 0
124
- for (const c of chunks) {
125
- merged.set(c, offset)
126
- offset += c.length
127
- }
128
- return merged
129
- }
@@ -2282,99 +2282,3 @@ export function decodeMessage<T = Record<string, unknown>>(
2282
2282
  const decoded = type.decode(data)
2283
2283
  return type.toObject(decoded, { defaults: true, json: true, longs: Number }) as T
2284
2284
  }
2285
-
2286
- /** Decode without inventing absent proto defaults (used for byte-stable snapshots). */
2287
- export function decodeMessageSparse<T = Record<string, unknown>>(
2288
- typeName: string,
2289
- data: Uint8Array,
2290
- ): T {
2291
- const root = getMessageTypes()
2292
- const type = root.lookupType(typeName)
2293
- const decoded = type.decode(data)
2294
- return type.toObject(decoded, { json: true, longs: Number }) as T
2295
- }
2296
-
2297
- type RawField = {
2298
- field: number
2299
- wireType: number
2300
- varint?: string
2301
- fixed?: number | string
2302
- bytes?: Uint8Array
2303
- }
2304
-
2305
- /** Schema-free walk of one message's top-level wire fields (no nested descent). */
2306
- function readRawFields(buf: Uint8Array): RawField[] {
2307
- const reader = protobuf.Reader.create(buf)
2308
- const fields: RawField[] = []
2309
- while (reader.pos < reader.len) {
2310
- const tag = reader.uint32()
2311
- const field = tag >>> 3
2312
- const wireType = tag & 7
2313
- switch (wireType) {
2314
- case 0:
2315
- fields.push({ field, wireType, varint: reader.uint64().toString() })
2316
- break
2317
- case 1:
2318
- fields.push({ field, wireType, fixed: reader.fixed64().toString() })
2319
- break
2320
- case 2:
2321
- fields.push({ field, wireType, bytes: reader.bytes() })
2322
- break
2323
- case 5:
2324
- fields.push({ field, wireType, fixed: reader.fixed32() })
2325
- break
2326
- default:
2327
- // Groups (3/4) aren't used on this wire; bail rather than desync the reader.
2328
- return fields
2329
- }
2330
- }
2331
- return fields
2332
- }
2333
-
2334
- /**
2335
- * Diagnostic-only: walk AgentServerMessage.interaction_update(1).turn_ended(14)
2336
- * without going through our hand-written TurnEnded schema, so a `cache_write`/
2337
- * `reasoning_tokens` question can be checked against the raw wire instead of
2338
- * guessed. Confirmed field ids 1-5 (input/output/cache_read/cache_write/
2339
- * reasoning) decode correctly. Live captures routinely include `f4` explicitly
2340
- * as `0` while `f3` (cache_read) and `f5` (reasoning) are non-zero — Cursor's
2341
- * agent TurnEnded populates the field but does not report write counts here.
2342
- */
2343
- export function debugWalkTurnEnded(payload: Uint8Array): string {
2344
- try {
2345
- const top = readRawFields(payload)
2346
- const iuField = top.find((f) => f.field === 1 && f.wireType === 2)
2347
- if (!iuField?.bytes) return "(no interaction_update field)"
2348
- const iu = readRawFields(iuField.bytes)
2349
- const teField = iu.find((f) => f.field === 14 && f.wireType === 2)
2350
- if (!teField?.bytes) return "(no turn_ended field in interaction_update)"
2351
- const te = readRawFields(teField.bytes)
2352
- return te
2353
- .map(
2354
- (f) =>
2355
- `f${f.field}:wt${f.wireType}=${f.varint ?? f.fixed ?? (f.bytes ? `bytes[${f.bytes.length}]` : "?")}`,
2356
- )
2357
- .join(" ")
2358
- } catch (e) {
2359
- return `(walk failed: ${(e as Error).message})`
2360
- }
2361
- }
2362
-
2363
- /**
2364
- * Decode a protobuf sub-message from a frame payload that wraps it in
2365
- * a top-level field key + length varint. Skips the outer wrapper and
2366
- * decodes the inner body as `typeName`.
2367
- */
2368
- export function decodeWrappedMessage<T = Record<string, unknown>>(
2369
- typeName: string,
2370
- data: Uint8Array,
2371
- ): T {
2372
- let offset = 1 // skip field key
2373
- // skip varint length
2374
- while (offset < data.length) {
2375
- const b = data[offset] ?? 0
2376
- offset++
2377
- if (!(b & 0x80)) break
2378
- }
2379
- return decodeMessage<T>(typeName, data.subarray(offset))
2380
- }
@@ -106,7 +106,7 @@ function factoryClientHeaders(orgId: string): Record<string, string> {
106
106
  * (`packages/ai/src/providers/factory-droid.ts:271` — `"x-api-provider":
107
107
  * input.upstream`), so a value outside it has no meaning on this wire.
108
108
  */
109
- export const FACTORY_DROID_UPSTREAMS = [
109
+ const FACTORY_DROID_UPSTREAMS = [
110
110
  "fireworks",
111
111
  "baseten",
112
112
  "mistral",
@@ -123,7 +123,7 @@ export const FACTORY_DROID_UPSTREAMS = [
123
123
  "databricks",
124
124
  ] as const;
125
125
 
126
- export type FactoryDroidUpstream = (typeof FACTORY_DROID_UPSTREAMS)[number];
126
+ type FactoryDroidUpstream = (typeof FACTORY_DROID_UPSTREAMS)[number];
127
127
 
128
128
  const FACTORY_DROID_UPSTREAM_SET: ReadonlySet<string> = new Set(FACTORY_DROID_UPSTREAMS);
129
129
 
@@ -154,7 +154,7 @@ function logDroppedUpstream(reason: string, value: string): void {
154
154
  * unmapped failure. Omitting falls back to Factory's default routing, which is
155
155
  * the behavior a request that names no upstream already gets.
156
156
  */
157
- export function factoryApiProviderHeader(
157
+ function factoryApiProviderHeader(
158
158
  options: Readonly<Record<string, unknown>> | undefined,
159
159
  ): Record<string, string> {
160
160
  const value = options?.["factory_api_provider"];
@@ -179,7 +179,7 @@ export function factoryApiProviderHeader(
179
179
  * `/api/cli/whoami` reported. An account with no recorded residency is the
180
180
  * global host, which is Factory's default.
181
181
  */
182
- export function factoryBaseUrl(context: ProviderDispatchContext): string {
182
+ function factoryBaseUrl(context: ProviderDispatchContext): string {
183
183
  const region = context.credential.auth_state?.["region"];
184
184
  return region === "eu" ? FACTORY_DROID_EU_BASE_URL : FACTORY_DROID_BASE_URL;
185
185
  }
@@ -200,7 +200,7 @@ export function factoryBaseUrl(context: ProviderDispatchContext): string {
200
200
  * operator wrote into `auth_state`. The two-axis read is kept because the donor
201
201
  * needs it, not because we currently populate the second axis.
202
202
  */
203
- export function factoryInferenceRegion(
203
+ function factoryInferenceRegion(
204
204
  context: ProviderDispatchContext,
205
205
  ): "global" | "us" | "eu" {
206
206
  const state = context.credential.auth_state;
@@ -266,11 +266,11 @@ const REGION_UPSTREAMS: Readonly<Record<"global" | "us" | "eu", readonly Factory
266
266
  * is, which is a property of the route rather than of the request. The donor's
267
267
  * own message text is preserved as the `reason` so the mapping is traceable.
268
268
  */
269
- export function factoryRegionUnavailable(modelId: string, region: string): GatewayError {
269
+ function factoryRegionUnavailable(modelId: string, region: string): GatewayError {
270
270
  return new GatewayError(
271
271
  "capability_unsupported",
272
272
  400,
273
- `factory: ${modelId} is unavailable in this account region (${region})`,
273
+ `factory: ${modelId} is unavailable in this account region (${region}) (request {requestId}). Know your tools — this one just isn't in the box 🧰`,
274
274
  { model: modelId, region, reason: "Factory Droid model is unavailable in this account region." },
275
275
  );
276
276
  }
@@ -294,7 +294,7 @@ export function factoryRegionUnavailable(modelId: string, region: string): Gatew
294
294
  * Sending `configured_order` without that discovery would claim a rotation we
295
295
  * never resolved.
296
296
  */
297
- export function factoryRoutingSourceHeader(request: CanonicalRequest): Record<string, string> {
297
+ function factoryRoutingSourceHeader(request: CanonicalRequest): Record<string, string> {
298
298
  const continuing = request.messages.some((message) => message.role === "assistant");
299
299
  return { "x-provider-routing-source": continuing ? "session_lock" : "registry_default" };
300
300
  }
@@ -313,7 +313,7 @@ export function factoryRoutingSourceHeader(request: CanonicalRequest): Record<st
313
313
  * donor's own region-unavailable error rather than an upstream 4xx this layer
314
314
  * cannot explain.
315
315
  */
316
- export function factoryRegionCheckedApiProviderHeader(
316
+ function factoryRegionCheckedApiProviderHeader(
317
317
  request: CanonicalRequest,
318
318
  context: ProviderDispatchContext,
319
319
  ): Record<string, string> {
@@ -330,7 +330,7 @@ export function factoryRegionCheckedApiProviderHeader(
330
330
  }
331
331
 
332
332
  /** The two routing headers together, as every Factory wire sends them. */
333
- export function factoryRoutingHeaders(
333
+ function factoryRoutingHeaders(
334
334
  request: CanonicalRequest,
335
335
  context: ProviderDispatchContext,
336
336
  ): Record<string, string> {
@@ -351,7 +351,7 @@ export function factoryRoutingHeaders(
351
351
  function factoryEnvelope(context: ProviderDispatchContext): { token: string; orgId: string } {
352
352
  const secret = context.credential.secret;
353
353
  if (secret === undefined || secret.length === 0) {
354
- throw new GatewayError("invalid_request", 400, "factory: credential is missing a secret value");
354
+ throw new GatewayError("invalid_request", 400, "factory: credential is missing a secret value (request {requestId}). Wrong key, right door — check the credential 🔑");
355
355
  }
356
356
  try {
357
357
  const envelope = decodeFactoryDroidCredential(secret);
@@ -360,7 +360,7 @@ function factoryEnvelope(context: ProviderDispatchContext): { token: string; org
360
360
  throw new GatewayError(
361
361
  "invalid_request",
362
362
  400,
363
- `factory: ${error instanceof Error ? error.message : "stored credential is not a Factory Droid envelope"}`,
363
+ `factory: ${error instanceof Error ? error.message : "stored credential is not a Factory Droid envelope"} (request {requestId}). Wrong key, right door — check the credential 🔑`,
364
364
  );
365
365
  }
366
366
  }
@@ -523,7 +523,7 @@ class FactoryDroidAdapter implements ProviderAdapter {
523
523
  throw new GatewayError(
524
524
  "platform_unavailable",
525
525
  502,
526
- "factory gemini returned empty stream",
526
+ "factory gemini returned empty stream (request {requestId}). The upstream came back empty-handed — retry 🌙",
527
527
  {},
528
528
  "upstream",
529
529
  );
@@ -6,7 +6,7 @@ import { mapUpstreamHttpError } from "../../transport/failure-policy";
6
6
  import type { CanonicalEvent, CanonicalRequest, CanonicalStopReason } from "../../protocol/canonical-model";
7
7
  import { decodeSseEvents } from "../../protocol/sse-decoder";
8
8
  import { usageFromProvider } from "../provider-usage";
9
- import { readCredentialSecret, type ImageGenerationOutcome, type ProviderDispatchTarget, type ProviderAdapter, type ProviderDispatchContext, type WebSearchOutcome, type WebSearchResult } from "../provider-contracts";
9
+ import { readCredentialSecret, assertWireFamily, type ImageGenerationOutcome, type ProviderDispatchTarget, type ProviderAdapter, type ProviderDispatchContext, type WebSearchOutcome, type WebSearchResult } from "../provider-contracts";
10
10
  import type { ModelDefinition } from "../provider-model-definition";
11
11
  import { fetchOpenAICompatibleModels } from "../discovery/openai-model-discovery";
12
12
  import { isRecord } from "../../protocol/codecs";
@@ -181,7 +181,7 @@ class GeminiAdapter implements ProviderAdapter {
181
181
  context: ProviderDispatchContext,
182
182
  ): AsyncIterable<CanonicalEvent> {
183
183
  if (candidate.wire_family !== "chat" && candidate.wire_family !== "responses" && candidate.wire_family !== "messages") {
184
- throw new GatewayError("capability_unsupported", 400, `gemini supports chat/responses/messages, got ${candidate.wire_family}`);
184
+ assertWireFamily(candidate, ["chat", "responses", "messages"], "gemini");
185
185
  }
186
186
  const apiKey = readCredentialSecret(context.credential, "Gemini API key is required");
187
187
 
@@ -262,7 +262,7 @@ class GeminiAdapter implements ProviderAdapter {
262
262
  return;
263
263
  }
264
264
 
265
- if (!response.body) throw new GatewayError("platform_unavailable", 502, "Gemini returned empty stream", {}, "upstream");
265
+ if (!response.body) throw new GatewayError("platform_unavailable", 502, "Gemini returned empty stream (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
266
266
  let seq = 1;
267
267
  yield { type: "response_start", sequence_number: seq++, model: request.model } as CanonicalEvent;
268
268
  const state = { started: true, stopped: false, activeCalls: new Set<string>() };
@@ -339,7 +339,7 @@ class GeminiAdapter implements ProviderAdapter {
339
339
  context: ProviderDispatchContext,
340
340
  ): Promise<WebSearchOutcome> {
341
341
  const query = typeof body.query === "string" ? body.query.trim() : "";
342
- if (!query) throw new GatewayError("invalid_request", 400, "search query is required");
342
+ if (!query) throw new GatewayError("invalid_request", 400, "search query is required (request {requestId}). Check the shape of what you sent — then resend 🪞");
343
343
  const rawMax = body.max_results;
344
344
  const maxResults =
345
345
  typeof rawMax === "number" && Number.isFinite(rawMax)
@@ -368,7 +368,7 @@ class GeminiAdapter implements ProviderAdapter {
368
368
  throw new GatewayError(
369
369
  "platform_unavailable",
370
370
  502,
371
- `Gemini search unreachable: ${error instanceof Error ? error.message : String(error)}`,
371
+ `Gemini search unreachable (request {requestId}): ${error instanceof Error ? error.message : String(error)}. The connection ghosted us — come back in a moment 👻`,
372
372
  );
373
373
  }
374
374
  if (!response.ok) throw await mapUpstreamHttpError(response, "gemini search");
@@ -376,11 +376,11 @@ class GeminiAdapter implements ProviderAdapter {
376
376
  try {
377
377
  data = await response.json();
378
378
  } catch {
379
- throw new GatewayError("platform_unavailable", 502, "Gemini search returned invalid JSON");
379
+ throw new GatewayError("platform_unavailable", 502, "Gemini search returned invalid JSON (request {requestId}). The upstream came back empty-handed — retry 🌙");
380
380
  }
381
381
  const results = normalizeGroundingResults(data, maxResults);
382
382
  if (results.length === 0)
383
- throw new GatewayError("platform_unavailable", 502, "Gemini search returned no grounded results");
383
+ throw new GatewayError("platform_unavailable", 502, "Gemini search returned no grounded results (request {requestId}). The upstream came back empty-handed — retry 🌙");
384
384
  return { results, total_results: results.length };
385
385
  }
386
386
 
@@ -391,7 +391,7 @@ class GeminiAdapter implements ProviderAdapter {
391
391
  ): Promise<ImageGenerationOutcome> {
392
392
  const prompt = imagePrompt(body);
393
393
  const model = candidate.model_id.length > 0 ? candidate.model_id : typeof body.model === "string" ? body.model : "";
394
- if (model.length === 0) throw new GatewayError("invalid_request", 400, "model is required");
394
+ if (model.length === 0) throw new GatewayError("invalid_request", 400, "model is required (request {requestId}). Check the shape of what you sent — then resend 🪞");
395
395
  const apiKey = readCredentialSecret(context.credential, "Gemini API key is required");
396
396
  const outboundFetch = context.outbound_fetch ?? this.fetchFn;
397
397
  const lifecycle = createUpstreamDeadlineLifecycle(context);
@@ -418,7 +418,7 @@ class GeminiAdapter implements ProviderAdapter {
418
418
  throw new GatewayError(
419
419
  "platform_unavailable",
420
420
  502,
421
- `Gemini image generation unreachable: ${error instanceof Error ? error.message : String(error)}`,
421
+ `Gemini image generation unreachable (request {requestId}): ${error instanceof Error ? error.message : String(error)}. The connection ghosted us — come back in a moment 👻`,
422
422
  {},
423
423
  "upstream",
424
424
  );
@@ -430,11 +430,11 @@ class GeminiAdapter implements ProviderAdapter {
430
430
  try {
431
431
  data = await response.json();
432
432
  } catch {
433
- throw new GatewayError("platform_unavailable", 502, "Gemini image generation returned invalid JSON", {}, "upstream");
433
+ throw new GatewayError("platform_unavailable", 502, "Gemini image generation returned invalid JSON (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
434
434
  }
435
435
  const images = geminiInlineImages(data);
436
436
  if (images.length === 0) {
437
- throw new GatewayError("platform_unavailable", 502, "Gemini image generation returned no image", {}, "upstream");
437
+ throw new GatewayError("platform_unavailable", 502, "Gemini image generation returned no image (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
438
438
  }
439
439
  return { created: imageCreatedSeconds(), data: images };
440
440
  }