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,298 @@
1
+ // Shared IP parsing primitives: IPv4 → number, IPv6 → groups/bytes/BigInt,
2
+ // CIDR containment, and loopback detection.
3
+ //
4
+ // Consolidates the two independent copies that used to live in `ssrf.ts` and
5
+ // `security/ip-boundary.ts`. The two modules' semantics differ **on purpose**,
6
+ // so every shared entry point carries the divergence as an explicit opt-in
7
+ // rather than silently picking one behavior:
8
+ //
9
+ // - **Dotted-quad IPv6** (`::ffff:1.2.3.4`): the SSRF guard accepts it (the DNS
10
+ // layer can hand it one); the client-identity layer deliberately fail-closes
11
+ // its BigInt key path on a dotted tail so an unparseable spelling can never
12
+ // mint a database uniqueness key.
13
+ // - **CIDR**: the SSRF guard requires an explicit `/prefix` (a bare address
14
+ // matches nothing — fail-closed on an allowlist) and does not unwrap
15
+ // IPv4-mapped spellings; the trusted-proxy boundary treats a bare address as
16
+ // "this exact host" and unwraps mapped spellings, because Bun reports an
17
+ // IPv4 loopback peer as `::ffff:127.0.0.1` on Windows.
18
+ import { isIP } from "node:net";
19
+
20
+ /**
21
+ * IPv4 literal as a 32-bit unsigned number, or `undefined` when `address` is
22
+ * not a plain dotted quad. Decimal octets only — the exotic spellings `Number`
23
+ * accepts (`0x`, leading `+`, octal) are refused, because this value feeds
24
+ * allowlist comparisons where a lenient parse would widen the match.
25
+ */
26
+ export function ipv4ToNumber(address: string): number | undefined {
27
+ const parts = address.split(".");
28
+ if (parts.length !== 4 || parts.some((part) => !/^\d+$/.test(part))) return undefined;
29
+ const octets = parts.map(Number);
30
+ if (octets.some((part) => part < 0 || part > 255)) return undefined;
31
+ const [a, b, c, d] = octets;
32
+ if (a === undefined || b === undefined || c === undefined || d === undefined) return undefined;
33
+ return ((a * 256 + b) * 256 + c) * 256 + d;
34
+ }
35
+
36
+ /**
37
+ * Same parse as {@link ipv4ToNumber}, but a refusal is an error. The SSRF
38
+ * guard's internal call sites only ever reach the parser with an `isIP`-validated
39
+ * string, so a malformed octet there is an invariant breach, not input handling.
40
+ */
41
+ export function ipv4ToNumberStrict(address: string): number {
42
+ const value = ipv4ToNumber(address);
43
+ if (value === undefined) throw new Error("invalid IPv4");
44
+ return value;
45
+ }
46
+
47
+ /**
48
+ * An IPv6 literal as exactly eight lowercase 16-bit hex groups, or `undefined`
49
+ * when it is not one. Expands a `::` run (which must stand for at least one
50
+ * group) and — with `allowDottedQuad` — converts a trailing dotted quad into
51
+ * the two groups it represents.
52
+ *
53
+ * `allowDottedQuad` is the preserved SSRF/boundary divergence: the SSRF guard
54
+ * passes `true` (DNS answers can arrive as `::ffff:1.2.3.4`), the client-identity
55
+ * key path leaves the default (`false`) so a dotted spelling never reaches a
56
+ * database uniqueness key. The zone id (`%eth0`) is stripped; it identifies an
57
+ * interface, not an address.
58
+ */
59
+ export function normalizeIpv6Groups(
60
+ value: string,
61
+ opts: { allowDottedQuad?: boolean } = {},
62
+ ): string[] | undefined {
63
+ const allowDottedQuad = opts.allowDottedQuad ?? false;
64
+ const text = value.toLowerCase().trim().split("%", 1)[0] ?? "";
65
+ if (text.length === 0) return undefined;
66
+ const doubleIndex = text.indexOf("::");
67
+ // More than one `::` is invalid, as is a `::` inside a group.
68
+ if (doubleIndex !== text.lastIndexOf("::")) return undefined;
69
+
70
+ const headText = doubleIndex >= 0 ? text.slice(0, doubleIndex) : text;
71
+ const tailText = doubleIndex >= 0 ? text.slice(doubleIndex + 2) : "";
72
+ const head = headText.length > 0 ? headText.split(":") : [];
73
+ const tail = tailText.length > 0 ? tailText.split(":") : [];
74
+
75
+ // A dotted quad is a single token that fills two groups.
76
+ const expandDotted = (tokens: string[]): string[] | undefined => {
77
+ const last = tokens[tokens.length - 1];
78
+ if (last === undefined || !last.includes(".")) return tokens;
79
+ if (!allowDottedQuad) return undefined;
80
+ const quad = last.split(".");
81
+ if (quad.length !== 4) return undefined;
82
+ const octets = quad.map((part) => Number(part));
83
+ if (octets.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) return undefined;
84
+ const [a, b, c, d] = octets;
85
+ if (a === undefined || b === undefined || c === undefined || d === undefined) return undefined;
86
+ const high = ((a << 8) | b).toString(16);
87
+ const low = ((c << 8) | d).toString(16);
88
+ return [...tokens.slice(0, -1), high, low];
89
+ };
90
+
91
+ const expandedHead = expandDotted(head);
92
+ const expandedTail = expandDotted(tail);
93
+ if (expandedHead === undefined || expandedTail === undefined) return undefined;
94
+
95
+ let full: string[];
96
+ if (doubleIndex >= 0) {
97
+ const missing = 8 - expandedHead.length - expandedTail.length;
98
+ // `::` must stand for at least one group, so exactly-8 without it is invalid.
99
+ if (missing < 1) return undefined;
100
+ full = [...expandedHead, ...Array.from({ length: missing }, () => "0"), ...expandedTail];
101
+ } else {
102
+ full = expandedHead;
103
+ }
104
+ if (full.length !== 8) return undefined;
105
+ const normalized: string[] = [];
106
+ for (const group of full) {
107
+ if (!/^[0-9a-f]{1,4}$/.test(group)) return undefined;
108
+ normalized.push(Number.parseInt(group, 16).toString(16));
109
+ }
110
+ return normalized;
111
+ }
112
+
113
+ /** The 16 bytes of an IPv6 literal, or `undefined` when it is not one. */
114
+ export function ipv6GroupsToBytes(groups: readonly string[]): Uint8Array | undefined {
115
+ if (groups.length !== 8) return undefined;
116
+ const bytes = new Uint8Array(16);
117
+ for (let index = 0; index < 8; index += 1) {
118
+ const group = groups[index]!;
119
+ if (!/^[0-9a-f]{1,4}$/.test(group)) return undefined;
120
+ const n = Number.parseInt(group, 16);
121
+ bytes[index * 2] = n >>> 8;
122
+ bytes[index * 2 + 1] = n & 255;
123
+ }
124
+ return bytes;
125
+ }
126
+
127
+ /** The 128-bit value of an IPv6 literal, or `undefined` when it is not one. */
128
+ export function ipv6GroupsToBigInt(groups: readonly string[]): bigint | undefined {
129
+ if (groups.length !== 8) return undefined;
130
+ let result = 0n;
131
+ for (const group of groups) {
132
+ if (!/^[0-9a-f]{1,4}$/.test(group)) return undefined;
133
+ result = (result << 16n) | BigInt(Number.parseInt(group, 16));
134
+ }
135
+ return result;
136
+ }
137
+
138
+ /** 128-bit value of an IPv6 literal; `allowDottedQuad` opts into the dotted tail. */
139
+ export function ipv6ToBigInt(address: string, opts: { allowDottedQuad?: boolean } = {}): bigint | undefined {
140
+ const groups = normalizeIpv6Groups(address, opts);
141
+ return groups === undefined ? undefined : ipv6GroupsToBigInt(groups);
142
+ }
143
+
144
+ /**
145
+ * The 32-bit tail of an IPv4-mapped IPv6 address, or `undefined` when `value` is
146
+ * not one. Accepts every spelling of the same address: `::ffff:1.2.3.4`,
147
+ * `::ffff:102:304`, `0:0:0:0:0:ffff:102:304`, and `::ffff:0:102:304`.
148
+ *
149
+ * Works by normalizing to exactly eight 16-bit groups first. That is the only way
150
+ * to compare spellings: a dotted tail is written as ONE colon-separated token but
151
+ * occupies TWO group slots, so any check that counts `split(":")` entries gets the
152
+ * position of the `ffff` marker wrong.
153
+ */
154
+ function mappedIpv4Tail(value: string): string | undefined {
155
+ const groups = normalizeIpv6Groups(value, { allowDottedQuad: true });
156
+ if (groups === undefined) return undefined;
157
+ // The first five groups (80 bits) must be zero, and the sixth must be `ffff`.
158
+ for (let index = 0; index < 5; index += 1) {
159
+ if (groups[index] !== "0") return undefined;
160
+ }
161
+ if (groups[5] !== "ffff") return undefined;
162
+ const high = groups[6];
163
+ const low = groups[7];
164
+ if (high === undefined || low === undefined) return undefined;
165
+ return `${high}:${low}`;
166
+ }
167
+
168
+ /**
169
+ * Unwraps an IPv4-mapped IPv6 address to plain IPv4.
170
+ *
171
+ * Bun reports an IPv4 loopback peer as `::ffff:127.0.0.1` on Windows, and
172
+ * `isIP` classifies it as IPv6 — so a parser that bails on a dotted quad never
173
+ * matched `127.0.0.1/32`. Accepts both the dotted and the hex (`::ffff:7f00:1`)
174
+ * spelling.
175
+ */
176
+ export function unwrapMappedIpv4(address: string): string {
177
+ const value = address.toLowerCase().split("%", 1)[0] ?? "";
178
+ // Recognize the mapped range STRUCTURALLY, not by its compressed spelling. The
179
+ // old `startsWith("::ffff:")` test missed any form that writes the leading zero
180
+ // groups out — `0:0:0:0:0:ffff:7f00:1` is the same host as `::ffff:127.0.0.1`,
181
+ // but it failed the prefix test, fell through to the generic IPv6 parse, and
182
+ // was treated as an unrelated IPv6 address. The trust check then refused a peer
183
+ // the allowlist covered, and `canonicalClientIpKey` minted a second key for one
184
+ // host — which is stored on issued child keys for consistent usage reporting
185
+ // and audit.
186
+ //
187
+ // A mapped address is `::ffff:<32 bits>`, i.e. the first 80 bits are zero and the
188
+ // next 16 are `0xffff`. Matching that is the same test `isIP` uses internally.
189
+ const tail = mappedIpv4Tail(value);
190
+ if (tail === undefined) return address;
191
+ // `mappedIpv4Tail` always returns two hex groups, so the dotted and hex
192
+ // spellings have already converged by here.
193
+ const groups = tail.split(":");
194
+ if (groups.length !== 2) return address;
195
+ const high = Number.parseInt(groups[0] ?? "", 16);
196
+ const low = Number.parseInt(groups[1] ?? "", 16);
197
+ if (
198
+ !Number.isInteger(high) ||
199
+ !Number.isInteger(low) ||
200
+ high < 0 ||
201
+ high > 0xffff ||
202
+ low < 0 ||
203
+ low > 0xffff
204
+ )
205
+ return address;
206
+ return `${(high >> 8) & 0xff}.${high & 0xff}.${(low >> 8) & 0xff}.${low & 0xff}`;
207
+ }
208
+
209
+ export interface CidrContainsOptions {
210
+ /**
211
+ * `true` (SSRF guard): the CIDR must carry an explicit `/prefix` — a bare
212
+ * address matches nothing, so a misconfigured allowlist entry fails closed.
213
+ * `false` (trusted-proxy boundary): a bare address means "this exact host".
214
+ */
215
+ requirePrefix?: boolean;
216
+ /**
217
+ * `true` (trusted-proxy boundary): unwrap IPv4-mapped spellings on both the
218
+ * subject and the network before comparing, so `::ffff:127.0.0.1` and
219
+ * `127.0.0.1/32` describe the same host. `false` (SSRF guard): compare the
220
+ * literal as written.
221
+ */
222
+ unwrapMapped?: boolean;
223
+ /**
224
+ * The SSRF guard's IPv6 parse accepts the dotted-quad spelling (`::ffff:1.2.3.4`,
225
+ * which DNS answers can take); the boundary path refuses it. Defaults to
226
+ * `false`; the SSRF call site passes `true`.
227
+ */
228
+ allowDottedQuad?: boolean;
229
+ }
230
+
231
+ /**
232
+ * Whether `address` falls inside `cidr`. The blank-prefix refusal
233
+ * (`"10.0.0.0/"` must NOT mean /0) is shared by both former implementations and
234
+ * is preserved unconditionally: `Number("")` is 0 and /0 would admit every
235
+ * address of the family — for the SSRF guard that includes the link-local
236
+ * metadata endpoint the guard exists to refuse, and for the trusted-proxy
237
+ * boundary it would believe any caller's `X-Forwarded-For`.
238
+ */
239
+ export function cidrContainsAddress(
240
+ address: string,
241
+ cidr: string,
242
+ opts: CidrContainsOptions = {},
243
+ ): boolean {
244
+ const requirePrefix = opts.requirePrefix ?? true;
245
+ const unwrapMapped = opts.unwrapMapped ?? false;
246
+ const allowDottedQuad = opts.allowDottedQuad ?? false;
247
+ const [network, rawPrefix] = cidr.split("/");
248
+ if (!network) return false;
249
+ const prefixText = rawPrefix === undefined ? undefined : rawPrefix.trim();
250
+ // Requiring a run of digits also refuses the exotic spellings `Number`
251
+ // accepts for zero (`-0`, `+0`, `0x0`, `0b0`, `0o0`, `0.0`) — typos, not a
252
+ // deliberate "match everything".
253
+ if (prefixText !== undefined && !/^\d+$/.test(prefixText)) return false;
254
+ if (requirePrefix && prefixText === undefined) return false;
255
+ const subject = unwrapMapped ? unwrapMappedIpv4(address) : address;
256
+ const base = unwrapMapped ? unwrapMappedIpv4(network) : network;
257
+ const family = isIP(subject);
258
+ if (family !== isIP(base)) return false;
259
+ const maxPrefix = family === 4 ? 32 : 128;
260
+ const prefix = prefixText === undefined ? maxPrefix : Number(prefixText);
261
+ if (!Number.isInteger(prefix) || prefix < 0 || prefix > maxPrefix) return false;
262
+ if (family === 4) {
263
+ const value = ipv4ToNumber(subject);
264
+ const networkValue = ipv4ToNumber(base);
265
+ if (value === undefined || networkValue === undefined) return false;
266
+ if (prefix === 0) return true;
267
+ const mask = (0xffffffff << (32 - prefix)) >>> 0;
268
+ return ((value >>> 0) & mask) === ((networkValue >>> 0) & mask);
269
+ }
270
+ if (family !== 6) return false;
271
+ const value = ipv6ToBigInt(subject, { allowDottedQuad });
272
+ const networkValue = ipv6ToBigInt(base, { allowDottedQuad });
273
+ if (value === undefined || networkValue === undefined) return false;
274
+ const shift = BigInt(128 - prefix);
275
+ return (value >> shift) === (networkValue >> shift);
276
+ }
277
+
278
+ /**
279
+ * True when `address` is a loopback address.
280
+ *
281
+ * Structural, not a string-prefix test: `127.0.0.1`, `::1`, and the mapped
282
+ * `::ffff:127.0.0.1` a Windows/Bun peer arrives as are all the same host, and
283
+ * a prefix test would miss the mapped form — the exact shape that hid this
284
+ * class of bug before.
285
+ *
286
+ * `transport/drain-endpoint.ts` keeps its own weaker `isLoopbackPeer` (a
287
+ * `::ffff:` prefix strip that misses the `0:0:0:0:0:ffff:7f00:1` spelling);
288
+ * unifying it widens the admin gate's acceptance set and is deferred — switch
289
+ * it to this predicate as a deliberate, separately-landed behavior widening.
290
+ */
291
+ export function isLoopbackAddress(address: string): boolean {
292
+ const normalized = unwrapMappedIpv4(address.trim().split("%", 1)[0] ?? "");
293
+ if (normalized === "::1") return true;
294
+ if (isIP(normalized) !== 4) return false;
295
+ const value = ipv4ToNumber(normalized);
296
+ // 127.0.0.0/8 — the whole loopback block, not just 127.0.0.1.
297
+ return value !== undefined && ((value >>> 0) & 0xff000000) === (127 << 24);
298
+ }
@@ -1,7 +1,7 @@
1
1
  // Validated fetch: SSRF-guarded outbound fetch with optional pool-agent support.
