cartethyia 2.7.8 → 2.7.9

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 (371) hide show
  1. package/.env.example +0 -1
  2. package/README.md +29 -59
  3. package/dist/dashboard/assets/BannedPage-BxoG1MrL.js +1 -0
  4. package/dist/dashboard/assets/BannedPage-BxoG1MrL.js.gz +0 -0
  5. package/dist/dashboard/assets/{CliToolDetailPage-BRWKPQPf.js → CliToolDetailPage-ixwgKk53.js} +1 -1
  6. package/dist/dashboard/assets/CliToolDetailPage-ixwgKk53.js.gz +0 -0
  7. package/dist/dashboard/assets/{CliToolsPage-BP-Y5iHN.js → CliToolsPage-BmttNV3U.js} +1 -1
  8. package/dist/dashboard/assets/CliToolsPage-BmttNV3U.js.gz +0 -0
  9. package/dist/dashboard/assets/CombosPage-BE3qwwfC.js +1 -0
  10. package/dist/dashboard/assets/CombosPage-BE3qwwfC.js.gz +0 -0
  11. package/dist/dashboard/assets/ConsoleLogPage-CRZdmMIX.js +1 -0
  12. package/dist/dashboard/assets/ConsoleLogPage-CRZdmMIX.js.gz +0 -0
  13. package/dist/dashboard/assets/CustomizationPage-Dv_FJ6Er.js +1 -0
  14. package/dist/dashboard/assets/CustomizationPage-Dv_FJ6Er.js.gz +0 -0
  15. package/dist/dashboard/assets/HealthEventsModal-CoVrw2sM.js +1 -0
  16. package/dist/dashboard/assets/HealthEventsModal-CoVrw2sM.js.gz +0 -0
  17. package/dist/dashboard/assets/{InjectSystemPromptPanel-Cv-2Ol_K.js → InjectSystemPromptPanel-BOoO8rpz.js} +2 -2
  18. package/dist/dashboard/assets/InjectSystemPromptPanel-BOoO8rpz.js.gz +0 -0
  19. package/dist/dashboard/assets/{LoginPage-bKfz_b1K.js → LoginPage-ChJuNNVN.js} +1 -1
  20. package/dist/dashboard/assets/LoginPage-ChJuNNVN.js.gz +0 -0
  21. package/dist/dashboard/assets/{ModelPicker-B-WQxx_H.js → ModelPicker-BZAJtPg1.js} +1 -1
  22. package/dist/dashboard/assets/ModelPicker-BZAJtPg1.js.gz +0 -0
  23. package/dist/dashboard/assets/OverviewPage-BE40s3u5.js +1 -0
  24. package/dist/dashboard/assets/OverviewPage-BE40s3u5.js.gz +0 -0
  25. package/dist/dashboard/assets/ProviderDetailPage-sfLoQtPV.js +8 -0
  26. package/dist/dashboard/assets/ProviderDetailPage-sfLoQtPV.js.gz +0 -0
  27. package/dist/dashboard/assets/ProviderIcon-CQ6bPUf_.js +1 -0
  28. package/dist/dashboard/assets/ProviderIcon-CQ6bPUf_.js.gz +0 -0
  29. package/dist/dashboard/assets/ProvidersPage-DcepmPWo.js +1 -0
  30. package/dist/dashboard/assets/ProvidersPage-DcepmPWo.js.gz +0 -0
  31. package/dist/dashboard/assets/{ProxyPage-emlTuxz6.js → ProxyPage-BJ87Pp0U.js} +1 -1
  32. package/dist/dashboard/assets/ProxyPage-BJ87Pp0U.js.gz +0 -0
  33. package/dist/dashboard/assets/{QuotaPage-CMJKNYWb.js → QuotaPage-gkSyRAKa.js} +1 -1
  34. package/dist/dashboard/assets/QuotaPage-gkSyRAKa.js.gz +0 -0
  35. package/dist/dashboard/assets/SettingsPage-C5HPWqEv.js +1 -0
  36. package/dist/dashboard/assets/SettingsPage-C5HPWqEv.js.gz +0 -0
  37. package/dist/dashboard/assets/{SetupPage-CUZrKs9r.js → SetupPage-CFNcATJJ.js} +1 -1
  38. package/dist/dashboard/assets/SetupPage-CFNcATJJ.js.gz +0 -0
  39. package/dist/dashboard/assets/{StudioPage-jWmUdDpy.js → StudioPage-YoD4tY5l.js} +1 -1
  40. package/dist/dashboard/assets/StudioPage-YoD4tY5l.js.gz +0 -0
  41. package/dist/dashboard/assets/UsagePage-DxjaAhb_.js +56 -0
  42. package/dist/dashboard/assets/UsagePage-DxjaAhb_.js.gz +0 -0
  43. package/dist/dashboard/assets/api-keys-BTlkq_Uk.js +1 -0
  44. package/dist/dashboard/assets/api-keys-BTlkq_Uk.js.gz +0 -0
  45. package/dist/dashboard/assets/app-BX2nnKqs.css +2 -0
  46. package/dist/dashboard/assets/app-BX2nnKqs.css.gz +0 -0
  47. package/dist/dashboard/assets/assert-CndthXFQ.js +1 -0
  48. package/dist/dashboard/assets/assert-CndthXFQ.js.gz +0 -0
  49. package/dist/dashboard/assets/{badge-DyI_mJfi.js → badge-DdbT_7Am.js} +1 -1
  50. package/dist/dashboard/assets/badge-DdbT_7Am.js.gz +0 -0
  51. package/dist/dashboard/assets/ban-durations-B58FyRxC.js +1 -0
  52. package/dist/dashboard/assets/ban-durations-B58FyRxC.js.gz +0 -0
  53. package/dist/dashboard/assets/{banner-B-yXtCqb.js → banner-CsGN-nbm.js} +1 -1
  54. package/dist/dashboard/assets/banner-CsGN-nbm.js.gz +0 -0
  55. package/dist/dashboard/assets/brand/bansos_page.webp +0 -0
  56. package/dist/dashboard/assets/brand/denia_bansos.webp +0 -0
  57. package/dist/dashboard/assets/brand/favicon.webp +0 -0
  58. package/dist/dashboard/assets/brand/login_register.webp +0 -0
  59. package/dist/dashboard/assets/brand/login_register_initial.webp +0 -0
  60. package/dist/dashboard/assets/brand/og_bansos.webp +0 -0
  61. package/dist/dashboard/assets/brand/og_image.webp +0 -0
  62. package/dist/dashboard/assets/brand/sidebar_ico.webp +0 -0
  63. package/dist/dashboard/assets/{card-_HwAEqFF.js → card-mgZ_TLLZ.js} +1 -1
  64. package/dist/dashboard/assets/card-mgZ_TLLZ.js.gz +0 -0
  65. package/dist/dashboard/assets/cli-tools-A1-v8-03.js +1 -0
  66. package/dist/dashboard/assets/cli-tools-A1-v8-03.js.gz +0 -0
  67. package/dist/dashboard/assets/{clipboard-button-ByjCk0o9.js → clipboard-button-CJ4Zty89.js} +1 -1
  68. package/dist/dashboard/assets/clipboard-button-CJ4Zty89.js.gz +0 -0
  69. package/dist/dashboard/assets/{dialog-BZhOlfz-.js → dialog-B5L2jPQ_.js} +1 -1
  70. package/dist/dashboard/assets/dialog-B5L2jPQ_.js.gz +0 -0
  71. package/dist/dashboard/assets/{drawer-BADqIRYP.js → drawer-DkjUouAj.js} +1 -1
  72. package/dist/dashboard/assets/drawer-DkjUouAj.js.gz +0 -0
  73. package/dist/dashboard/assets/{entry-COaC-irN.js → entry-B-LVLV6o.js} +1 -1
  74. package/dist/dashboard/assets/entry-B-LVLV6o.js.gz +0 -0
  75. package/dist/dashboard/assets/entry-CAhdYl3B.js +1 -0
  76. package/dist/dashboard/assets/entry-CAhdYl3B.js.gz +0 -0
  77. package/dist/dashboard/assets/entry-D2xD0Tmd.js +4 -0
  78. package/dist/dashboard/assets/entry-D2xD0Tmd.js.gz +0 -0
  79. package/dist/dashboard/assets/{formatters-DcHJyRiu.js → formatters-CrsdLVsn.js} +1 -1
  80. package/dist/dashboard/assets/formatters-CrsdLVsn.js.gz +0 -0
  81. package/dist/dashboard/assets/{index-CgKmFngv.js → index-BU64Lqvp.js} +2 -2
  82. package/dist/dashboard/assets/index-BU64Lqvp.js.gz +0 -0
  83. package/dist/dashboard/assets/{inline-B2BirKry.js → inline-B2aDLyyg.js} +1 -1
  84. package/dist/dashboard/assets/inline-B2aDLyyg.js.gz +0 -0
  85. package/dist/dashboard/assets/{input-D-qV9dKY.js → input-BCVkdR9d.js} +1 -1
  86. package/dist/dashboard/assets/input-BCVkdR9d.js.gz +0 -0
  87. package/dist/dashboard/assets/{layout-B7tVWhtV.js → layout-DNcAdMK-.js} +1 -1
  88. package/dist/dashboard/assets/layout-DNcAdMK-.js.gz +0 -0
  89. package/dist/dashboard/assets/{list-row-CtkGE3bF.js → list-row-DgrIH0hw.js} +1 -1
  90. package/dist/dashboard/assets/list-row-DgrIH0hw.js.gz +0 -0
  91. package/dist/dashboard/assets/{live-DGizF06-.js → live-Cae01iu8.js} +1 -1
  92. package/dist/dashboard/assets/live-Cae01iu8.js.gz +0 -0
  93. package/dist/dashboard/assets/{mutation-GU7XdEjz.js → mutation-BQ0ccH_A.js} +1 -1
  94. package/dist/dashboard/assets/mutation-BQ0ccH_A.js.gz +0 -0
  95. package/dist/dashboard/assets/{network-C9UE-bOr.js → network-CRib7H_2.js} +1 -1
  96. package/dist/dashboard/assets/network-CRib7H_2.js.gz +0 -0
  97. package/dist/dashboard/assets/{numbers-BnAd22a_.js → numbers-BKQE8Xpz.js} +1 -1
  98. package/dist/dashboard/assets/numbers-BKQE8Xpz.js.gz +0 -0
  99. package/dist/dashboard/assets/{pagination-BAywyMzS.js → pagination-ecPvG_8g.js} +1 -1
  100. package/dist/dashboard/assets/pagination-ecPvG_8g.js.gz +0 -0
  101. package/dist/dashboard/assets/providers/devin.svg +9 -0
  102. package/dist/dashboard/assets/providers/devin.svg.gz +0 -0
  103. package/dist/dashboard/assets/providers/windsurf.svg +10 -0
  104. package/dist/dashboard/assets/providers/windsurf.svg.gz +0 -0
  105. package/dist/dashboard/assets/{query-keys-YSoQOUh6.js → query-keys-IKLh0YFd.js} +1 -1
  106. package/dist/dashboard/assets/query-keys-IKLh0YFd.js.gz +0 -0
  107. package/dist/dashboard/assets/routing-Ckm1UXuC.js +1 -0
  108. package/dist/dashboard/assets/routing-Ckm1UXuC.js.gz +0 -0
  109. package/dist/dashboard/assets/{select-BCOcv8WK.js → select-ChoFWyKz.js} +1 -1
  110. package/dist/dashboard/assets/select-ChoFWyKz.js.gz +0 -0
  111. package/dist/dashboard/assets/settings-CKjFPlKW.js +1 -0
  112. package/dist/dashboard/assets/settings-CKjFPlKW.js.gz +0 -0
  113. package/dist/dashboard/assets/{stack-ByKt3Q1g.js → stack-DWCwU3fh.js} +1 -1
  114. package/dist/dashboard/assets/stack-DWCwU3fh.js.gz +0 -0
  115. package/dist/dashboard/assets/{state-DXbGTx7N.js → state-Cg3VL5J_.js} +1 -1
  116. package/dist/dashboard/assets/state-Cg3VL5J_.js.gz +0 -0
  117. package/dist/dashboard/assets/story/25817331.webp +0 -0
  118. package/dist/dashboard/assets/story/Shorekeeper.webp +0 -0
  119. package/dist/dashboard/assets/story/cartethyia-god.webp +0 -0
  120. package/dist/dashboard/assets/story/fleurdelys_plus.webp +0 -0
  121. package/dist/dashboard/assets/story/jinhsi-blossom.webp +0 -0
  122. package/dist/dashboard/assets/story/phrolova-a.webp +0 -0
  123. package/dist/dashboard/assets/story/requestdeniawokkjpg.webp +0 -0
  124. package/dist/dashboard/assets/story/wuthering-waves-hiyuki-aemeath-hiyuki-rover.webp +0 -0
  125. package/dist/dashboard/assets/{switch-ZeTsW623.js → switch-Db70BMix.js} +1 -1
  126. package/dist/dashboard/assets/switch-Db70BMix.js.gz +0 -0
  127. package/dist/dashboard/assets/{useMutation-CbCP4w1o.js → useMutation-BqdE0LuM.js} +1 -1
  128. package/dist/dashboard/assets/useMutation-BqdE0LuM.js.gz +0 -0
  129. package/dist/dashboard/console-backgrounds.webp +0 -0
  130. package/dist/dashboard/console-backgrounds_initial.webp +0 -0
  131. package/dist/dashboard/donation/qris-risun-marke.webp +0 -0
  132. package/dist/dashboard/index.html +3 -3
  133. package/dist/dashboard/index.html.gz +0 -0
  134. package/dist/dashboard/precompressed-assets.json +57 -55
  135. package/migrations/full-postgres/0044_add_models_warning_metadata.sql +10 -0
  136. package/migrations/full-postgres/0045_migrate_workbuddy_to_codebuddy.sql +156 -0
  137. package/migrations/lite-sqlite3/0044_add_models_warning_metadata.sql +11 -0
  138. package/migrations/lite-sqlite3/0045_migrate_workbuddy_to_codebuddy.sql +156 -0
  139. package/package.json +1 -1
  140. package/src/boot/dependencies.ts +11 -3
  141. package/src/config/cooldowns.ts +1 -1
  142. package/src/config/spec.ts +0 -6
  143. package/src/console/api-keys/routes.ts +11 -4
  144. package/src/console/console-router.ts +7 -0
  145. package/src/console/dashboard-assets.ts +5 -5
  146. package/src/console/domain-registration.ts +27 -0
  147. package/src/console/ip-bans/routes.ts +99 -0
  148. package/src/console/providers/catalog/catalog-projections.ts +5 -2
  149. package/src/console/providers/catalog/contracts.ts +123 -0
  150. package/src/console/providers/catalog/model-operations.ts +122 -10
  151. package/src/console/providers/catalog/provider-operations.ts +116 -8
  152. package/src/console/providers/catalog/public-model-store.ts +0 -0
  153. package/src/console/providers/catalog/routes.ts +64 -4
  154. package/src/console/providers/catalog/store-helpers.ts +30 -1
  155. package/src/console/providers/catalog/store.ts +243 -3
  156. package/src/console/quota/account-quota-global.ts +89 -1
  157. package/src/console/quota/native-identity.ts +2 -0
  158. package/src/console/settings/contracts.ts +18 -0
  159. package/src/console/settings/store.ts +32 -0
  160. package/src/console/system-prompt/contracts.ts +0 -1
  161. package/src/main.ts +1 -0
  162. package/src/observability/event-query.ts +18 -4
  163. package/src/persistence/dialect.ts +12 -0
  164. package/src/persistence/schema.ts +20 -2
  165. package/src/protocol/dsml.ts +1 -1
  166. package/src/protocol/gateway-error.ts +1 -0
  167. package/src/protocol/reasoning.ts +1 -1
  168. package/src/protocol/request/chat.ts +4 -4
  169. package/src/protocol/request/messages.ts +1 -1
  170. package/src/protocol/response/gemini.ts +6 -1
  171. package/src/protocol/sse-decoder.ts +25 -8
  172. package/src/protocol/stream-error-frames.ts +13 -3
  173. package/src/protocol/translation/quirks.ts +1 -1
  174. package/src/protocol/translation/tool-repair.ts +2 -2
  175. package/src/protocol/upstream/send-claude-messages.ts +15 -4
  176. package/src/protocol/upstream-parsing.ts +1 -1
  177. package/src/providers/adapters/compatible-adapter.ts +7 -5
  178. package/src/providers/authentication/oauth-refresh-service.ts +149 -36
  179. package/src/providers/default-registry.ts +68 -3
  180. package/src/providers/discovery/discovery-types.ts +12 -0
  181. package/src/providers/discovery/probe-phases.ts +61 -30
  182. package/src/providers/discovery/probing-service.ts +279 -164
  183. package/src/providers/integrations/antigravity/antigravity-protocol.ts +117 -2
  184. package/src/providers/integrations/antigravity/antigravity.ts +13 -2
  185. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +72 -34
  186. package/src/providers/integrations/buddy/buddy-chat-shared.ts +18 -18
  187. package/src/providers/integrations/buddy/buddy-checkin.ts +17 -39
  188. package/src/providers/integrations/buddy/buddy-conversation-headers.ts +2 -2
  189. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +5 -5
  190. package/src/providers/integrations/buddy/buddy-identity.ts +70 -0
  191. package/src/providers/integrations/buddy/buddy-media.ts +4 -5
  192. package/src/providers/integrations/buddy/buddy-oauth-shared.ts +2 -2
  193. package/src/providers/integrations/buddy/buddy-quota-shared.ts +1 -1
  194. package/src/providers/integrations/buddy/buddy-usage-shared.ts +1 -3
  195. package/src/providers/integrations/buddy/capabilities.ts +4 -41
  196. package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +2 -1
  197. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +20 -1
  198. package/src/providers/integrations/buddy/codebuddy/provider.ts +2 -2
  199. package/src/providers/integrations/buddy/codebuddy/shared.ts +6 -10
  200. package/src/providers/integrations/cursor/cursor-catalog.ts +2 -0
  201. package/src/providers/integrations/cursor/cursor-exec.ts +738 -0
  202. package/src/providers/integrations/cursor/cursor-http2.ts +2 -0
  203. package/src/providers/integrations/cursor/cursor-request.ts +9 -8
  204. package/src/providers/integrations/cursor/cursor-shared.ts +19 -1
  205. package/src/providers/integrations/cursor/cursor.ts +50 -30
  206. package/src/providers/integrations/cursor/protocol/tool-canonical.ts +26 -100
  207. package/src/providers/integrations/devin/devin-auth.ts +353 -0
  208. package/src/providers/integrations/devin/devin-catalog.ts +207 -0
  209. package/src/providers/integrations/devin/devin-http.ts +91 -0
  210. package/src/providers/integrations/devin/devin-quota.ts +214 -0
  211. package/src/providers/integrations/devin/devin-shared.ts +111 -0
  212. package/src/providers/integrations/devin/devin.ts +539 -0
  213. package/src/providers/integrations/devin/protocol/devin-proto.ts +584 -0
  214. package/src/providers/integrations/devin/protocol/framing.ts +124 -0
  215. package/src/providers/integrations/factory/factory.ts +2 -2
  216. package/src/providers/integrations/freepi/freepi-catalog.ts +60 -0
  217. package/src/providers/integrations/freepi/freepi-discovery.ts +82 -0
  218. package/src/providers/integrations/freepi/freepi-headers.ts +21 -0
  219. package/src/providers/integrations/freepi/freepi-oauth.ts +142 -0
  220. package/src/providers/integrations/freepi/freepi-protocol.ts +242 -0
  221. package/src/providers/integrations/freepi/freepi-quota.ts +126 -0
  222. package/src/providers/integrations/freepi/freepi-realm.ts +35 -0
  223. package/src/providers/integrations/freepi/freepi.ts +292 -0
  224. package/src/providers/integrations/gemini.ts +2 -2
  225. package/src/providers/integrations/hermes/hermes-oauth.ts +94 -0
  226. package/src/providers/integrations/hermes.ts +4 -12
  227. package/src/providers/integrations/opencode/fingerprint.ts +1 -1
  228. package/src/providers/integrations/perch/perch-catalog.ts +105 -0
  229. package/src/providers/integrations/perch/perch-oauth.ts +170 -0
  230. package/src/providers/integrations/perch/perch-protocol.ts +211 -0
  231. package/src/providers/integrations/perch/perch-quota.ts +137 -0
  232. package/src/providers/integrations/perch/perch-realm.ts +82 -0
  233. package/src/providers/integrations/perch/perch-system-prompt.ts +13 -0
  234. package/src/providers/integrations/perch/perch.ts +315 -0
  235. package/src/providers/integrations/perch/saffron-system-prompt.txt +8 -0
  236. package/src/providers/integrations/qoder/chat-proto.ts +328 -328
  237. package/src/providers/integrations/qoder/claim-qoder-credits.ts +1 -1
  238. package/src/providers/integrations/qoder/qoder-adapter.ts +89 -28
  239. package/src/providers/integrations/qoder/qoder-cache.ts +32 -0
  240. package/src/providers/integrations/qoder/qoder-catalog.ts +6 -1
  241. package/src/providers/integrations/qoder/qoder-checkin.ts +2 -2
  242. package/src/providers/integrations/qoder/qoder-grpc.ts +210 -6
  243. package/src/providers/integrations/qoder/qoder-ide.ts +5 -1
  244. package/src/providers/integrations/qoder/qoder-identity-cache.ts +184 -0
  245. package/src/providers/integrations/qoder/qoder-native-identity-store.ts +83 -18
  246. package/src/providers/integrations/qoder/qoder-realm.ts +9 -6
  247. package/src/providers/integrations/qoder/qoder-umid.ts +109 -342
  248. package/src/providers/integrations/qoder/qoder-virtual.ts +33 -1
  249. package/src/providers/integrations/qoder/qoder.ts +3 -6
  250. package/src/providers/integrations/trae/trae-catalog.ts +9 -14
  251. package/src/providers/integrations/trae/trae-headers.ts +36 -11
  252. package/src/providers/integrations/trae/trae-oauth.ts +14 -17
  253. package/src/providers/integrations/trae/trae-protocol.ts +3 -6
  254. package/src/providers/integrations/trae/trae-quota.ts +103 -21
  255. package/src/providers/integrations/trae/trae-realm.ts +36 -15
  256. package/src/providers/integrations/trae/trae.ts +97 -7
  257. package/src/providers/operations/account-error-classification.ts +66 -1
  258. package/src/providers/operations/account-health-service.ts +192 -26
  259. package/src/providers/operations/account-refresh-status.ts +284 -0
  260. package/src/providers/operations/account-tier.ts +67 -0
  261. package/src/providers/operations/client-versions.ts +37 -99
  262. package/src/providers/operations/cooldown-rules.ts +61 -0
  263. package/src/providers/operations/provider-catalog-seeder.ts +4 -0
  264. package/src/providers/provider-contracts.ts +29 -0
  265. package/src/providers/provider-metadata.ts +59 -4
  266. package/src/providers/provider-model-definition.ts +36 -0
  267. package/src/providers/quota/refresh.ts +21 -0
  268. package/src/security/abuse.ts +71 -0
  269. package/src/transport/dispatch/proxy-request.ts +1 -0
  270. package/src/transport/dispatch/streaming/stream.ts +1 -0
  271. package/src/transport/dispatch/upstream.ts +4 -1
  272. package/src/transport/failure-policy.ts +113 -7
  273. package/src/transport/routing/router.ts +25 -1
  274. package/src/transport/surface/messages/adapter.ts +2 -1
  275. package/src/transport/surface/responses/encode.ts +12 -1
  276. package/src/workers/daily-checkin.ts +1 -1
  277. package/src/workers/quota-refresh-sweep.ts +1 -1
  278. package/src/workers/scheduler.ts +22 -4
  279. package/dist/dashboard/assets/BannedPage-DeIbIiBU.js +0 -1
  280. package/dist/dashboard/assets/BannedPage-DeIbIiBU.js.gz +0 -0
  281. package/dist/dashboard/assets/CliToolDetailPage-BRWKPQPf.js.gz +0 -0
  282. package/dist/dashboard/assets/CliToolsPage-BP-Y5iHN.js.gz +0 -0
  283. package/dist/dashboard/assets/CombosPage-B7JHozVa.js +0 -1
  284. package/dist/dashboard/assets/CombosPage-B7JHozVa.js.gz +0 -0
  285. package/dist/dashboard/assets/ConsoleLogPage-GX7iGGcu.js +0 -1
  286. package/dist/dashboard/assets/ConsoleLogPage-GX7iGGcu.js.gz +0 -0
  287. package/dist/dashboard/assets/CustomizationPage-HVonbGEa.js +0 -1
  288. package/dist/dashboard/assets/CustomizationPage-HVonbGEa.js.gz +0 -0
  289. package/dist/dashboard/assets/HealthEventsModal-xaut4OJ9.js +0 -1
  290. package/dist/dashboard/assets/HealthEventsModal-xaut4OJ9.js.gz +0 -0
  291. package/dist/dashboard/assets/InjectSystemPromptPanel-Cv-2Ol_K.js.gz +0 -0
  292. package/dist/dashboard/assets/LoginPage-bKfz_b1K.js.gz +0 -0
  293. package/dist/dashboard/assets/ModelPicker-B-WQxx_H.js.gz +0 -0
  294. package/dist/dashboard/assets/OverviewPage-Z9pJZFo8.js +0 -1
  295. package/dist/dashboard/assets/OverviewPage-Z9pJZFo8.js.gz +0 -0
  296. package/dist/dashboard/assets/ProviderDetailPage-CMeDBxR1.js +0 -7
  297. package/dist/dashboard/assets/ProviderDetailPage-CMeDBxR1.js.gz +0 -0
  298. package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js +0 -1
  299. package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js.gz +0 -0
  300. package/dist/dashboard/assets/ProvidersPage-BXPdOpG4.js +0 -1
  301. package/dist/dashboard/assets/ProvidersPage-BXPdOpG4.js.gz +0 -0
  302. package/dist/dashboard/assets/ProxyPage-emlTuxz6.js.gz +0 -0
  303. package/dist/dashboard/assets/QuotaPage-CMJKNYWb.js.gz +0 -0
  304. package/dist/dashboard/assets/SettingsPage-BSy7EwGo.js +0 -1
  305. package/dist/dashboard/assets/SettingsPage-BSy7EwGo.js.gz +0 -0
  306. package/dist/dashboard/assets/SetupPage-CUZrKs9r.js.gz +0 -0
  307. package/dist/dashboard/assets/StudioPage-jWmUdDpy.js.gz +0 -0
  308. package/dist/dashboard/assets/UsagePage-5F9tGVGG.js +0 -56
  309. package/dist/dashboard/assets/UsagePage-5F9tGVGG.js.gz +0 -0
  310. package/dist/dashboard/assets/api-keys-KvX83mRe.js +0 -1
  311. package/dist/dashboard/assets/api-keys-KvX83mRe.js.gz +0 -0
  312. package/dist/dashboard/assets/app-BEsU8hKs.css +0 -2
  313. package/dist/dashboard/assets/app-BEsU8hKs.css.gz +0 -0
  314. package/dist/dashboard/assets/assert-CidQVoNo.js +0 -1
  315. package/dist/dashboard/assets/assert-CidQVoNo.js.gz +0 -0
  316. package/dist/dashboard/assets/badge-DyI_mJfi.js.gz +0 -0
  317. package/dist/dashboard/assets/banner-B-yXtCqb.js.gz +0 -0
  318. package/dist/dashboard/assets/brand/bansos_page.png +0 -0
  319. package/dist/dashboard/assets/brand/denia_bansos.jpg +0 -0
  320. package/dist/dashboard/assets/brand/favicon.jpg +0 -0
  321. package/dist/dashboard/assets/brand/login_register.jpg +0 -0
  322. package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
  323. package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
  324. package/dist/dashboard/assets/brand/og_image.jpg +0 -0
  325. package/dist/dashboard/assets/brand/sidebar_ico.jpg +0 -0
  326. package/dist/dashboard/assets/card-_HwAEqFF.js.gz +0 -0
  327. package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js +0 -1
  328. package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js.gz +0 -0
  329. package/dist/dashboard/assets/clipboard-button-ByjCk0o9.js.gz +0 -0
  330. package/dist/dashboard/assets/dialog-BZhOlfz-.js.gz +0 -0
  331. package/dist/dashboard/assets/drawer-BADqIRYP.js.gz +0 -0
  332. package/dist/dashboard/assets/entry-COaC-irN.js.gz +0 -0
  333. package/dist/dashboard/assets/entry-Ca7LSCet.js +0 -4
  334. package/dist/dashboard/assets/entry-Ca7LSCet.js.gz +0 -0
  335. package/dist/dashboard/assets/entry-PDd27uCd.js +0 -1
  336. package/dist/dashboard/assets/entry-PDd27uCd.js.gz +0 -0
  337. package/dist/dashboard/assets/formatters-DcHJyRiu.js.gz +0 -0
  338. package/dist/dashboard/assets/index-CgKmFngv.js.gz +0 -0
  339. package/dist/dashboard/assets/inline-B2BirKry.js.gz +0 -0
  340. package/dist/dashboard/assets/input-D-qV9dKY.js.gz +0 -0
  341. package/dist/dashboard/assets/layout-B7tVWhtV.js.gz +0 -0
  342. package/dist/dashboard/assets/list-row-CtkGE3bF.js.gz +0 -0
  343. package/dist/dashboard/assets/live-DGizF06-.js.gz +0 -0
  344. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js +0 -1
  345. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
  346. package/dist/dashboard/assets/mutation-GU7XdEjz.js.gz +0 -0
  347. package/dist/dashboard/assets/network-C9UE-bOr.js.gz +0 -0
  348. package/dist/dashboard/assets/numbers-BnAd22a_.js.gz +0 -0
  349. package/dist/dashboard/assets/pagination-BAywyMzS.js.gz +0 -0
  350. package/dist/dashboard/assets/query-keys-YSoQOUh6.js.gz +0 -0
  351. package/dist/dashboard/assets/routing-fnCsxgNO.js +0 -1
  352. package/dist/dashboard/assets/routing-fnCsxgNO.js.gz +0 -0
  353. package/dist/dashboard/assets/select-BCOcv8WK.js.gz +0 -0
  354. package/dist/dashboard/assets/settings-BxslsErn.js +0 -1
  355. package/dist/dashboard/assets/settings-BxslsErn.js.gz +0 -0
  356. package/dist/dashboard/assets/stack-ByKt3Q1g.js.gz +0 -0
  357. package/dist/dashboard/assets/state-DXbGTx7N.js.gz +0 -0
  358. package/dist/dashboard/assets/switch-ZeTsW623.js.gz +0 -0
  359. package/dist/dashboard/assets/useMutation-CbCP4w1o.js.gz +0 -0
  360. package/dist/dashboard/console-backgrounds.jpg +0 -0
  361. package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
  362. package/dist/dashboard/donation/qris-risun-marke.png +0 -0
  363. package/src/providers/integrations/buddy/turing-device-token.ts +0 -262
  364. package/src/providers/integrations/buddy/workbuddy/cn-oauth.ts +0 -30
  365. package/src/providers/integrations/buddy/workbuddy/cn-provider.ts +0 -58
  366. package/src/providers/integrations/buddy/workbuddy/oauth.ts +0 -31
  367. package/src/providers/integrations/buddy/workbuddy/provider.ts +0 -209
  368. package/src/providers/integrations/buddy/workbuddy/quota.ts +0 -161
  369. package/src/providers/integrations/buddy/workbuddy/shared.ts +0 -285
  370. /package/dist/dashboard/assets/{app-CTxoqyYB.js → app-B7kIWFm5.js} +0 -0
  371. /package/dist/dashboard/assets/{app-CTxoqyYB.js.gz → app-B7kIWFm5.js.gz} +0 -0
