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
@@ -0,0 +1,353 @@
1
+ /**
2
+ * Devin authentication: OAuth login flows and credential import.
3
+ *
4
+ * Three paths are supported:
5
+ *
6
+ * 1. **Devin CLI** (`app.devin.ai`): PKCE S256 browser-code flow. Authorize
7
+ * at `https://app.devin.ai/auth/cli/continue`, exchange at
8
+ * `https://api.devin.ai/auth/cli/token` (body JSON, non-standard).
9
+ * 2. **Windsurf Enterprise** (`windsurf.com`): PKCE S256 loopback flow on
10
+ * port 59654, exchanging via `ExchangePKCEAuthorizationCode` to a raw
11
+ * seat key (`sk-ws-…`, stripped from the `gsk-ws-…` wire form).
12
+ * 3. **CLI adopt**: reads the official Devin CLI `credentials.toml`
13
+ * (`windsurf_api_key`) — no network, no prompts.
14
+ *
15
+ * All three store under the `devin` credential slot (`store-as "devin"`).
16
+ *
17
+ * Ported from oh-my-pi (`registry/oauth/devin-windsurf.ts` +
18
+ * `devin-cli.ts`, MIT) and opencode-devin-provider (`oauth.ts`, MIT).
19
+ */
20
+
21
+ import { createHash, randomBytes } from "node:crypto";
22
+ import { readFileSync, existsSync } from "node:fs";
23
+ import { join } from "node:path";
24
+ import {
25
+ base64url,
26
+ decodeJwt,
27
+ } from "jose";
28
+ import type {
29
+ OAuthAuthorizeRequest,
30
+ OAuthCodeExchangeContext,
31
+ OAuthExchangeResult,
32
+ OAuthImportInput,
33
+ OAuthLoginClient,
34
+ } from "../../authentication/oauth-flow-store";
35
+ import { callDevinUnary } from "./devin-http";
36
+ import { DevinMessageTypes } from "./protocol/devin-proto";
37
+ import {
38
+ DEVIN_DEFAULT_BASE_URL,
39
+ DEVIN_EXCHANGE_PATH,
40
+ DEVIN_GET_USER_STATUS_PATH,
41
+ } from "./devin-shared";
42
+
43
+ // ── PKCE ──
44
+
45
+ export function generatePKCE(): { verifier: string; challenge: string } {
46
+ const verifier = base64url.encode(randomBytes(96));
47
+ const challenge = base64url.encode(createHash("sha256").update(verifier).digest());
48
+ return { verifier, challenge };
49
+ }
50
+
51
+ // ── Identity resolution ──
52
+
53
+ /** JWT claim keys tried in order when deriving an email/identity label. */
54
+ const IDENTITY_CLAIM_KEYS = [
55
+ "email",
56
+ "preferred_username",
57
+ "name",
58
+ "sub",
59
+ "user_id",
60
+ "userId",
61
+ "session_id",
62
+ ] as const;
63
+
64
+ /**
65
+ * Derives a display label from a session token without any network: decode
66
+ * the JWT payload and read the first present identity claim. A token that is
67
+ * not a JWT (raw `sk-ws-…` seat key) yields nothing.
68
+ */
69
+ export function identityLabelFromDevinCredential(credential: string): string | undefined {
70
+ const key = credential.trim();
71
+ if (!key) return undefined;
72
+ const jwt = key.startsWith("devin-session-token$")
73
+ ? key.slice("devin-session-token$".length)
74
+ : key;
75
+ if (jwt.split(".").length !== 3) return undefined;
76
+ let payload: Record<string, unknown>;
77
+ try {
78
+ payload = decodeJwt(jwt) as Record<string, unknown>;
79
+ } catch {
80
+ // The token is opaque — no claims to read. Callers fall back to the
81
+ // network identity below.
82
+ return undefined;
83
+ }
84
+ for (const claim of IDENTITY_CLAIM_KEYS) {
85
+ const value = payload[claim];
86
+ if (typeof value === "string" && value.length > 0 && value !== "unauthenticated") {
87
+ return value;
88
+ }
89
+ }
90
+ return undefined;
91
+ }
92
+
93
+ /** A single identity string from `GetUserStatus`, if the upstream reports one. */
94
+ export function identityFromDevinUserStatus(planInfo: unknown): string | undefined {
95
+ if (typeof planInfo !== "object" || planInfo === null) return undefined;
96
+ const info = planInfo as {
97
+ userStatus?: { email?: string; teamId?: string };
98
+ planInfo?: { devinInfo?: { accountDisplayName?: string; orgId?: string } };
99
+ };
100
+ const email = info.userStatus?.email?.trim();
101
+ if (email) return email;
102
+ const displayName = info.planInfo?.devinInfo?.accountDisplayName?.trim();
103
+ if (displayName) return displayName;
104
+ const orgId = info.planInfo?.devinInfo?.orgId?.trim();
105
+ if (orgId) return orgId;
106
+ const teamId = info.userStatus?.teamId?.trim();
107
+ if (teamId) return teamId;
108
+ return undefined;
109
+ }
110
+
111
+ /**
112
+ * Resolves the human-friendly label for a Devin credential: JWT claims first
113
+ * (free), then `GetUserStatus` (network). Never throws — a failed lookup
114
+ * returns `undefined`, leaving the caller's default label in place.
115
+ */
116
+ export async function resolveDevinAccountLabel(
117
+ credential: string,
118
+ fetcher: typeof fetch = fetch,
119
+ ): Promise<string | undefined> {
120
+ const jwtLabel = identityLabelFromDevinCredential(credential);
121
+ if (jwtLabel) return jwtLabel;
122
+ try {
123
+ const response = await callDevinUnary<Record<string, unknown>>(
124
+ DEVIN_GET_USER_STATUS_PATH,
125
+ DevinMessageTypes.GetUserStatusRequest,
126
+ DevinMessageTypes.GetUserStatusResponse,
127
+ { metadata: { apiKey: credential } },
128
+ { baseUrl: DEVIN_DEFAULT_BASE_URL, fetchImpl: fetcher, signal: AbortSignal.timeout(10_000) },
129
+ );
130
+ return identityFromDevinUserStatus(response);
131
+ } catch {
132
+ return undefined;
133
+ }
134
+ }
135
+
136
+ // ── Devin CLI flow (app.devin.ai) ──
137
+
138
+ const DEVIN_AUTHORIZE_URL = "https://app.devin.ai/auth/cli/continue";
139
+ const DEVIN_TOKEN_URL = "https://api.devin.ai/auth/cli/token";
140
+ const DEVIN_BROWSER_REDIRECT_URI = "http://127.0.0.1:59653/callback";
141
+
142
+ export const devinOAuthClient: OAuthLoginClient = {
143
+ supportsDeviceCode: false,
144
+ supportsBrowserCode: true,
145
+ browserRedirectUri: DEVIN_BROWSER_REDIRECT_URI,
146
+
147
+ buildAuthorizeUrl(request: OAuthAuthorizeRequest): string {
148
+ const params = new URLSearchParams({
149
+ redirect_uri: DEVIN_BROWSER_REDIRECT_URI,
150
+ state: request.state,
151
+ prompt: "select_account",
152
+ code_challenge: request.codeChallenge,
153
+ code_challenge_method: "S256",
154
+ });
155
+ return `${DEVIN_AUTHORIZE_URL}?${params.toString()}`;
156
+ },
157
+
158
+ async exchangeCode(
159
+ code: string,
160
+ codeVerifier: string,
161
+ _redirectUri: string,
162
+ _state?: string,
163
+ _context?: OAuthCodeExchangeContext,
164
+ ): Promise<OAuthExchangeResult> {
165
+ const response = await fetch(DEVIN_TOKEN_URL, {
166
+ method: "POST",
167
+ headers: { "content-type": "application/json", accept: "application/json" },
168
+ body: JSON.stringify({ code, code_verifier: codeVerifier }),
169
+ });
170
+ if (!response.ok) {
171
+ const text = await response.text().catch(() => "");
172
+ throw new Error(`Devin token exchange failed: HTTP ${response.status} ${text}`);
173
+ }
174
+ const body = (await response.json()) as { token?: string };
175
+ const token = body.token;
176
+ if (!token) throw new Error("Devin token exchange returned no token");
177
+
178
+ // Parse JWT exp, fallback to 1 year.
179
+ let expiresAt: Date;
180
+ try {
181
+ const claims = decodeJwt(token);
182
+ expiresAt = claims.exp ? new Date(claims.exp * 1000) : new Date(Date.now() + 365 * 24 * 3600 * 1000);
183
+ } catch {
184
+ expiresAt = new Date(Date.now() + 365 * 24 * 3600 * 1000);
185
+ }
186
+
187
+ // The exchange only returns a token; the label comes from the token's
188
+ // own claims (when present) or `GetUserStatus`. Never fail the login on
189
+ // a label lookup.
190
+ const accountLabel = await resolveDevinAccountLabel(token).catch(() => undefined);
191
+
192
+ return {
193
+ access: token,
194
+ refresh: "",
195
+ expiresAt,
196
+ ...(accountLabel ? { accountLabel } : {}),
197
+ };
198
+ },
199
+
200
+ async importCredential(input: OAuthImportInput): Promise<OAuthExchangeResult> {
201
+ const credential = input.credential.trim();
202
+ if (!credential) throw new Error("Devin credential is empty");
203
+ // The key itself is the identity; resolve a label when the material can
204
+ // provide one (JWT claims or `GetUserStatus`), never failing on it.
205
+ const accountLabel = await resolveDevinAccountLabel(credential).catch(() => undefined);
206
+ return {
207
+ access: credential,
208
+ refresh: "",
209
+ expiresAt: new Date(8640000000000000),
210
+ ...(accountLabel ? { accountLabel } : {}),
211
+ ...(input.accountLabel ? { accountLabel: input.accountLabel } : {}),
212
+ };
213
+ },
214
+ };
215
+
216
+ // ── Windsurf Enterprise flow (windsurf.com/editor/signin) ──
217
+
218
+ const WINDSURF_AUTHORIZE_URL = "https://windsurf.com/editor/signin";
219
+
220
+ /** Loopback port the official Devin CLI uses for the Windsurf flow. */
221
+ export const WINDSURF_CALLBACK_PORT = 59654;
222
+ /** Loopback hostname for the Windsurf flow. */
223
+ export const WINDSURF_PREFERRED_HOSTNAME = "127.0.0.1";
224
+
225
+ export const devinWindsurfOAuthClient: OAuthLoginClient = {
226
+ supportsDeviceCode: false,
227
+ supportsBrowserCode: true,
228
+
229
+ buildAuthorizeUrl(request: OAuthAuthorizeRequest): string {
230
+ const params = new URLSearchParams({
231
+ response_type: "code",
232
+ redirect_uri: request.redirectUri,
233
+ state: request.state,
234
+ code_challenge: request.codeChallenge,
235
+ code_challenge_method: "S256",
236
+ prompt: "login",
237
+ redirect_parameters_type: "query",
238
+ });
239
+ return `${WINDSURF_AUTHORIZE_URL}?${params.toString()}`;
240
+ },
241
+
242
+ async exchangeCode(
243
+ code: string,
244
+ codeVerifier: string,
245
+ _redirectUri: string,
246
+ _state?: string,
247
+ _context?: OAuthCodeExchangeContext,
248
+ ): Promise<OAuthExchangeResult> {
249
+ const response = await callDevinUnary<{
250
+ apiKey?: string;
251
+ name?: string;
252
+ apiServerUrl?: string;
253
+ devinWebappHost?: string;
254
+ devinApiUrl?: string;
255
+ }>(
256
+ DEVIN_EXCHANGE_PATH,
257
+ DevinMessageTypes.ExchangePKCEAuthorizationCodeRequest,
258
+ DevinMessageTypes.ExchangePKCEAuthorizationCodeResponse,
259
+ { authorizationCode: code, codeVerifier },
260
+ { baseUrl: DEVIN_DEFAULT_BASE_URL },
261
+ );
262
+ const apiKey = response.apiKey;
263
+ if (!apiKey) throw new Error("Windsurf code exchange returned no API key");
264
+ // The exchange returns the `g`-prefixed wire form (`gsk-ws-…`); RPCs
265
+ // authenticate with the raw key.
266
+ const key = apiKey.startsWith("gsk-") ? apiKey.slice(1) : apiKey;
267
+ // Prefer the upstream-reported name; fall back to the seat key's identity
268
+ // claims or `GetUserStatus` — never fail the login on a label lookup.
269
+ const accountLabel =
270
+ response.name?.trim() || (await resolveDevinAccountLabel(key).catch(() => undefined));
271
+ return {
272
+ access: key,
273
+ refresh: "",
274
+ expiresAt: new Date(8640000000000000), // seat key: never expires
275
+ ...(accountLabel ? { accountLabel } : {}),
276
+ };
277
+ },
278
+ };
279
+
280
+ // ── CLI adopt (credentials.toml) ──
281
+
282
+ const KNOWN_API_HOSTS = new Set(["server.codeium.com", "server.enterprise.windsurf.com"]);
283
+
284
+ /** Candidate `credentials.toml` paths per platform. */
285
+ export function devinCliCredentialPaths(): string[] {
286
+ const paths: string[] = [];
287
+ if (process.platform === "win32") {
288
+ const appData = process.env.APPDATA;
289
+ if (appData) paths.push(join(appData, "devin", "credentials.toml"));
290
+ paths.push(join(process.env.HOME ?? "", ".local", "share", "devin", "credentials.toml"));
291
+ } else {
292
+ const xdg = process.env.XDG_DATA_HOME;
293
+ if (xdg) paths.push(join(xdg, "devin", "credentials.toml"));
294
+ paths.push(join(process.env.HOME ?? "", ".local", "share", "devin", "credentials.toml"));
295
+ if (process.platform === "darwin") {
296
+ paths.push(join(process.env.HOME ?? "", "Library", "Application Support", "devin", "credentials.toml"));
297
+ }
298
+ }
299
+ return [...new Set(paths)];
300
+ }
301
+
302
+ /** Parses a Devin CLI `credentials.toml` and returns the Windsurf seat key. */
303
+ export function readDevinCliCredentials(file: string): string {
304
+ const text = readFileSync(file, "utf8");
305
+ // Minimal TOML parse: key = "value" lines.
306
+ const record: Record<string, string> = {};
307
+ for (const line of text.split(/\r?\n/)) {
308
+ const trimmed = line.trim();
309
+ if (!trimmed || trimmed.startsWith("#")) continue;
310
+ const eq = trimmed.indexOf("=");
311
+ if (eq < 0) continue;
312
+ const key = trimmed.slice(0, eq).trim();
313
+ let value = trimmed.slice(eq + 1).trim();
314
+ if (value.startsWith('"') && value.endsWith('"')) value = value.slice(1, -1);
315
+ record[key] = value;
316
+ }
317
+ const key = record["windsurf_api_key"]?.trim() ?? "";
318
+ if (!key) throw new Error(`Devin CLI credentials at ${file} have no windsurf_api_key`);
319
+ const serverUrl = record["api_server_url"]?.trim();
320
+ if (serverUrl) {
321
+ let host: string | undefined;
322
+ try {
323
+ host = new URL(serverUrl).host;
324
+ } catch {
325
+ host = undefined;
326
+ }
327
+ if (!host || !KNOWN_API_HOSTS.has(host)) {
328
+ throw new Error(`Devin CLI credentials at ${file} point at unsupported API server`);
329
+ }
330
+ }
331
+ return key;
332
+ }
333
+
334
+ /** Reads the first existing credentials file and returns its seat key. */
335
+ export function loginDevinCliHook(): Promise<string> {
336
+ for (const file of devinCliCredentialPaths()) {
337
+ if (!existsSync(file)) continue;
338
+ try {
339
+ return Promise.resolve(readDevinCliCredentials(file));
340
+ } catch (error) {
341
+ // First existing file that fails validation wins; no fallback probing.
342
+ return Promise.reject(error);
343
+ }
344
+ }
345
+ return Promise.reject(
346
+ new Error(
347
+ `No Devin CLI credentials found (probed: ${devinCliCredentialPaths().join(", ")}). Run \`devin auth login\`, then retry.`,
348
+ ),
349
+ );
350
+ }
351
+
352
+ // ── Import credential (raw API key / session token) ──
353
+ // `devinOAuthClient.importCredential` handles pasted keys; no separate client.
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Devin model catalog: live discovery via `GetCliModelConfigs` plus a static
3
+ * seed for boot. Ported from oh-my-pi (`discovery/devin.ts`, MIT) with the
4
+ * Cartethyia `defineModel` shape.
5
+ */
6
+
7
+ import { defineModel, type ModelDefinition } from "../../provider-model-definition";
8
+ import { callDevinUnary } from "./devin-http";
9
+ import {
10
+ DEVIN_CHAT_MESSAGE_PATH,
11
+ DEVIN_GET_CLI_MODEL_CONFIGS_PATH,
12
+ DEFAULT_CONTEXT_WINDOW,
13
+ DEFAULT_MAX_TOKENS,
14
+ DEVIN_DEFAULT_BASE_URL,
15
+ DEVIN_INTERNAL_MODEL_DISPLAYS,
16
+ devinDiscoveryMetadata,
17
+ } from "./devin-shared";
18
+ import { DevinMessageTypes } from "./protocol/devin-proto";
19
+
20
+ const DISCOVERY_TIMEOUT_MS = 15_000;
21
+
22
+ /** Static seed shown before discovery succeeds (boot-safe, synchronous). */
23
+ export const DEVIN_MODELS: readonly ModelDefinition[] = [
24
+ defineModel({
25
+ id: "swe-1-6-fast",
26
+ providerId: "devin",
27
+ wireFamily: "chat",
28
+ endpoint: DEVIN_CHAT_MESSAGE_PATH,
29
+ ctx: DEFAULT_CONTEXT_WINDOW,
30
+ out: 128_000,
31
+ // No vision: paired live control showed both available models either
32
+ // ignore or reject inline images. Declaring it would route images into a
33
+ // guaranteed failure.
34
+ reasoning: true,
35
+ toolCall: true,
36
+ }),
37
+ // `swe-1-6` removed: not present in the live `GetCliModelConfigs` roster on
38
+ // this account (1003 of 1010 configs are disabled) and a chat dispatch to
39
+ // it failed with `failed_precondition`.
40
+ // Operator-declared lanes. The upstream catalog has not been fetched live
41
+ // yet, so these rows are pinned from the measured spec rather than generated.
42
+ defineModel({
43
+ id: "swe-1-6-slow",
44
+ providerId: "devin",
45
+ wireFamily: "chat",
46
+ endpoint: DEVIN_CHAT_MESSAGE_PATH,
47
+ ctx: 200_000,
48
+ out: 128_000,
49
+ reasoning: true,
50
+ toolCall: true,
51
+ }),
52
+ defineModel({
53
+ id: "swe-2-flash-medium",
54
+ providerId: "devin",
55
+ wireFamily: "chat",
56
+ endpoint: DEVIN_CHAT_MESSAGE_PATH,
57
+ // Live `GetCliModelConfigs` (2026-11-07): ctx=262144, maxOutput=131072,
58
+ // features = thinking + tools. This uid is the account default.
59
+ ctx: 262_144,
60
+ out: 131_072,
61
+ reasoning: true,
62
+ toolCall: true,
63
+ }),
64
+ ];
65
+
66
+ interface RawModelConfig {
67
+ modelUid?: string;
68
+ label?: string;
69
+ disabled?: boolean;
70
+ supportsImages?: boolean;
71
+ maxTokens?: number;
72
+ description?: string;
73
+ isNew?: boolean;
74
+ isBeta?: boolean;
75
+ isRecommended?: boolean;
76
+ isDefaultModelInFamily?: boolean;
77
+ modelInfo?: {
78
+ maxTokens?: number;
79
+ maxOutputTokens?: number;
80
+ displayOption?: number;
81
+ harnessUids?: string[];
82
+ isModelRouter?: boolean;
83
+ modelFeatures?: { supportsImages?: boolean; supportsToolCalls?: boolean; supportsThinking?: boolean };
84
+ };
85
+ modelDimensions?: { label?: string; value?: number; denominator?: string; kind?: number }[];
86
+ }
87
+
88
+ const REASONING_LABEL_PATTERN = /think|thinking|minimal|high|medium|low|xhigh|max|reasoning/i;
89
+ const NO_REASONING_LABEL_PATTERN = /\bno thinking\b/i;
90
+
91
+ function supportsThinking(config: RawModelConfig): boolean {
92
+ const features = config.modelInfo?.modelFeatures;
93
+ if (features) return features.supportsThinking === true;
94
+ const label = config.label ?? "";
95
+ if (NO_REASONING_LABEL_PATTERN.test(label)) return false;
96
+ return REASONING_LABEL_PATTERN.test(label);
97
+ }
98
+
99
+ function modelCost(config: RawModelConfig): { input: number; output: number; cache_read: number } {
100
+ let input = 0;
101
+ let output = 0;
102
+ let cache_read = 0;
103
+ const dims = config.modelDimensions ?? [];
104
+ for (const dim of dims) {
105
+ if (dim.kind !== 1 && dim.kind !== 2) continue; // COST / COST_FUZZY
106
+ if ((dim.label ?? "").toLowerCase() === "sidekick") break;
107
+ let value = dim.value ?? 0;
108
+ const denom = dim.denominator ?? "";
109
+ const m = denom.match(/(\d+(?:\.\d+)?)\s*([kmb])?/i);
110
+ let divisor = 1_000_000;
111
+ if (m) {
112
+ const num = Number(m[1]);
113
+ const suffix = m[2]?.toLowerCase();
114
+ if (suffix === "k") divisor = num * 1_000;
115
+ else if (suffix === "m") divisor = num * 1_000_000;
116
+ else if (suffix === "b") divisor = num * 1_000_000_000;
117
+ else divisor = num;
118
+ }
119
+ value = (value * 1_000_000) / divisor;
120
+ const label = (dim.label ?? "").toLowerCase();
121
+ if (label.includes("input")) input = Math.round(value * 1e6) / 1e6;
122
+ else if (label.includes("output")) output = Math.round(value * 1e6) / 1e6;
123
+ else if (label.includes("cached")) cache_read = Math.round(value * 1e6) / 1e6;
124
+ }
125
+ return { input, output, cache_read };
126
+ }
127
+
128
+ export interface DevinDiscoveryInput {
129
+ readonly credential: string;
130
+ readonly fetcher?: typeof fetch;
131
+ }
132
+
133
+ /** Live discovery: GetCliModelConfigs -> ModelDefinition rows. */
134
+ export async function discoverDevinModels(
135
+ input: DevinDiscoveryInput,
136
+ ): Promise<readonly ModelDefinition[] | null> {
137
+ const { credential, fetcher } = input;
138
+ const request = {
139
+ metadata: devinDiscoveryMetadata(credential),
140
+ };
141
+ const response = await callDevinUnary<{ clientModelConfigs?: RawModelConfig[]; defaultOverrideModelConfig?: { modelUid?: string } }>(
142
+ DEVIN_GET_CLI_MODEL_CONFIGS_PATH,
143
+ DevinMessageTypes.GetCliModelConfigsRequest,
144
+ DevinMessageTypes.GetCliModelConfigsResponse,
145
+ request,
146
+ {
147
+ baseUrl: DEVIN_DEFAULT_BASE_URL,
148
+ ...(fetcher === undefined ? {} : { fetchImpl: fetcher }),
149
+ signal: AbortSignal.timeout(DISCOVERY_TIMEOUT_MS),
150
+ },
151
+ );
152
+ const configs = response.clientModelConfigs ?? [];
153
+ const defaultUid = response.defaultOverrideModelConfig?.modelUid?.trim();
154
+
155
+ const rows: ModelDefinition[] = [];
156
+ const seen = new Set<string>();
157
+ for (const config of configs) {
158
+ if (config.disabled) continue;
159
+ const uid = config.modelUid?.trim();
160
+ if (!uid || seen.has(uid)) continue;
161
+ const displayOption = config.modelInfo?.displayOption ?? 0;
162
+ if (DEVIN_INTERNAL_MODEL_DISPLAYS.has(displayOption)) continue;
163
+ seen.add(uid);
164
+
165
+ const features = config.modelInfo?.modelFeatures;
166
+ // Operator-pinned lanes, confirmed against live `GetCliModelConfigs`:
167
+ // `swe-2-flash-medium` = 262144/131072 (account default),
168
+ // `swe-1-6-slow` = 200000/128000.
169
+ const pinnedContext = uid === "swe-2-flash-medium" ? 262_144 : uid === "swe-1-6-slow" ? 200_000 : undefined;
170
+ const pinnedOutput = uid === "swe-2-flash-medium" ? 131_072 : undefined;
171
+ const contextLimit =
172
+ pinnedContext ?? ((config.maxTokens ?? 0) > 0 ? config.maxTokens! : (config.modelInfo?.maxTokens ?? 0) > 0 ? config.modelInfo!.maxTokens! : DEFAULT_CONTEXT_WINDOW);
173
+ const outputLimit = pinnedOutput ?? ((config.modelInfo?.maxOutputTokens ?? 0) > 0 ? config.modelInfo!.maxOutputTokens! : DEFAULT_MAX_TOKENS);
174
+ // `supportsImages` from the server is NOT an entitlement: live paired
175
+ // control showed `swe-1-6-slow` (supportsImages=true) answering the same
176
+ // "cannot see" with and without an ImageData payload. Never advertise
177
+ // vision for this provider; the adapter drops image parts entirely.
178
+ const cost = modelCost(config);
179
+
180
+ rows.push(
181
+ defineModel({
182
+ id: uid,
183
+ providerId: "devin",
184
+ wireFamily: "chat",
185
+ endpoint: DEVIN_CHAT_MESSAGE_PATH,
186
+ ctx: contextLimit,
187
+ out: outputLimit,
188
+ reasoning: supportsThinking(config),
189
+ toolCall: features ? features.supportsToolCalls !== false : true,
190
+ free: cost.input === 0 && cost.output === 0,
191
+ ...(config.isNew ? { warnings: [{ kind: "is_new", message: "New model", severity: "info" as const }] } : {}),
192
+ ...(config.isRecommended ? { warnings: [{ kind: "recommended", message: "Recommended by Devin", severity: "info" as const }] } : {}),
193
+ }),
194
+ );
195
+ }
196
+
197
+ // Account default wins: promote its row to default (first in list).
198
+ if (defaultUid && seen.has(defaultUid)) {
199
+ const idx = rows.findIndex((r) => r.modelId === defaultUid);
200
+ if (idx > 0) {
201
+ const [row] = rows.splice(idx, 1);
202
+ rows.unshift(row!);
203
+ }
204
+ }
205
+
206
+ return rows;
207
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Devin unary Connect-RPC client (`application/proto`).
3
+ *
4
+ * Responses may arrive as raw protobuf or gzipped protobuf (Connect servers
5
+ * compress when the client advertises `connect-accept-encoding: gzip`), so
6
+ * decode always has a gunzip fallback. Ported from oh-my-pi
7
+ * (`packages/catalog/src/wire/devin-proto.ts` + `discovery/devin.ts`, MIT).
8
+ */
9
+
10
+ import { gunzipSync } from "node:zlib";
11
+ import type { Type } from "protobufjs";
12
+ import { GatewayError } from "../../../protocol/gateway-error";
13
+ import { DEVIN_CONNECT_UA } from "./devin-shared";
14
+
15
+ export interface DevinUnaryOptions {
16
+ readonly baseUrl?: string;
17
+ readonly fetchImpl?: typeof fetch;
18
+ readonly signal?: AbortSignal;
19
+ readonly timeoutMs?: number;
20
+ /** Include `connect-go` User-Agent (chat path) rather than plain unary. */
21
+ readonly connectGoUA?: boolean;
22
+ }
23
+
24
+ /** Calls one unary RPC and decodes the response message. */
25
+ export async function callDevinUnary<T extends object>(
26
+ path: string,
27
+ requestType: Type,
28
+ responseType: Type,
29
+ request: object,
30
+ options: DevinUnaryOptions = {},
31
+ ): Promise<T> {
32
+ const baseUrl = (options.baseUrl ?? "https://server.codeium.com").replace(/\/+$/, "");
33
+ const fetcher = options.fetchImpl ?? fetch;
34
+ const body = Buffer.from(requestType.encode(request).finish());
35
+
36
+ const headers: Record<string, string> = {
37
+ "content-type": "application/proto",
38
+ "connect-protocol-version": "1",
39
+ accept: "*/*",
40
+ };
41
+ if (options.connectGoUA) headers["user-agent"] = DEVIN_CONNECT_UA;
42
+
43
+ const signal =
44
+ options.signal === undefined
45
+ ? options.timeoutMs !== undefined
46
+ ? AbortSignal.timeout(options.timeoutMs)
47
+ : undefined
48
+ : options.timeoutMs !== undefined
49
+ ? AbortSignal.any([options.signal, AbortSignal.timeout(options.timeoutMs)])
50
+ : options.signal;
51
+
52
+ const response = await fetcher(`${baseUrl}${path}`, {
53
+ method: "POST",
54
+ headers,
55
+ body,
56
+ ...(signal === undefined ? {} : { signal }),
57
+ });
58
+
59
+ const buf = Buffer.from(await response.arrayBuffer());
60
+ if (!response.ok) {
61
+ const text = buf.subarray(0, 2048).toString("utf8");
62
+ throw new GatewayError(
63
+ "upstream_unprocessable",
64
+ response.status,
65
+ `Devin upstream error ${response.status}: ${text} (request {requestId})`,
66
+ { providerId: "devin", upstreamCode: String(response.status) },
67
+ "upstream",
68
+ );
69
+ }
70
+
71
+ return decodeDevinUnary<T>(responseType, buf);
72
+ }
73
+
74
+ /** Decodes a unary response, trying raw protobuf then gunzip. */
75
+ export function decodeDevinUnary<T extends object>(type: Type, buf: Buffer): T {
76
+ try {
77
+ return type.decode(buf) as T;
78
+ } catch {
79
+ try {
80
+ return type.decode(gunzipSync(buf)) as T;
81
+ } catch {
82
+ throw new GatewayError(
83
+ "internal_error",
84
+ 500,
85
+ "Devin upstream returned an undecodable response (request {requestId})",
86
+ { providerId: "devin" },
87
+ "upstream",
88
+ );
89
+ }
90
+ }
91
+ }