2
2
  import { GatewayError } from "../protocol/gateway-error";
3
- import { Agent as HttpAgent } from "node:http";
4
- import { Agent as HttpsAgent } from "node:https";
3
+ import { request as httpRequest, Agent as HttpAgent } from "node:http";
4
+ import { request as httpsRequest, Agent as HttpsAgent } from "node:https";
5
5
  import { Agent as UndiciAgent, fetch as undiciFetch, type UndiciRequestInit } from "undici";
6
6
  import { SocksProxyAgent } from "socks-proxy-agent";
7
7
  import { resolveAllAddresses, validateResolvedAddresses } from "./ssrf";
@@ -10,7 +10,7 @@ import { isProxyAgentPair, type ProxyAgentPair } from "./pool/agent";
10
10
  import { metrics } from "../observability/metrics";
11
11
  import { trackNetworkCall } from "../observability/performance-metrics";
12
12
  // Validated fetch — single egress with ProxyAgent support
13
- export interface ValidatedFetchOptions {
13
+ interface ValidatedFetchOptions {
14
14
  readonly fetchFn?: typeof fetch;
15
15
  readonly policy?: SsrfPolicy;
16
16
  readonly maxRedirects?: number;
@@ -167,6 +167,66 @@ async function dial(
167
167
  });
168
168
  }