@@ -1,6 +1,7 @@
1
1
  import { t } from "elysia";
2
2
  import type { Elysia } from "elysia";
3
- import { and, eq, inArray, isNull } from "drizzle-orm";
3
+ import { and, eq, inArray, isNull, sql } from "drizzle-orm";
4
+ import { isSqliteDatabase } from "../../persistence/dialect";
4
5
  import { adminAuditLog, providerAccounts } from "../../persistence/schema";
5
6
  import { requireGlobalAdmin } from "../shared/errors";
6
7
  import {
@@ -26,6 +27,24 @@ import {
26
27
  type AccountQuotaRoutesDeps,
27
28
  type AccountStatus,
28
29
  } from "./account-quota-shared";
30
+ import type { ProviderAccountBulkActionResult } from "../providers/catalog/contracts";
31
+
32
+ /**
33
+ * The same live-cooldown predicate as the tenant-scoped store, evaluated in
34
+ * SQL so one statement covers the whole shared-account set. `status='cooldown'`
35
+ * counts even when its deadline elapsed; a live `model_cooldowns` entry counts
36
+ * even while `status` is still `active` (D07).
37
+ */
38
+ function globalLiveCooldownPredicate(database: unknown): ReturnType<typeof and> {
39
+ const nowIso = new Date().toISOString();
40
+ const liveModelCooldown = isSqliteDatabase(database)
41
+ ? sql`EXISTS (SELECT 1 FROM json_each(${providerAccounts.modelCooldowns}) AS entry WHERE json_valid(${providerAccounts.modelCooldowns}) AND json_type(${providerAccounts.modelCooldowns}) = 'object' AND entry.value > ${nowIso})`
42
+ : sql`EXISTS (SELECT 1 FROM jsonb_each_text(${providerAccounts.modelCooldowns}) AS entry WHERE jsonb_typeof(${providerAccounts.modelCooldowns}) = 'object' AND entry.value > ${nowIso})`;
43
+ return and(
44
+ isNull(providerAccounts.tenantId),
45
+ sql`${providerAccounts.status} = 'cooldown' OR ${providerAccounts.cooldownUntil} > ${nowIso} OR ${liveModelCooldown}`,
46
+ )!;
47
+ }
29
48
 
30
49
  /**
31
50
  * Registers the platform-admin account/quota routes onto `app`. Every route here
@@ -38,6 +57,47 @@ export function registerAccountQuotaGlobalRoutes(
38
57
  refreshDeps: QuotaRefreshDeps,
39
58
  ): void {
40
59
  const { db, accessResolver, redis, snapshotInvalidator } = deps;
60
+ /**
61
+ * The two bulk actions are one statement each, scoped strictly to shared
62
+ * rows. Neither action takes a provider filter: the global-admin surface
63
+ * lists shared accounts across providers, and the same health predicate
64
+ * defines the target set everywhere.
65
+ */
66
+ const runGlobalBulk = async (
67
+ kind: "disable" | "delete",
68
+ ): Promise<ProviderAccountBulkActionResult> => {
69
+ if (kind === "disable") {
70
+ const updated = await db
71
+ .update(providerAccounts)
72
+ .set({ status: "disabled" })
73
+ .where(globalLiveCooldownPredicate(db))
74
+ .returning({ id: providerAccounts.id });
75
+ invalidateCredentialCaches(updated.map((row) => row.id));
76
+ await snapshotInvalidator?.invalidate();
77
+ return { ok: true, updated: updated.length, globalSkipped: 0 };
78
+ }
79
+ const deleted = await db
80
+ .delete(providerAccounts)
81
+ .where(
82
+ and(
83
+ isNull(providerAccounts.tenantId),
84
+ eq(providerAccounts.status, "disabled"),
85
+ eq(providerAccounts.lastErrorCategory, "policy_blocked_banned"),
86
+ ),
87
+ )
88
+ .returning({ id: providerAccounts.id });
89
+ const deletedIds = deleted.map((row) => row.id);
90
+ invalidateCredentialCaches(deletedIds);
91
+ await invalidateQuotaCaches(GLOBAL_QUOTA_LENS, deletedIds, redis);
92
+ await snapshotInvalidator?.invalidate();
93
+ return {
94
+ ok: true,
95
+ updated: 0,
96
+ deleted: deletedIds,
97
+ missing: [],
98
+ globalSkipped: 0,
99
+ };
100
+ };
41
101
  app
42
102
  .get("/global/accounts", async ({ request }) => {
43
103
  requireGlobalAdmin(accessResolver(request));
@@ -157,6 +217,34 @@ export function registerAccountQuotaGlobalRoutes(
157
217
  });
158
218
  return { ok: true, deleted: deletedIds, missing };
159
219
  })