169
169
 
170
+ /**
171
+ * One raw GET/HEAD dial that carries a body.
172
+ *
173
+ * The runtime-native fetch (`Bun`/undici) rejects `GET`/`HEAD` requests with a
174
+ * body — "request with GET/HEAD method cannot have body" — but some upstreams
175
+ * validate a signature over the body on a GET (Qoder's `/model/list` does).
176
+ * This is the Node `http`/`https` path for exactly that case. It runs only
177
+ * after the SSRF resolve/validate pass and only when no pool/relay agent owns
178
+ * the dial, so the security boundary is unchanged.
179
+ */
180
+ function nodeDial(url: URL, init: RequestInit): Promise<Response> {
181
+ return new Promise((resolve, reject) => {
182
+ const mod = url.protocol === "http:" ? httpRequest : httpsRequest;
183
+ const headers: Record<string, string> = {};
184
+ {
185
+ const h = new Headers(init.headers);
186
+ h.forEach((value, key) => {
187
+ headers[key] = value;
188
+ });
189
+ }
190
+ let payload: Buffer | undefined;
191
+ const body = init.body;
192
+ if (typeof body === "string") {
193
+ payload = Buffer.from(body, "utf8");
194
+ } else if (Buffer.isBuffer(body)) {
195
+ payload = body;
196
+ } else if (body instanceof Uint8Array) {
197
+ payload = Buffer.from(body);
198
+ } else if (body !== null && body !== undefined) {
199
+ reject(new TypeError("nodeDial only supports string/Buffer/Uint8Array bodies"));
200
+ return;
201
+ }
202
+ if (payload !== undefined) headers["content-length"] = String(payload.byteLength);
203
+ const req = mod(
204
+ url,
205
+ {
206
+ method: (init.method ?? "GET").toUpperCase(),
207
+ headers,
208
+ ...(init.signal === undefined ? {} : { signal: init.signal as AbortSignal }),
209
+ },
210
+ (res) => {
211
+ const chunks: Buffer[] = [];
212
+ res.on("data", (c: Buffer) => chunks.push(c));
213
+ res.on("end", () => {
214
+ resolve(
215
+ new Response(Buffer.concat(chunks), {
216
+ status: res.statusCode ?? 200,
217
+ ...(res.statusMessage === undefined ? {} : { statusText: res.statusMessage }),
218
+ headers: res.headers as unknown as HeadersInit,
219
+ }),
220
+ );
221
+ });
222
+ },
223
+ );
224
+ req.on("error", reject);
225
+ if (payload !== undefined) req.write(payload);
226
+ req.end();
227
+ });
228
+ }
229
+
170
230
  export function createValidatedFetch(options: ValidatedFetchOptions = {}): ValidatedFetch {
171
231
  const policy = options.policy ?? {};
172
232
  const maxRedirects = options.maxRedirects ?? policy.maxRedirects ?? 3;
@@ -242,7 +302,18 @@ export function createValidatedFetch(options: ValidatedFetchOptions = {}): Valid
242
302
  // serverless front door refuses (400).
243
303
  response = await dial(relay.url, relay.init, fetchFn, undefined);
244
304
  } else {
245
- response = await dial(url, requestInit, fetchFn, proxyUrl, socksAgent ? socksDispatcherFor(socksAgent) : undefined, socksFetch);
305
+ const method = (requestInit.method ?? "GET").toUpperCase();
306
+ const hasBody = requestInit.body !== undefined && requestInit.body !== null;
307
+ // The runtime fetch cannot carry a body on GET/HEAD, but Qoder's
308
+ // model-list endpoint requires a signed GET body. When no pool/relay
309
+ // owns the dial and no injected test fetch is present, dial through the
310
+ // Node http(s) client (post SSRF validation). Every other case keeps
311
+ // the existing fetch path.
312
+ if ((method === "GET" || method === "HEAD") && hasBody && !socksAgent && options.fetchFn === undefined) {
313
+ response = await nodeDial(url, requestInit);
314
+ } else {
315
+ response = await dial(url, requestInit, fetchFn, proxyUrl, socksAgent ? socksDispatcherFor(socksAgent) : undefined, socksFetch);
316
+ }
246
317
  }
247
318
  if (response.status < 300 || response.status >= 400) {
248
319
  trackNetworkCall(url.hostname, performance.now() - startedAt);
@@ -264,7 +335,7 @@ export function createValidatedFetch(options: ValidatedFetchOptions = {}): Valid
264
335
  if (isCrossOriginRedirect && (response.status === 307 || response.status === 308)) {
265
336
  const method = (requestInit.method ?? "GET").toUpperCase();
266
337
  if (method !== "GET" && method !== "HEAD" && requestInit.body !== undefined) {
267
- throw new GatewayError("invalid_request", 400, "cross-origin redirect with body blocked");
338
+ throw new GatewayError("invalid_request", 400, "cross-origin redirect with body blocked (request {requestId}). Check the shape of what you sent — then resend 🪞");
268
339
  }
269
340
  }
270
341
  url = nextUrl;
@@ -44,7 +44,7 @@ export type TransportKind = (typeof TRANSPORT_KINDS)[number];
44
44
 
45
45
 
46
46
  /** Config keys reserved for endpoint/label; every other key is opaque non-secret transport config. */
47
- export const RESERVED_ENDPOINT_CONFIG_KEYS: Record<string, true> = {
47
+ const RESERVED_ENDPOINT_CONFIG_KEYS: Record<string, true> = {
48
48
  endpoint: true,
49
49
  label: true,
50
50
  };
@@ -122,9 +122,11 @@ function createSsrfLookup(policy: SsrfPolicy): NonNullable<RequestOptions["looku
122
122
  family?: number,
123
123
  ) => void,
124
124
  ): void => {
125
- const abort = new AbortController();
126
- const timer = setTimeout(() => abort.abort(), 5_000);
127
- void resolveAllAddresses(hostname, abort.signal)
125
+ // No outer AbortController/timer here: `resolveAllAddresses` already races
126
+ // its own 5s deadline against the OS lookup and maps the failure to a
127
+ // deterministic `transport_unavailable` 502. A second timer would only
128
+ // re-race it with a different error class (499/504 depending on timing).
129
+ void resolveAllAddresses(hostname)
128
130
  .then((addrs) => {
129
131
  try {
130
132
  validateResolvedAddresses(addrs, policy);
@@ -145,8 +147,7 @@ function createSsrfLookup(policy: SsrfPolicy): NonNullable<RequestOptions["looku
145
147
  }
146
148
  cb(null, first, family);
147
149
  })
148
- .catch((error: unknown) => cb(error as Error))
149
- .finally(() => clearTimeout(timer));
150
+ .catch((error: unknown) => cb(error as Error));
150
151
  }) as unknown as NonNullable<RequestOptions["lookup"]>;
151
152
  }
152
153
 
@@ -14,7 +14,7 @@
14
14
  */
15
15
 
16
16
  /** Running totals for one pool. */
17
- export interface PoolByteTotals {
17
+ interface PoolByteTotals {
18
18
  /** Bytes sent to the proxy (uploads, TLS handshake, CONNECT). */
19
19
  readonly sent: number;
20
20
  /** Bytes received from the proxy (downloads). */