220
+ /**
221
+ * Global counterparts of the provider-detail bulk actions (D10): shared
222
+ * accounts carry `tenant_id IS NULL`, so the tenant-scoped pair can never
223
+ * match them. Both are one statement, one audit row, one snapshot
224
+ * invalidation, and authorize through `requireGlobalAdmin`.
225
+ */
226
+ .post("/global/accounts/actions/disable-cooldowns", async ({ request }) => {
227
+ const access = requireGlobalAdmin(accessResolver(request));
228
+ const { ok, updated, globalSkipped } = await runGlobalBulk("disable");
229
+ await db.insert(adminAuditLog).values({
230
+ actor: access.admissionIdentity,
231
+ action: "provider_account.bulk_disabled_cooldowns",
232
+ target: "global",
233
+ detail: { updated, globalSkipped },
234
+ });
235
+ return { ok, updated, globalSkipped };
236
+ })
237
+ .post("/global/accounts/actions/delete-banned", async ({ request }) => {
238
+ const access = requireGlobalAdmin(accessResolver(request));
239
+ const { ok, updated, deleted, missing, globalSkipped } = await runGlobalBulk("delete");
240
+ await db.insert(adminAuditLog).values({
241
+ actor: access.admissionIdentity,
242
+ action: "provider_account.bulk_deleted_banned",
243
+ target: "global",
244
+ detail: { deleted: deleted ?? [], missing: missing ?? [], globalSkipped },
245
+ });
246
+ return { ok, updated, deleted: deleted ?? [], missing: missing ?? [], globalSkipped };
247
+ })
160
248
  .post("/global/accounts/:id/quota/refresh", async ({ request, params, set }) => {
161
249
  const access = requireGlobalAdmin(accessResolver(request));
162
250
  const target = await loadQuotaTarget(db, params.id, null);
@@ -16,6 +16,7 @@ import {
16
16
  DrizzleQoderNativeIdentityStore,
17
17
  type QoderNativeIdentityStore,
18
18
  } from "../../providers/integrations/qoder/qoder-native-identity-store";
19
+ import { qoderRealmForProvider } from "../../providers/integrations/qoder/qoder-realm";
19
20
  import { ConsoleDomainError, requireGlobalAdmin, requireTenantScope } from "../shared/errors";
20
21
  import type { ConsoleAccessResolver } from "../auth/access";
21
22
 
@@ -38,6 +39,7 @@ export type NativeIdentityAllocator = (args: {
38
39
  function defaultAllocator(store: QoderNativeIdentityStore): NativeIdentityAllocator {
39
40
  return ({ providerId, accountId, avoidToken }) =>
40
41
  allocateQoderNativeIdentity(store, providerId, accountId, {
42
+ realm: qoderRealmForProvider(providerId),
41
43
  ...(avoidToken !== undefined ? { avoidToken } : {}),
42
44
  });
43
45
  }
@@ -59,6 +59,15 @@ export interface RuntimeSettingsResponse {
59
59
  * used instead.
60
60
  */
61
61
  readonly visionHandoffModels: readonly string[];
62
+ /**
63
+ * Auto-disable provider accounts stuck on upstream rate-limit/quota verdicts.
64
+ * Default false — the risky direction stays off until an operator flips it.
65
+ */
66
+ readonly autoDisableRateLimited: boolean;
67
+ /** Modelprobe mode; `detail` when unset. */
68
+ readonly modelProbeMode: ModelProbeMode;
69
+ /** Fast-probe timeout in seconds; null means the built-in default (10). */
70
+ readonly modelProbeFastTimeoutSeconds: number | null;
62
71
  readonly updatedAt: string;
63
72
  /*
64
73
  * What each `null` actually resolves to, in the same units the row stores.
@@ -136,6 +145,10 @@ export type PonyTailLevel = (typeof PONYTAIL_LEVELS)[number];
136
145
  /** RTK prune strength accepted by the runtime-settings PATCH. */
137
146
  export type RtkLevel = (typeof RTK_LEVELS)[number];
138
147
 
148
+ export const MODEL_PROBE_MODES = ["fast", "detail"] as const;
149
+ /** Modelprobe mode accepted by the runtime-settings PATCH and the probe route. */
150
+ export type ModelProbeMode = (typeof MODEL_PROBE_MODES)[number];
151
+
139
152
  /** Upper bound on the number of ordered vision-handoff models. */
140
153
  export const VISION_HANDOFF_MAX_MODELS = 8;
141
154
 
@@ -272,4 +285,9 @@ export const runtimeUpdateBody = t.Object({
272
285
  t.Union([t.Null(), t.Number({ minimum: 64 * 1024, maximum: 100 * 1024 * 1024 })]),
273
286
  ),
274
287
  visionHandoffModels: t.Optional(t.Array(t.String())),
288
+ autoDisableRateLimited: t.Optional(t.Boolean()),
289
+ modelProbeMode: t.Optional(literalUnion(MODEL_PROBE_MODES)),
290
+ modelProbeFastTimeoutSeconds: t.Optional(
291
+ t.Union([t.Null(), t.Number({ minimum: 1, maximum: 120 })]),
292
+ ),
275
293
  });
@@ -10,6 +10,7 @@ import { resolveUpstreamTimeoutMs } from "../../config/timeouts";
10
10
  import { payloadRetentionMs } from "../../observability/payload-capture";
11
11
  import { IMAGE_CLAMP_MAX_BYTES } from "../../protocol/image-clamp";
12
12
  import {
13
+ MODEL_PROBE_MODES,
13
14
  PONYTAIL_LEVELS,
14
15
  RESPONSES_REASONING_SUMMARIES,
15
16
  RTK_LEVELS,
@@ -23,6 +24,7 @@ import {
23
24
  type RuntimeSettingsStore,
24
25
  type TelemetryPayloadDepth,
25
26
  type TelemetryPayloadMode,
27
+ type ModelProbeMode,
26
28
  type UpdateRuntimeSettingsRequest,
27
29
  } from "./contracts";
28
30
 
@@ -116,6 +118,23 @@ function normalizeConcurrencyTolerance(value: unknown): number | null {
116
118
  return normalizeBoundedNumber(value, 0, 1024);
117
119
  }
118
120
 
121
+ /** Modelprobe mode: only the two known modes survive; anything else = `detail`. */
122
+ function normalizeModelProbeMode(value: unknown): ModelProbeMode {
123
+ return typeof value === "string" && (MODEL_PROBE_MODES as readonly string[]).includes(value)
124
+ ? (value as ModelProbeMode)
125
+ : "detail";
126
+ }
127
+
128
+ /**
129
+ * Fast-probe timeout in seconds: 1–120, `null` = deployment default, and any
130
+ * other absent/non-finite value reads as the built-in default of 10 (the UI
131
+ * select's middle choice, and the value probing uses when unset).
132
+ */
133
+ function normalizeModelProbeFastTimeoutSeconds(value: unknown): number | null {
134
+ if (value === null) return null;
135
+ return normalizeBoundedNumber(value, 1, 120) ?? 10;
136
+ }
137
+
119
138
  /** Ordered vision-handoff models: non-empty strings, trimmed, de-duplicated. */
120
139
  function normalizeVisionHandoffModels(value: unknown): readonly string[] {
121
140
  if (!Array.isArray(value)) return [];
@@ -166,6 +185,11 @@ function mapRuntimeSettingsRow(
166
185
  telemetryWindowMinutes: normalizeTelemetryWindowMinutes(prefs.telemetryWindowMinutes),
167
186
  imageClampMaxBytes: normalizeImageClampMaxBytes(prefs.imageClampMaxBytes),
168
187
  visionHandoffModels: normalizeVisionHandoffModels(prefs.visionHandoffModels),
188
+ autoDisableRateLimited: prefs.autoDisableRateLimited === true,
189
+ modelProbeMode: normalizeModelProbeMode(prefs.modelProbeMode),
190
+ modelProbeFastTimeoutSeconds: normalizeModelProbeFastTimeoutSeconds(
191
+ prefs.modelProbeFastTimeoutSeconds,
192
+ ),
169
193
  updatedAt,
170
194
  /*
171
195
  * Resolved at request time, not at module load: all four come from env the
@@ -231,6 +255,14 @@ export class DrizzleRuntimeSettingsStore implements RuntimeSettingsStore {
231
255
  patchPrefs.imageClampMaxBytes = normalizeImageClampMaxBytes(patch.imageClampMaxBytes);
232
256
  if (patch.visionHandoffModels !== undefined)
233
257
  patchPrefs.visionHandoffModels = normalizeVisionHandoffModels(patch.visionHandoffModels);
258
+ if (patch.autoDisableRateLimited !== undefined)
259
+ patchPrefs.autoDisableRateLimited = patch.autoDisableRateLimited === true;
260
+ if (patch.modelProbeMode !== undefined)
261
+ patchPrefs.modelProbeMode = normalizeModelProbeMode(patch.modelProbeMode);
262
+ if (patch.modelProbeFastTimeoutSeconds !== undefined)
263
+ patchPrefs.modelProbeFastTimeoutSeconds = normalizeModelProbeFastTimeoutSeconds(
264
+ patch.modelProbeFastTimeoutSeconds,
265
+ );
234
266
 
235
267
  const rows = await this.db
236
268
  .insert(consoleSettings)
@@ -115,7 +115,6 @@ export const SYSTEM_PROMPT_ALIAS_LIMIT = 512;
115
115
  const WRAPPED_INJECTION_PROVIDERS: ReadonlySet<string> = new Set([
116
116
  "cb", // CodeBuddy International
117
117
  "cbcn", // CodeBuddy CN
118
- "workbuddy",
119
118
  ]);
120
119
 
121
120
  /** Whether a provider's tenant text rides wrapped after the fixed identity prompt. */
package/src/main.ts CHANGED
@@ -84,6 +84,7 @@ const app = boot
84
84
  oauthRefreshService: boot.deps.oauthRefreshService,
85
85
  admissionService: boot.deps.admissionService,
86
86
  readRoutingAccountInflight: boot.deps.readRoutingAccountInflight,
87
+ ipAbuseStore: boot.deps.ipAbuseStore,
87
88
  // The same three writers the shutdown coordinator drains, so a manual
88
89
  // flush from the console does exactly what a graceful stop would do
89
90
  // for the queues, minus the stop.
@@ -41,11 +41,26 @@ export const IS_GATEWAY_ERROR_SQL = gatewayErrorSql(telemetryEvents.status, tele
41
41
  * cannot drift; the sum fragments coalesce to 0 so an empty window reads as
42
42
  * zero rather than null.
43
43
  */
44
+ /**
45
+ * Cached-token aggregate under the "eligible request" rule.
46
+ *
47
+ * A request that carried any cached input (`cached_input_tokens > 0`) is a
48
+ * cache hit, and the WHOLE prompt is read as cached — the operator's rule is
49
+ * binary per request, not proportional (see the Usage page's "Cached tokens"
50
+ * tile and the provider Activity Matrix). So the aggregate is the sum of
51
+ * `input_tokens` over rows with a positive cached count, not the sum of the
52
+ * cached counts themselves.
53
+ *
54
+ * This reuses the same column the row-level estimator writes
55
+ * (`cached_input_tokens`), so it stays consistent across SQLite and Postgres.
56
+ */
57
+ const CACHED_INPUT_TOKENS_SQL = sql<number>`coalesce(sum(case when ${telemetryEvents.cachedInputTokens} > 0 then ${telemetryEvents.inputTokens} else 0 end), 0)`;
58
+
44
59
  const AGGREGATE_SELECT = {
45
60
  errors: sql<number>`sum(case when ${IS_GATEWAY_ERROR_SQL} then 1 else 0 end)`,
46
61
  inputTokens: sql<number>`coalesce(sum(${telemetryEvents.inputTokens}), 0)`,
47
62
  outputTokens: sql<number>`coalesce(sum(${telemetryEvents.outputTokens}), 0)`,
48
- cachedInputTokens: sql<number>`coalesce(sum(${telemetryEvents.cachedInputTokens}), 0)`,
63
+ cachedInputTokens: CACHED_INPUT_TOKENS_SQL,
49
64
  } as const;
50
65
 
51
66
  /** One bounded page of events, newest first. */
@@ -531,7 +546,6 @@ export async function breakdownTelemetry(
531
546
  key: column,
532
547
  requests: sql<number>`count(*)`,
533
548
  ...AGGREGATE_SELECT,
534
- cachedInputTokens: sql<number>`coalesce(sum(${telemetryEvents.cachedInputTokens}), 0)`,
535
549
  costUsd: sql<number>`coalesce(sum(${telemetryEvents.estimatedCostUsd}), 0)`,
536
550
  avgTokensPerSec: sql<number>`coalesce(avg(${telemetryEvents.tokensPerSec}), 0)`,
537
551
  })
@@ -620,7 +634,7 @@ export async function providerUsageMatrix(
620
634
  model: telemetryEvents.requestedModel,
621
635
  requests: sql<number>`count(*)`,
622
636
  inputTokens: AGGREGATE_SELECT.inputTokens,
623
- cachedInputTokens: sql<number>`coalesce(sum(${telemetryEvents.cachedInputTokens}), 0)`,
637
+ cachedInputTokens: CACHED_INPUT_TOKENS_SQL,
624
638
  outputTokens: AGGREGATE_SELECT.outputTokens,
625
639
  costUsd: sql<number>`coalesce(sum(${telemetryEvents.estimatedCostUsd}), 0)`,
626
640
  })
@@ -861,7 +875,7 @@ export async function summarizeUsageWithPerformance(
861
875
  truncated: sql<number>`sum(case when ${telemetryEvents.status} = 'truncated' then 1 else 0 end)`,
862
876
  inputTokens: AGGREGATE_SELECT.inputTokens,
863
877
  outputTokens: AGGREGATE_SELECT.outputTokens,
864
- cachedInputTokens: sql<number>`coalesce(sum(${telemetryEvents.cachedInputTokens}), 0)`,
878
+ cachedInputTokens: CACHED_INPUT_TOKENS_SQL,
865
879
  reasoningTokens: sql<number>`coalesce(sum(${telemetryEvents.reasoningTokens}), 0)`,
866
880
  estimatedCostUsd: sql<number>`coalesce(sum(${telemetryEvents.estimatedCostUsd}), 0)`,
867
881
  sampled: sql<number>`count(${telemetryEvents.latencyMs})`,
@@ -55,6 +55,18 @@ function hasSqliteDialect(database: unknown): boolean {
55
55
  return (database as { session?: unknown } | undefined)?.session instanceof SQLiteBunSession;
56
56
  }
57
57
 
58
+ /**
59
+ * Dialect of a concrete Drizzle handle, rather than of the process's env.
60
+ *
61
+ * A test can hold an in-memory SQLite database while `CARTETHYIA_DB_MODE`
62
+ * names `full` (the backend suite sets that globally). A raw SQL predicate
63
+ * chosen from the env would emit `jsonb_each_text` into SQLite and fail; the
64
+ * handle's session is the authority, exactly as `mergeJson` already treats it.
65
+ */
66
+ export function isSqliteDatabase(database: unknown): boolean {
67
+ return hasSqliteDialect(database);
68
+ }
69
+
58
70
  /**
59
71
  * The per-model cooldown object with every entry whose timestamp has passed
60
72
  * removed, or `'{}'` when nothing remains.
@@ -219,8 +219,8 @@ export interface NativeIdentityRecord {
219
219
  readonly machineCode: string;
220
220
  /** The seed-file content this identity was minted from, if captured. */
221
221
  readonly seed?: string;
222
- /** How this identity was produced: the genuine SG bridge or the fallback. */
223
- readonly source?: "runtime-info" | "generated";
222
+ /** How this identity was produced: the genuine SG bridge, the OS fingerprint, or the fallback. */
223
+ readonly source?: "runtime-info" | "generated" | "os";
224
224
  }
225
225
 
226
226
  export const providerAccounts = pgTable("provider_accounts", {
@@ -432,6 +432,11 @@ export const models = pgTable(
432
432
  reasoning: boolean("reasoning").notNull().default(false),
433
433
  toolCall: boolean("tool_call").notNull().default(false),
434
434
  cost: jsonb("cost"),
435
+ // Display-only warning metadata for this row: Postgres `jsonb` (SQLite
436
+ // `text` carrying the JSON string), nullable, `NULL` = no warnings. Shape:
437
+ // `{ warnings: [{ kind, message, severity?, updatedAt? }] }`. Part of the
438
+ // provider-account model-update proposal §3.4 area 06; not read by routing.
439
+ warningMetadata: jsonb("warning_metadata"),
435
440
  source: text("source"),
436
441
  sourceUpdatedAt: timestamp("source_updated_at", { withTimezone: true }),
437
442
  enabled: boolean("enabled").notNull().default(true),
@@ -1288,6 +1293,19 @@ export interface ConsoleSettingsPreferences {
1288
1293
  * placeholder behavior.
1289
1294
  */
1290
1295
  visionHandoffModels?: readonly string[];
1296
+ /**
1297
+ * Auto-disable provider accounts that keep failing with upstream rate-limit
1298
+ * or quota verdicts. Off by default: without it, those failures land on a
1299
+ * per-model/account cooldown and the account stays rotatable.
1300
+ */
1301
+ autoDisableRateLimited?: boolean;
1302
+ /**
1303
+ * Modelprobe mode: `detail` (full identity probe) or `fast` (minimal
1304
+ * 32-token check). `detail` when unset.
1305
+ */
1306
+ modelProbeMode?: "fast" | "detail";
1307
+ /** Fast-probe timeout in seconds; `null`/absent means the built-in default (10). */
1308
+ modelProbeFastTimeoutSeconds?: number | null;
1291
1309
  }
1292
1310
 
1293
1311
  export const consoleSettings = pgTable("console_settings", {
@@ -5,7 +5,7 @@
5
5
  * markup inside the plain-text content stream instead of the structured
6
6
  * `tool_calls` field. The gateway sees it from one upstream family:
7
7
  *
8
- * - CodeBuddy/WorkBuddy emit strict blocks (`<|DSML| calls>…</|DSML| calls>`)
8
+ * - CodeBuddy emits strict blocks (`<|DSML| calls>…</|DSML| calls>`)
9
9
  * that must be parsed exactly, with declared-tool allowlisting.
10
10
  */
11
11
  export interface DsmlToolCall {
@@ -174,6 +174,7 @@ const PUBLIC_ERROR_DETAIL_KEYS = new Set([
174
174
  "status",
175
175
  "upstreamRequestId",
176
176
  "upstreamStatus",
177
+ "upstreamMessageAsIs",
177
178
  "maxInflight",
178
179
  "weight",
179
180
  ]);
@@ -34,7 +34,7 @@ export function applyOpenAIReasoning(payload: Record<string, unknown>, request:
34
34
  payload["reasoning_effort"] = effort;
35
35
  }
36
36
 
37
- /** Applies the DeepSeek chat contract shared by CodeBuddy and WorkBuddy. */
37
+ /** Applies the DeepSeek chat contract shared by CodeBuddy. */
38
38
  export function applyDeepSeekReasoning(payload: Record<string, unknown>): void {
39
39
  const model = typeof payload["model"] === "string" ? payload["model"].trim().toLowerCase() : "";
40
40
  if (!model.startsWith("deepseek")) return;
@@ -248,7 +248,7 @@ export function canonicalToChatPayload(
248
248
  // to be replayed, and the tool-call turn is exactly where a thinking
249
249
  // model emits it — dropping it here made the next request fail with
250
250
  // "the reasoning content from the previous turn must be passed back in
251
- // thinking mode" (WorkBuddy/CodeBuddy 400). Chat Completions has no
251
+ // thinking mode" (CodeBuddy 400). Chat Completions has no
252
252
  // reasoning content part, so it travels in the provider-native
253
253
  // `reasoning_content` side channel, same as the branches below.
254
254
  const reasoningContent = m.content
@@ -365,7 +365,7 @@ export function canonicalToChatPayload(
365
365
  // nothing left to say: emitting it as an empty-content `user` message
366
366
  // right after the tool results breaks strict upstreams (a bare `user`
367
367
  // turn between a result and the next assistant turn reads as a broken
368
- // tool sequence — WorkBuddy/CodeBuddy `11148`). Fold the leftover
368
+ // tool sequence — CodeBuddy `11148`). Fold the leftover
369
369
  // reasoning into the last tool message's side channel when one was
370
370
  // just emitted, and skip the turn otherwise; a turn that still
371
371
  // carries text or rich parts emits below as usual.
@@ -382,7 +382,7 @@ export function canonicalToChatPayload(
382
382
  // or a non-empty serialised payload. Pushing `{content:"",
383
383
  // reasoning_content:""}` is the opposite of load-bearing — the
384
384
  // upstream reads it as "thinking mode with the reasoning stripped"
385
- // and rejects the whole request (`WorkBuddy/CodeBuddy 11151 "history
385
+ // and rejects the whole request (`CodeBuddy 11151 "history
386
386
  // not found"`). The canonical form that reaches this branch with no
387
387
  // trace is an input `reasoning` item whose `summary` was absent and
388
388
  // whose `payload` was `null` — a signature-less thinking block
@@ -570,7 +570,7 @@ export function canonicalToChatPayload(
570
570
  // Resolved unconditionally, not only when the caller sent a value: the bound
571
571
  // table now carries a `defaultMaxTokens` for this wire, and an omitted limit
572
572
  // must reach the upstream as a real number. Upstreams that require the field
573
- // reject a large prompt whose `max_tokens` is absent outright (WorkBuddy
573
+ // reject a large prompt whose `max_tokens` is absent outright (CodeBuddy
574
574
  // `400 model_param_invalid`, reproduced through the gateway).
575
575
  const bounded = resolveWireMaxTokens("chat", { max_tokens: requestedMaxTokens }, {
576
576
  ...(options.maxOutputTokens === undefined ? {} : { maxOutputTokens: options.maxOutputTokens }),
@@ -39,7 +39,7 @@ import { claudeResponseToEvents, parseClaudeSseStream } from "../response/messag
39
39
  * Gateway default when the caller sent no max_tokens (Anthropic requires
40
40
  * the field). Explicit and warned at the call site — never silent.
41
41
  */
42
- const ANTHROPIC_DEFAULT_MAX_TOKENS = WIRE_TOKEN_BOUNDS["messages"]?.defaultMaxTokens ?? 4_096;
42
+ export const ANTHROPIC_DEFAULT_MAX_TOKENS = WIRE_TOKEN_BOUNDS["messages"]?.defaultMaxTokens ?? 4_096;
43
43
 
44
44
  /** Anthropic Messages' documented base64-image media-type allowlist. */
45
45
  const ANTHROPIC_IMAGE_MEDIA_TYPES = new Set([
@@ -38,15 +38,20 @@ export function responseParts(parts: readonly Record<string, unknown>[]): {
38
38
  readonly text: string;
39
39
  readonly calls: readonly { readonly id: string; readonly name: string; readonly args: Record<string, unknown> }[];
40
40
  readonly thought: string;
41
+ readonly thoughtSignature: string;
41
42
  } {
42
43
  const text: string[] = [];
43
44
  const thought: string[] = [];
45
+ let thoughtSignature = "";
44
46
  const calls: Array<{ id: string; name: string; args: Record<string, unknown> }> = [];
45
47
  let callIndex = 0;
46
48
  for (const part of parts) {
47
49
  if (typeof part["text"] === "string") {
48
50
  if (part["thought"] === true) {
49
51
  thought.push(part["text"] as string);
52
+ if (thoughtSignature.length === 0 && typeof part["thoughtSignature"] === "string") {
53
+ thoughtSignature = part["thoughtSignature"] as string;
54
+ }
50
55
  } else text.push(part["text"] as string);
51
56
  }
52
57
  if (isRecord(part["functionCall"]) && typeof (part["functionCall"] as Record<string, unknown>)["name"] === "string") {
@@ -58,7 +63,7 @@ export function responseParts(parts: readonly Record<string, unknown>[]): {
58
63
  });
59
64
  }
60
65
  }
61
- return { text: text.join(""), calls, thought: thought.join("") };
66
+ return { text: text.join(""), calls, thought: thought.join(""), thoughtSignature };
62
67
  }
63
68
 
64
69
  export function mapGeminiUsage(body: Record<string, unknown>): Record<string, unknown> {
@@ -93,8 +93,19 @@ function isCompleteJsonValue(data: string): boolean {
93
93
  // invited a retry of the identical request against a broken upstream. The
94
94
  // `upstream` origin keeps the public envelope from prefixing it
95
95
  // a gateway-branded message, which blamed the gateway for the provider's bytes.
96
- function oversized(what: string): GatewayError {
97
- return new GatewayError("platform_unavailable", 502, `SSE ${what} exceeds size bound (request {requestId}). The upstream sent more than we can hold — retry 🌙`, {}, "upstream");
96
+ // The message quotes the offending line (bounded) so the client and operator
97
+ // see the actual bytes the upstream sent rather than a generic sentence.
98
+ const MAX_SSE_SNIPPET = 160;
99
+ function snippet(line: string): string {
100
+ const collapsed = line.replace(/[\r\n]+/g, " ").trim();
101
+ if (collapsed.length === 0) return "<empty line>";
102
+ return collapsed.length > MAX_SSE_SNIPPET
103
+ ? `${collapsed.slice(0, MAX_SSE_SNIPPET)}…`
104
+ : collapsed;
105
+ }
106
+ function oversized(what: string, line?: string): GatewayError {
107
+ const quote = line === undefined ? "" : `: ${snippet(line)}`;
108
+ return new GatewayError("platform_unavailable", 502, `SSE ${what} exceeds size bound${quote} (request {requestId}). The upstream sent more than we can hold — retry 🌙`, {}, "upstream");
98
109
  }
99
110
 
100
111
  export async function* decodeSseEvents(
@@ -161,7 +172,7 @@ export async function* decodeSseEvents(
161
172
  const rest = last.subarray(newlineAt + 1);
162
173
  if (rest.byteLength > 0) pending.unshift(rest);
163
174
  const line = decoder.decode(lineBytes).replace(/\r$/, "");
164
- if (line.length > maxLineBytes) throw oversized("line");
175
+ if (line.length > maxLineBytes) throw oversized("line", line);
165
176
  return line;
166
177
  }
167
178
  function* dispatchEvent(): Iterable<SseEvent> {
@@ -188,7 +199,7 @@ export async function* decodeSseEvents(
188
199
  const piece = line.slice(5).trimStart();
189
200
  dataLines.push(piece);
190
201
  eventBytes += piece.length;
191
- if (eventBytes > maxEventBytes) throw oversized("event");
202
+ if (eventBytes > maxEventBytes) throw oversized("event", line);
192
203
  return;
193
204
  }
194
205
  if (line.startsWith("id:") || line.startsWith("retry:")) return;
@@ -220,10 +231,10 @@ export async function* decodeSseEvents(
220
231
  if (dataLines.length > 0) {
221
232
  dataLines.push(line);
222
233
  eventBytes += line.length;
223
- if (eventBytes > maxEventBytes) throw oversized("event");
234
+ if (eventBytes > maxEventBytes) throw oversized("event", line);
224
235
  return;
225
236
  }
226
- throw new GatewayError("platform_unavailable", 502, "Malformed SSE line (request {requestId}). The upstream sent something we can't read — retry 🌙", {}, "upstream");
237
+ throw new GatewayError("platform_unavailable", 502, `Malformed SSE line: ${snippet(line)} (request {requestId}). The upstream sent something we can't read — retry 🌙`, {}, "upstream");
227
238
  }
228
239
 
229
240
  try {
@@ -233,7 +244,10 @@ export async function* decodeSseEvents(
233
244
  if (chunk.value && chunk.value.byteLength > 0) {
234
245
  pending.push(chunk.value);
235
246
  pendingBytes += chunk.value.byteLength;
236
- if (!hasPendingNewline() && pendingBytes > maxLineBytes) throw oversized("line");
247
+ if (!hasPendingNewline() && pendingBytes > maxLineBytes) {
248
+ const partial = decoder.decode(new Uint8Array(pendingBytes)).slice(0, MAX_SSE_SNIPPET);
249
+ throw oversized("line", partial);
250
+ }
237
251
  }
238
252
  let line = takeLine();
239
253
  while (line !== undefined) {
@@ -243,7 +257,10 @@ export async function* decodeSseEvents(
243
257
  if (chunk.done) break;
244
258
  }
245
259
  if (pendingBytes > 0) {
246
- if (pendingBytes > maxLineBytes) throw oversized("line");
260
+ if (pendingBytes > maxLineBytes) {
261
+ const partial = decoder.decode(new Uint8Array(pendingBytes)).slice(0, MAX_SSE_SNIPPET);
262
+ throw oversized("line", partial);
263
+ }
247
264
  const total = new Uint8Array(pendingBytes);
248
265
  let offset = 0;
249
266
  for (const chunk of pending.splice(0, pending.length)) {
@@ -17,7 +17,11 @@
17
17
  * which is honest: the upstream failed and we do not know more.
18
18
  */
19
19
  import { GatewayError } from "./gateway-error";
20
- import { classifyUpstreamError, extractUpstreamMessage, upstreamErrorIdentifier } from "../transport/failure-policy";
20
+ import {
21
+ classifyUpstreamError,
22
+ upstreamErrorIdentifier,
23
+ extractUpstreamMessageText,
24
+ } from "../transport/failure-policy";
21
25
  import { isRecord } from "./codecs";
22
26
 
23
27
 
@@ -54,19 +58,25 @@ export function gatewayErrorFromStreamError(
54
58
  }
55
59
  if (!isRecord(error)) return undefined;
56
60
 
57
- const message = extractUpstreamMessage(error);
58
61
  const identifier = upstreamErrorIdentifier(error);
59
62
  const explicitStatus = numericStatus(error["status"], error["statusCode"], error["status_code"]);
60
63
  const classification = classifyUpstreamError(explicitStatus, identifier);
61
64
  const raw = JSON.stringify(error, null, 2).slice(0, 16_384);
65
+ // Only genuine prose (`message`/`msg`/`error` fields) counts as upstream
66
+ // passthrough text. A `code`/identifier is a classification key, not prose —
67
+ // it still serves as the message fallback when no text exists, but it must
68
+ // not set `upstreamMessageAsIs`.
69
+ const prose = extractUpstreamMessageText(error);
70
+ const message = prose.slice(0, 500) || identifier || label;
62
71
  return new GatewayError(
63
72
  classification.code,
64
73
  classification.status,
65
- message || label,
74
+ message,
66
75
  {
67
76
  ...(explicitStatus === undefined ? {} : { providerStatus: explicitStatus }),
68
77
  ...(identifier ? { providerCode: identifier } : {}),
69
78
  raw,
79
+ ...(prose ? { upstreamMessageAsIs: true } : {}),
70
80
  ...(classification.credentialEvidence ? { credentialEvidence: true } : {}),
71
81
  ...(classification.rateLimitScope
72
82
  ? { rateLimitScope: classification.rateLimitScope }
@@ -49,7 +49,7 @@ interface WireTokenBound {
49
49
  * The output limit assumed when the caller states none, for wires that need
50
50
  * the field present.
51
51
  *
52
- * PROVEN, not assumed: a 3.16M-char prompt to WorkBuddy/CodeBuddy
52
+ * PROVEN, not assumed: a 3.16M-char prompt to CodeBuddy
53
53
  * (`bansos/deepseek-v4.1-flash`) answered `400 model_param_invalid` whenever
54
54
  * `max_tokens` was absent, and answered 200 with the *same* body plus any
55
55
  * explicit value — `1`, `16`, `1000`, `8000`, `64000`, `384000` all passed.
@@ -91,7 +91,7 @@ export function repairMissingToolResponses(
91
91
  * fully answered by matching `tool` results is stripped of its calls (kept when
92
92
  * it still carries content), and the dangling results go with it.
93
93
  *
94
- * This is the buddy-family policy (WorkBuddy/CodeBuddy code `11148`): a partial
94
+ * This is the buddy-family policy (CodeBuddy code `11148`): a partial
95
95
  * batch `assistant[c1 c2] + tool[c1]` is a broken sequence the upstream rejects,
96
96
  * and a synthesized placeholder cannot stand in for a real execution the model
97
97
  * never saw. Contrast `repairMissingToolResponses`, which synthesizes an
@@ -188,7 +188,7 @@ export function repairRequestToolCalls(request: CanonicalRequest): CanonicalRequ
188
188
  * True when a `toolResult` part is a `tool`-role turn whose `toolCall` is
189
189
  * absent from the whole history, or when results for one batch of calls are
190
190
  * not contiguous. Upstreams reject both with `tool_call_sequence_broken`
191
- * (WorkBuddy/CodeBuddy code `11148`), and a broken history stays broken: the
191
+ * (CodeBuddy code `11148`), and a broken history stays broken: the
192
192
  * client replays it on every later turn, so the whole conversation dies.
193
193
  */
194
194
  function toolCallIdsIn(message: CanonicalMessage): string[] {